From bf8898acee6d14cbd62e185cbbc093767d968919 Mon Sep 17 00:00:00 2001 From: Jesse Wierzbinski Date: Mon, 13 May 2024 21:00:05 -1000 Subject: [PATCH] feat: :sparkles: Port code from Lysand Server and improve it --- .vscode/settings.json | 2 +- biome.json | 20 ++ build.ts | 17 ++ bun.lockb | Bin 5092 -> 34244 bytes federation/biome.json | 20 -- federation/index.ts | 53 +++++ federation/package.json | 126 ++++++------ federation/schemas/base.ts | 188 ++++++++++++++++++ federation/schemas/content_format.ts | 17 ++ .../schemas/extensions/custom_emojis.ts | 50 +++++ federation/schemas/extensions/polls.ts | 92 +++++++++ federation/schemas/extensions/reactions.ts | 28 +++ federation/schemas/extensions/vanity.ts | 93 +++++++++ federation/schemas/regex.ts | 40 ++++ federation/tests/index.test.ts | 12 ++ federation/tsconfig.json | 8 + package.json | 24 ++- tsconfig.json | 32 +++ 18 files changed, 733 insertions(+), 89 deletions(-) create mode 100644 biome.json create mode 100644 build.ts delete mode 100644 federation/biome.json create mode 100644 federation/index.ts create mode 100644 federation/schemas/base.ts create mode 100644 federation/schemas/content_format.ts create mode 100644 federation/schemas/extensions/custom_emojis.ts create mode 100644 federation/schemas/extensions/polls.ts create mode 100644 federation/schemas/extensions/reactions.ts create mode 100644 federation/schemas/extensions/vanity.ts create mode 100644 federation/schemas/regex.ts create mode 100644 federation/tests/index.test.ts create mode 100644 federation/tsconfig.json create mode 100644 tsconfig.json diff --git a/.vscode/settings.json b/.vscode/settings.json index 2679899..4b62e3e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,3 @@ { - "conventionalCommits.scopes": ["docs"] + "conventionalCommits.scopes": ["docs"] } diff --git a/biome.json b/biome.json new file mode 100644 index 0000000..d8d8db0 --- /dev/null +++ b/biome.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.6.4/schema.json", + "organizeImports": { + "enabled": true, + "ignore": ["node_modules", "dist"] + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true + }, + "ignore": ["node_modules", "dist"] + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 4, + "ignore": ["node_modules", "dist"] + } +} diff --git a/build.ts b/build.ts new file mode 100644 index 0000000..f08fc94 --- /dev/null +++ b/build.ts @@ -0,0 +1,17 @@ +import dts from "bun-plugin-dts"; +import ora from "ora"; + +const spinner = ora("Building...").start(); + +await Bun.build({ + entrypoints: ["federation/index.ts"], + outdir: "federation/dist", + format: "esm", + minify: true, + sourcemap: "external", + splitting: true, + target: "browser", + plugins: [dts()], +}); + +spinner.succeed("Built federation module"); diff --git a/bun.lockb b/bun.lockb index 363a67f80c5064d9d8c457e4e50f3cc309a31b68..287f9ef0d167ddc417ecf78f20c9550789c4cd24 100755 GIT binary patch literal 34244 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 FQvlg@zNG*F diff --git a/federation/biome.json b/federation/biome.json deleted file mode 100644 index 536d532..0000000 --- a/federation/biome.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "$schema": "https://biomejs.dev/schemas/1.6.4/schema.json", - "organizeImports": { - "enabled": true, - "ignore": ["node_modules", "dist"] - }, - "linter": { - "enabled": true, - "rules": { - "recommended": true - }, - "ignore": ["node_modules", "dist"] - }, - "formatter": { - "enabled": true, - "indentStyle": "space", - "indentWidth": 4, - "ignore": ["node_modules", "dist"] - } -} diff --git a/federation/index.ts b/federation/index.ts new file mode 100644 index 0000000..66b882c --- /dev/null +++ b/federation/index.ts @@ -0,0 +1,53 @@ +/** + * @file index.ts + * @fileoverview Main entrypoint and export for the module + * @module federation + * @see module:federation/schemas/base + */ + +import type { z } from "zod"; +import { + Action, + ActorPublicKeyData, + ContentFormat, + Dislike, + Entity, + Extension, + Follow, + FollowAccept, + FollowReject, + Like, + Note, + Patch, + Publication, + Report, + ServerMetadata, + Undo, + User, + VanityExtension, + Visibility, +} from "~/federation/schemas/base"; + +export type InferType = z.infer; + +export { + Entity, + ContentFormat, + Visibility, + Publication, + Note, + Patch, + ActorPublicKeyData, + VanityExtension, + User, + Action, + Like, + Undo, + Dislike, + Follow, + FollowAccept, + FollowReject, + Extension, + Report, + ServerMetadata, +}; diff --git a/federation/package.json b/federation/package.json index 0330d27..20664a8 100644 --- a/federation/package.json +++ b/federation/package.json @@ -1,63 +1,67 @@ { - "name": "@lysand-org/federation", - "displayName": "Lysand Federation", - "version": "3.0.0", - "author": { - "email": "jesse.wierzbinski@lysand.org", - "name": "Jesse Wierzbinski (CPlusPatch)", - "url": "https://cpluspatch.com" - }, - "readme": "README.md", - "repository": { - "type": "git", - "url": "https://github.com/lysand-org/api.git", - "directory": "federation" - }, - "bugs": { - "url": "https://github.com/lysand-org/api/issues" - }, - "license": "MIT", - "contributors": [ - { - "name": "Jesse Wierzbinski", - "email": "jesse.wierzbinski@lysand.org", - "url": "https://cpluspatch.com" - } - ], - "maintainers": [ - { - "name": "Jesse Wierzbinski", - "email": "jesse.wierzbinski@lysand.org", - "url": "https://cpluspatch.com" - } - ], - "description": "Type definitions for Lysand Federation, with validators.", - "categories": ["Other"], - "type": "module", - "engines": { - "bun": ">=1.1.8" - }, - "exports": { - "import": "./dist/index.js", - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/lysand" - }, - "homepage": "https://lysand.org", - "keywords": [ - "lysand", - "federation", - "api", - "typescript", - "zod", - "validation" - ], - "packageManager": "bun@1.1.8", - "devDependencies": { - "@biomejs/biome": "^1.7.3" - }, - "trustedDependencies": ["@biomejs/biome"] + "name": "@lysand-org/federation", + "displayName": "Lysand Federation", + "version": "3.0.0", + "author": { + "email": "jesse.wierzbinski@lysand.org", + "name": "Jesse Wierzbinski (CPlusPatch)", + "url": "https://cpluspatch.com" + }, + "readme": "README.md", + "repository": { + "type": "git", + "url": "https://github.com/lysand-org/api.git", + "directory": "federation" + }, + "bugs": { + "url": "https://github.com/lysand-org/api/issues" + }, + "license": "MIT", + "contributors": [ + { + "name": "Jesse Wierzbinski", + "email": "jesse.wierzbinski@lysand.org", + "url": "https://cpluspatch.com" + } + ], + "maintainers": [ + { + "name": "Jesse Wierzbinski", + "email": "jesse.wierzbinski@lysand.org", + "url": "https://cpluspatch.com" + } + ], + "description": "Type definitions for Lysand Federation, with validators.", + "categories": ["Other"], + "type": "module", + "engines": { + "bun": ">=1.1.8" + }, + "exports": { + ".": { + "import": "./dist/index.js", + "default": "./dist/index.js" + } + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/lysand" + }, + "homepage": "https://lysand.org", + "keywords": [ + "lysand", + "federation", + "api", + "typescript", + "zod", + "validation" + ], + "packageManager": "bun@1.1.8", + "dependencies": { + "@types/mime-types": "^2.1.4", + "magic-regexp": "^0.8.0", + "mime-types": "^2.1.35", + "zod": "^3.23.8", + "zod-validation-error": "^3.3.0" + } } diff --git a/federation/schemas/base.ts b/federation/schemas/base.ts new file mode 100644 index 0000000..f3cd237 --- /dev/null +++ b/federation/schemas/base.ts @@ -0,0 +1,188 @@ +import { z } from "zod"; +import { ContentFormat } from "./content_format"; +import { CustomEmojiExtension } from "./extensions/custom_emojis"; +import { VanityExtension } from "./extensions/vanity"; +import { extensionTypeRegex } from "./regex"; + +const Entity = z.object({ + id: z.string().uuid(), + created_at: z.string(), + uri: z.string().url(), + type: z.string(), + extensions: z.object({ + "org.lysand:custom_emojis": CustomEmojiExtension.optional(), + }), +}); + +const Visibility = z.enum(["public", "unlisted", "private", "direct"]); + +const Publication = Entity.extend({ + type: z.enum(["Note", "Patch"]), + author: z.string().url(), + content: ContentFormat.optional(), + attachments: z.array(ContentFormat).optional(), + replies_to: z.string().url().optional(), + quotes: z.string().url().optional(), + mentions: z.array(z.string().url()).optional(), + subject: z.string().optional(), + is_sensitive: z.boolean().optional(), + visibility: Visibility, + extensions: Entity.shape.extensions.extend({ + "org.lysand:reactions": z + .object({ + reactions: z.string(), + }) + .optional(), + "org.lysand:polls": z + .object({ + poll: z.object({ + options: z.array(ContentFormat), + votes: z.array(z.number().int().nonnegative()), + multiple_choice: z.boolean().optional(), + expires_at: z.string(), + }), + }) + .optional(), + }), +}); + +const Note = Publication.extend({ + type: z.literal("Note"), +}); + +const Patch = Publication.extend({ + type: z.literal("Patch"), + patched_id: z.string().uuid(), + patched_at: z.string(), +}); + +const ActorPublicKeyData = z.object({ + public_key: z.string(), + actor: z.string().url(), +}); + +const User = Entity.extend({ + type: z.literal("User"), + display_name: z.string().optional(), + username: z.string(), + avatar: ContentFormat.optional(), + header: ContentFormat.optional(), + indexable: z.boolean(), + public_key: ActorPublicKeyData, + bio: ContentFormat.optional(), + fields: z + .array( + z.object({ + name: ContentFormat, + value: ContentFormat, + }), + ) + .optional(), + featured: z.string().url(), + followers: z.string().url(), + following: z.string().url(), + likes: z.string().url(), + dislikes: z.string().url(), + inbox: z.string().url(), + outbox: z.string().url(), + extensions: Entity.shape.extensions.extend({ + "org.lysand:vanity": VanityExtension.optional(), + }), +}); + +const Action = Entity.extend({ + type: z.union([ + z.literal("Like"), + z.literal("Dislike"), + z.literal("Follow"), + z.literal("FollowAccept"), + z.literal("FollowReject"), + z.literal("Announce"), + z.literal("Undo"), + ]), + author: z.string().url(), +}); + +const Like = Action.extend({ + type: z.literal("Like"), + object: z.string().url(), +}); + +const Undo = Action.extend({ + type: z.literal("Undo"), + object: z.string().url(), +}); + +const Dislike = Action.extend({ + type: z.literal("Dislike"), + object: z.string().url(), +}); + +const Follow = Action.extend({ + type: z.literal("Follow"), + followee: z.string().url(), +}); + +const FollowAccept = Action.extend({ + type: z.literal("FollowAccept"), + follower: z.string().url(), +}); + +const FollowReject = Action.extend({ + type: z.literal("FollowReject"), + follower: z.string().url(), +}); + +const Extension = Entity.extend({ + type: z.literal("Extension"), + extension_type: z + .string() + .regex( + extensionTypeRegex, + "extension_type must be in the format 'namespaced_url:extension_name/ExtensionType', e.g. 'org.lysand:reactions/Reaction'. Notably, only the type can have uppercase letters.", + ), +}); + +const Report = Extension.extend({ + extension_type: z.literal("org.lysand:reports/Report"), + objects: z.array(z.string().url()), + reason: z.string(), + comment: z.string().optional(), +}); + +const ServerMetadata = Entity.extend({ + type: z.literal("ServerMetadata"), + name: z.string(), + version: z.string(), + description: z.string().optional(), + website: z.string().optional(), + moderators: z.array(z.string()).optional(), + admins: z.array(z.string()).optional(), + logo: ContentFormat.optional(), + banner: ContentFormat.optional(), + supported_extensions: z.array(z.string()), + extensions: z.record(z.string(), z.any()).optional(), +}); + +export { + Entity, + Visibility, + Publication, + Note, + Patch, + ActorPublicKeyData, + VanityExtension, + User, + Action, + Like, + Undo, + Dislike, + Follow, + FollowAccept, + FollowReject, + Extension, + Report, + ServerMetadata, + ContentFormat, + CustomEmojiExtension, +}; diff --git a/federation/schemas/content_format.ts b/federation/schemas/content_format.ts new file mode 100644 index 0000000..305f547 --- /dev/null +++ b/federation/schemas/content_format.ts @@ -0,0 +1,17 @@ +import { types } from "mime-types"; +import { z } from "zod"; + +export const ContentFormat = z.record( + z.enum(Object.values(types) as [string, ...string[]]), + z.object({ + content: z.string(), + description: z.string().optional(), + size: z.number().int().nonnegative().optional(), + hash: z.record(z.string(), z.string()).optional(), + blurhash: z.string().optional(), + fps: z.number().int().nonnegative().optional(), + width: z.number().int().nonnegative().optional(), + height: z.number().int().nonnegative().optional(), + duration: z.number().nonnegative().optional(), + }), +); diff --git a/federation/schemas/extensions/custom_emojis.ts b/federation/schemas/extensions/custom_emojis.ts new file mode 100644 index 0000000..a848e44 --- /dev/null +++ b/federation/schemas/extensions/custom_emojis.ts @@ -0,0 +1,50 @@ +/** + * Custom emojis extension. + * @module federation/schemas/extensions/custom_emojis + * @see module:federation/schemas/base + * @see https://lysand.org/extensions/custom-emojis + */ +import { z } from "zod"; +import { ContentFormat } from "../content_format"; +import { emojiRegex } from "../regex"; + +/** + * @description Used to validate the properties the extension's custom field + * @see https://lysand.org/extensions/custom-emojis + * @example + * { + * // ... + * "extensions": { + * "org.lysand:custom_emojis": { + * "emojis": [ + * { + * "name": "happy_face", + * "url": { + * "image/png": { + * "content": "https://cdn.example.com/emojis/happy_face.png", + * "content_type": "image/png" + * } + * } + * }, + * // ... + * ] + * } + * } + * // ... + * } + */ +export const CustomEmojiExtension = z.object({ + emojis: z.array( + z.object({ + name: z + .string() + .min(1) + .max(256) + .regex( + emojiRegex, + "Emoji name must be alphanumeric, underscores, or dashes.", + ), + url: ContentFormat, + }), + ), +}); diff --git a/federation/schemas/extensions/polls.ts b/federation/schemas/extensions/polls.ts new file mode 100644 index 0000000..c10697b --- /dev/null +++ b/federation/schemas/extensions/polls.ts @@ -0,0 +1,92 @@ +/** + * Polls extension + * @module federation/schemas/extensions/polls + * @see module:federation/schemas/base + * @see https://lysand.org/extensions/polls + */ +import { z } from "zod"; +import { Extension } from "../base"; +import { ContentFormat } from "../content_format"; + +/** + * @description Poll extension entity + * @see https://lysand.org/extensions/polls + * @example + * { + * "type": "Extension", + * "id": "d6eb84ea-cd13-43f9-9c54-01244da8e5e3", + * "extension_type": "org.lysand:polls/Poll", + * "uri": "https://example.com/polls/d6eb84ea-cd13-43f9-9c54-01244da8e5e3", + * "options": [ + * { + * "text/plain": { + * "content": "Red" + * } + * }, + * { + * "text/plain": { + * "content": "Blue" + * } + * }, + * { + * "text/plain": { + * "content": "Green" + * } + * } + * ], + * "votes": [ + * 9, + * 5, + * 0 + * ], + * "multiple_choice": false, + * "expires_at": "2021-01-04T00:00:00.000Z" + * } + */ +export const Poll = Extension.extend({ + extension_type: z.literal("org.lysand:polls/Poll"), + options: z.array(ContentFormat), + votes: z.array(z.number().int().nonnegative()), + multiple_choice: z.boolean().optional(), + expires_at: z.string(), +}); + +/** + * @description Vote extension entity + * @see https://lysand.org/extensions/polls + * @example + * { + * "type": "Extension", + * "id": "31c4de70-e266-4f61-b0f7-3767d3ccf565", + * "created_at": "2021-01-01T00:00:00.000Z", + * "uri": "https://example.com/votes/31c4de70-e266-4f61-b0f7-3767d3ccf565", + * "extension_type": "org.lysand:polls/Vote", + * "poll": "https://example.com/polls/31c4de70-e266-4f61-b0f7-3767d3ccf565", + * "option": 1 + * } + */ +export const Vote = Extension.extend({ + extension_type: z.literal("org.lysand:polls/Vote"), + poll: z.string().url(), + option: z.number(), +}); + +/** + * @description Vote result extension entity + * @see https://lysand.org/extensions/polls + * @example + * { + * "type": "Extension", + * "id": "c6d5755b-f42c-418f-ab53-2ee3705d6628", + * "created_at": "2021-01-01T00:00:00.000Z", + * "uri": "https://example.com/polls/c6d5755b-f42c-418f-ab53-2ee3705d6628/result", + * "extension_type": "org.lysand:polls/VoteResult", + * "poll": "https://example.com/polls/c6d5755b-f42c-418f-ab53-2ee3705d6628", + * "votes": [9, 5, 0] + * } + */ +export const VoteResult = Extension.extend({ + extension_type: z.literal("org.lysand:polls/VoteResult"), + poll: z.string().url(), + votes: z.array(z.number().int().nonnegative()), +}); diff --git a/federation/schemas/extensions/reactions.ts b/federation/schemas/extensions/reactions.ts new file mode 100644 index 0000000..ed6c9ea --- /dev/null +++ b/federation/schemas/extensions/reactions.ts @@ -0,0 +1,28 @@ +/** + * Reactions extension + * @module federation/schemas/extensions/reactions + * @see module:federation/schemas/base + * @see https://lysand.org/extensions/reactions + */ +import { z } from "zod"; +import { Extension } from "../base"; + +/** + * @description Reaction extension entity + * @see https://lysand.org/extensions/reactions + * @example + * { + * "type": "Extension", + * "id": "d6eb84ea-cd13-43f9-9c54-01244da8e5e3", + * "created_at": "2021-01-01T00:00:00.000Z", + * "uri": "https://example.com/reactions/d6eb84ea-cd13-43f9-9c54-01244da8e5e3", + * "extension_type": "org.lysand:reactions/Reaction", + * "object": "https://example.com/posts/d6eb84ea-cd13-43f9-9c54-01244da8e5e3", + * "content": "👍" + * } + */ +export const Reaction = Extension.extend({ + extension_type: z.literal("org.lysand:reactions/Reaction"), + object: z.string().url(), + content: z.string(), +}); diff --git a/federation/schemas/extensions/vanity.ts b/federation/schemas/extensions/vanity.ts new file mode 100644 index 0000000..0fabc46 --- /dev/null +++ b/federation/schemas/extensions/vanity.ts @@ -0,0 +1,93 @@ +/** + * Vanity extension schema. + * @module federation/schemas/extensions/vanity + * @see module:federation/schemas/base + * @see https://lysand.org/extensions/vanity + */ + +import { z } from "zod"; +import { ContentFormat } from "../content_format"; + +/** + * @description Vanity extension entity + * @see https://lysand.org/extensions/vanity + * @example + * { + * // ... + * "type": "User", + * // ... + * "extensions": { + * "org.lysand:vanity": { + * "avatar_overlay": { + * "image/png": { + * "content": "https://cdn.example.com/ab5081cf-b11f-408f-92c2-7c246f290593/cat_ears.png", + * "content_type": "image/png" + * } + * }, + * "avatar_mask": { + * "image/png": { + * "content": "https://cdn.example.com/d8c42be1-d0f7-43ef-b4ab-5f614e1beba4/rounded_square.jpeg", + * "content_type": "image/jpeg" + * } + * }, + * "background": { + * "image/png": { + * "content": "https://cdn.example.com/6492ddcd-311e-4921-9567-41b497762b09/untitled-file-0019822.png", + * "content_type": "image/png" + * } + * }, + * "audio": { + * "audio/mpeg": { + * "content": "https://cdn.example.com/4da2f0d4-4728-4819-83e4-d614e4c5bebc/michael-jackson-thriller.mp3", + * "content_type": "audio/mpeg" + * } + * }, + * "pronouns": { + * "en-us": [ + * "he/him", + * { + * "subject": "they", + * "object": "them", + * "dependent_possessive": "their", + * "independent_possessive": "theirs", + * "reflexive": "themself" + * }, + * ] + * }, + * "birthday": "1998-04-12", + * "location": "+40.6894-074.0447/", + * "activitypub": [ + * "@erikuden@mastodon.de" + * ], + * "aliases": [ + * "https://burger.social/accounts/349ee237-c672-41c1-aadc-677e185f795a", + * "https://social.lysand.org/users/f565ef02-035d-4974-ba5e-f62a8558331d" + * ] + * } + * } + * } + */ +export const VanityExtension = z.object({ + avatar_overlay: ContentFormat.optional(), + avatar_mask: ContentFormat.optional(), + background: ContentFormat.optional(), + audio: ContentFormat.optional(), + pronouns: z.record( + z.string(), + z.array( + z.union([ + z.object({ + subject: z.string(), + object: z.string(), + dependent_possessive: z.string(), + independent_possessive: z.string(), + reflexive: z.string(), + }), + z.string(), + ]), + ), + ), + birthday: z.string().optional(), + location: z.string().optional(), + activitypub: z.string().optional(), +}); diff --git a/federation/schemas/regex.ts b/federation/schemas/regex.ts new file mode 100644 index 0000000..f7cf362 --- /dev/null +++ b/federation/schemas/regex.ts @@ -0,0 +1,40 @@ +/** + * Regular expressions for matching various strings. + * @module federation/schemas/regex + * @see module:federation/schemas/base + */ + +import { + caseInsensitive, + charIn, + createRegExp, + digit, + exactly, + global, + letter, + oneOrMore, +} from "magic-regexp"; + +/** + * Regular expression for matching emojis. + */ +export const emojiRegex = createRegExp( + // A-Z a-z 0-9 _ - + oneOrMore(letter.or(digit).or(exactly("_")).or(exactly("-"))), + [caseInsensitive, global], +); + +/** + * Regular expression for matching an extension_type + * @example org.lysand:custom_emojis/Emoji + */ +export const extensionTypeRegex = createRegExp( + // org namespace, then colon, then alphanumeric/_/-, then extension name + exactly( + oneOrMore(exactly(letter.lowercase.or(digit).or(charIn("_-.")))), + exactly(":"), + oneOrMore(exactly(letter.lowercase.or(digit).or(charIn("_-")))), + exactly("/"), + oneOrMore(exactly(letter.or(digit).or(charIn("_-")))), + ), +); diff --git a/federation/tests/index.test.ts b/federation/tests/index.test.ts new file mode 100644 index 0000000..25ce70a --- /dev/null +++ b/federation/tests/index.test.ts @@ -0,0 +1,12 @@ +import { describe, expect, it } from "bun:test"; +import { Note } from "../schemas/base"; + +describe("Package testing", () => { + it("should not validate a bad Note", () => { + const badObject = { + IamBad: "Note", + }; + + expect(Note.parseAsync(badObject)).rejects.toThrow(); + }); +}); diff --git a/federation/tsconfig.json b/federation/tsconfig.json new file mode 100644 index 0000000..8fc1e6a --- /dev/null +++ b/federation/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "paths": { + "~/*": ["./*"] + } + } +} diff --git a/package.json b/package.json index d4883b9..8072c1f 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,19 @@ { - "name": "lysand-api", - "private": true, - "workspaces": ["federation"], - "scripts": { - "lint": "bunx @biomejs/biome check .", - "build": "echo 'Not implemented :(' && exit 1" - } + "name": "lysand-api", + "private": true, + "workspaces": ["federation"], + "scripts": { + "lint": "bunx @biomejs/biome check .", + "build": "bun run build.ts" + }, + "devDependencies": { + "@biomejs/biome": "^1.7.3", + "bun-plugin-dts": "^0.2.3", + "bun-types": "^1.1.8" + }, + "trustedDependencies": ["@biomejs/biome"], + "dependencies": { + "chalk": "^5.3.0", + "ora": "^8.0.1" + } } diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..2f1460a --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,32 @@ +{ + "compilerOptions": { + "lib": ["ESNext", "DOM", "DOM.Iterable"], + "module": "esnext", + "target": "esnext", + "moduleResolution": "Bundler", + "moduleDetection": "force", + "allowImportingTsExtensions": true, + "noEmit": true, + "composite": true, + "strict": true, + "downlevelIteration": true, + "skipLibCheck": true, + "noImplicitAny": true, + "jsx": "preserve", + "allowSyntheticDefaultImports": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "forceConsistentCasingInFileNames": true, + "allowJs": true, + "emitDecoratorMetadata": false, + "experimentalDecorators": true, + "verbatimModuleSyntax": true, + "types": [ + "bun-types" // add Bun global + ], + "paths": { + "~/*": ["./*"] + } + }, + "include": ["*.ts", "*.d.ts", "**/*.ts", "**/*.d.ts"] +}