Merge pull request #8 from sern-handler/feat/publisher

publisher
This commit is contained in:
Jacob Nguyen
2024-06-19 23:05:22 -05:00
committed by GitHub
11 changed files with 670 additions and 26 deletions

1
.gitignore vendored
View File

@@ -13,3 +13,4 @@ packages/ioc/node_modules/*
packages/ioc/dist
packages/poster/dts/discord.d.ts
packages/**/node_modules
packages/**/dist

View File

@@ -3,15 +3,15 @@
"version": "1.0.4",
"description": "Dependency Injection system",
"main": "dist/index.js",
"module": "./dist/index.js",
"module": "./dist/index.js",
"exports": {
"." : {
"import": "./dist/index.js",
"require": "./dist/index.js"
".": {
"import": "./dist/index.js",
"require": "./dist/index.js"
},
"./global": {
"import": "./dist/global.js",
"require": "./dist/global.js"
"import": "./dist/global.js",
"require": "./dist/global.js"
}
},
"scripts": {

View File

@@ -25,8 +25,9 @@
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
/* Modules */
"module": "commonjs", /* Specify what module code is generated. */
"rootDir": "./src", /* Specify the root folder within your source files. */
"module": "commonjs", /* Specify what module code is generated. */
// "rootDir": "./", /* Specify the root folder within your source files. */
"moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */

View File

@@ -2,7 +2,7 @@
title: Localizer
description: Translate your bot for the world
sidebar:
order: 1
order: 2
---
@@ -54,7 +54,7 @@ Create the directory `assets/locals`. Each json file in here must be named after
<Tabs>
<TabItem label="Spanish">
```json title=~/assets/locals/es.json
```json title=~/assets/locals/es-ES.json
{
"salute": {
"hello": "hola"
@@ -82,7 +82,7 @@ execute : (ctx, { deps }) => {
//the localizer object from makeDependencies
deps.localizer
// Returns the Spanish translation for 'salute.hello'
deps.localizer.translate("salute.hello", "es");
deps.localizer.translate("salute.hello", "es-ES");
}
```
@@ -91,5 +91,5 @@ execute : (ctx, { deps }) => {
import { local } from '@sern/localizer';
// Returns the Spanish translation for 'salute.hello'
const greeting = local('salute.hello', 'es');
const greeting = local('salute.hello', 'es-ES');
```

View File

@@ -54,10 +54,10 @@ class ShrimpleLocalizer implements Init {
* Note: this method only works AFTER your container has been initiated
* @example
* ```ts
* assert.deepEqual(locals("salute.hello", "es"), "hola")
* assert.deepEqual(locals("salute.hello", "es-ES"), "hola")
* ```
*/
export const local = (i: string, local: string) => {
export const local = (i: string, local: string) => {
return Service('localizer').translate(i, local)
}
@@ -67,8 +67,9 @@ export const local = (i: string, local: string) => {
/**
* An init plugin to add localization fields to a command module.
* Your localization configuration should look like,
* sets nloc and dloc on locals field of module.
* @param root {string} If you have conflicting command names, you may configure the root of the name. (= command/{root})
* Below is es.json (spanish)
* Below is es-ES.json (spanish)
* ```json
{
"command/comer" : {
@@ -87,16 +88,16 @@ export const localize = (root?: string) =>
//@ts-ignore
CommandInitPlugin(({ module, deps }) => {
if(module.type === CommandType.Slash || module.type === CommandType.Both) {
deps['@sern/logger'].info({ message: "Localizing "+ module.name });
const resolvedLocalization= 'command/'+(root??module.name);
Reflect.set(module, 'name_localizations', deps.localizer.translationsFor(resolvedLocalization+".name"));
Reflect.set(module, 'description_localizations', deps.localizer.translationsFor(resolvedLocalization+'.description'));
const newOpts = module.options ?? [];
//@ts-ignore
dfsApplyLocalization(newOpts, deps, [resolvedLocalization]);
const { localizer, '@sern/logger':log } = deps
const resolvedRoot = 'command/'+(root??module.name);
log?.info({ message: "Localizing "+ resolvedRoot });
//@ts-ignore
dfsApplyLocalization(module.options ?? [], deps, [resolvedRoot]);
Reflect.set(module.locals, 'nloc', localizer.translationsFor(resolvedRoot+".name"))
Reflect.set(module.locals, 'dloc', localizer.translationsFor(resolvedRoot+'.description'))
return controller.next();
} else {
//@ts-ignore
//@ts-ignore
return controller.stop("Cannot localize this type of module " + module.name);
}
})

View File

@@ -0,0 +1,106 @@
---
title: Publisher
description: Publish application commands as a Service
sidebar:
order: 1
---
## Implicits
- Requires process.env to be populated
- A common provider of this is `dotenv`
```txt title=".env"
DISCORD_TOKEN=<YOUR_TOKEN>
NODE_ENV=<production|development>
```
- Calls the discord API with the [PUT route](https://discord.com/developers/docs/interactions/application-commands#bulk-overwrite-global-application-commands). Wherever your commands directory is located, publish will override the existing application commands at Discord.
## Usage
**Initializing the Publisher**
```ts
import { makeDependencies } from '@sern/handler';
import { Publisher } from '@sern/publisher';
await makeDependencies(({ add }) => {
add('publisher', new Publisher());
});
```
## Features
- Automatically syncs api with your command base
- generates JSON file of output (**.sern/command-data-remote.json**)
- supports a configuration that is the same as the original publish plugin.
Each command file can have an extra plugin `publishConfig` that follows `ValidPublishOptions`:
## Config
```ts
type ValidMemberPermissions =
| typeof PermissionFlagBits //discord.js enum
| typeof PermissionFlagBits[] //array of discord.js enum
| string //must be a stringified number
| bigint
interface PublishConfig {
guildIds?: string[];
defaultMemberPermissions?: ValidMemberPermissions;
integrationTypes?: Array<'Guild'|'User'>
contexts: number[]
}
type ValidPublishOptions =
| PublishConfig
| (absPath: string, module: CommandModule) => PublishConfig
```
:::tip
These types are exported under @sern/publisher
:::
### Example: command published with integrationTypes
:::tip
Make sure you modify the install method in the Discord dev portal
:::
```ts title=src/commands/ping.ts
import { commandModule, CommandType } from '@sern/handler'
import { publishConfig } from '@sern/publisher'
export default commandModule( {
type: CommandType.Slash,
plugins: [
publishConfig({
integrationTypes: ['User'],
contexts: [1,2]
})
],
description: `hello worl`,
execute: (ctx) => {
ctx.reply('pong')
}
})
```
### Example: command published in guild
```ts title=src/commands/ping.ts
import { commandModule, CommandType } from '@sern/handler'
import { publishConfig } from '@sern/publisher'
export default commandModule( {
type: CommandType.Slash,
plugins: [
publishConfig({
guildIds: ["889026545715400705"]
})
],
description: `hello worl`,
execute: (ctx) => {
ctx.reply('pong')
}
})
```

226
packages/publisher/index.ts Normal file
View File

@@ -0,0 +1,226 @@
import type { Init, CommandModule, Emitter, Logging } from '@sern/handler'
import { controller, CommandInitPlugin, CommandType } from '@sern/handler'
import { writeFile } from 'node:fs/promises';
import { inspect } from 'node:util';
import type { PermissionFlagsBits } from 'discord.js'
const optionsTransformer = (ops?: Array<{ type: number }>) => {
return ops?.map((el) => {
if ('command' in el) {
const { command, ...rest } = el;
return rest;
}
return el;
}) ?? [];
};
const intoApplicationType = (type: number) =>
type === 3 ? 1 : Math.log2(type);
const makeDescription = (type: number, desc: string) => {
if (type !== CommandType.Text && desc !== '') {
console.warn('Found context menu that has non empty description field. Implictly publishing with empty description');
return '';
}
return desc;
};
const serializePerms = (perms: unknown) => {
if(typeof perms === 'bigint' || typeof perms === 'number') {
return perms.toString();
}
if(Array.isArray(perms)) {
return perms.reduce((acc, cur) => acc|cur, BigInt(0))
.toString()
}
return null;
}
const BASE_URL = new URL('https://discord.com/api/v10/applications/');
const PUBLISHABLE = 0b1110;
export class Publisher implements Init {
constructor(private modules: Map<string, CommandModule>,
private sernEmitter : Emitter,
private logger: Logging) {}
async init() {
if(!process.env.DISCORD_TOKEN) {
throw Error("No token found to publish. add DISCORD_TOKEN to .env");
}
const headers = [['Authorization', 'Bot ' + process.env.DISCORD_TOKEN],
['Content-Type', 'application/json']] as Array<[string,string]>
let me;
let appid: string;
try {
me = await fetch(new URL('@me', BASE_URL), { headers }).then(res => res.json());
appid = me.id;
} catch(e) {
console.log("Something went wrong while trying to fetch your application:");
throw e;
}
const GLOBAL_URL = new URL(`${appid}/commands`, BASE_URL);
interface LocalPublish {
guildIds?: string[]
default_member_permissions: string,
integration_types: string[],
contexts: number[]
}
const listener = async () => {
this.logger.info({ message: 'publishing modules' });
const modules =
Array.from(this.modules.values())
.filter(module => (module.type & PUBLISHABLE) != 0)
.map(module => {
const publish = module.locals.publish as LocalPublish || {}
return {
guildIds: publish?.guildIds ?? [],
toJSON() {
const applicationType = intoApplicationType(module.type);
const { default_member_permissions,
integration_types,
contexts } = publish;
return {
name: module.name, type: applicationType,
//@ts-ignore we know description is at least empty str or filled
description: makeDescription(applicationType, module.description),
//@ts-ignore shutup
options: optionsTransformer(module?.options),
default_member_permissions,
integration_types, contexts,
//@ts-ignore
name_localizations: module.locals.nloc,
//@ts-ignore
description_localizations: module.locals.dloc
}
}
}
})
const [globalCommands, guildedCommands] = modules.reduce(
//technically these aren't sern/handler modules.
([globals, guilded], module) => {
const isPublishableGlobally = !module.guildIds || module.guildIds.length === 0;
if (isPublishableGlobally) {
return [[module, ...globals], guilded];
}
return [globals, [module, ...guilded]];
}, [[], []] as [any[], any[]]);
const resultGlobal = await fetch(GLOBAL_URL, {
method: 'PUT',
headers,
body: JSON.stringify(globalCommands)
})
const globalJsonBody = await resultGlobal.json();
if(resultGlobal.ok) {
this.logger.info({ message: "Publisher: All global commands published." })
} else {
this.logger.info({ message: inspect(globalJsonBody, false, Infinity ) })
//todo: implement rate limiting
}
const guildIdMap: Map<string, CommandModule[]> = new Map();
const responsesMap = new Map();
guildedCommands.forEach((entry) => {
const guildIds: string[] = entry.guildIds ?? [];
if (guildIds) {
guildIds.forEach((guildId) => {
if (guildIdMap.has(guildId)) {
guildIdMap.get(guildId)?.push(entry);
} else {
guildIdMap.set(guildId, [entry]);
}
});
}
});
for (const [guildId, array] of guildIdMap.entries()) {
const guildCommandURL = new URL(`${appid}/guilds/${guildId}/commands`, BASE_URL);
const response = await fetch(guildCommandURL, {
method: 'PUT',
body: JSON.stringify(array),
headers,
});
const result = await response.json();
if (response.ok) {
this.logger.info({ message: guildId + " published succesfully" })
responsesMap.set(guildId, result);
} else {
switch(response.status) {
case 400 : {
console.error(inspect(result, { depth: Infinity }))
console.error("Modules with validation errors:"
+ inspect(Object.keys(result.errors).map(idx => array[idx as any])))
throw Error("400: Ensure your commands have proper fields and data and nothing left out");
}
case 404 : {
console.error(inspect(result, { depth: Infinity }))
throw Error("Forbidden 404. Is you application id and/or token correct?")
}
case 429: {
console.error(inspect(result, { depth: Infinity }))
throw Error('Chill out homie, too many requests')
}
}
}
}
await writeFile(
'.sern/command-data-remote.json',
JSON.stringify({ global: globalJsonBody,
...Object.fromEntries(responsesMap) }, null, 4),
'utf8')
}
this.sernEmitter.addListener('modulesLoaded', () => {
listener();
this.sernEmitter.removeListener('modulesLoaded', listener);
})
}
}
export type ValidMemberPermissions =
| typeof PermissionFlagsBits //discord.js enum
| Array<typeof PermissionFlagsBits>
| string //must be a stringified number
| bigint
export interface PublishConfig {
guildIds?: string[];
defaultMemberPermissions?: ValidMemberPermissions;
integrationTypes?: Array<'Guild'|'User'>
contexts?: number[]
}
export type ValidPublishOptions =
| PublishConfig
| ((absPath: string, module: CommandModule) => PublishConfig)
const IntegrationType = {
Guild: '0', User: '1'
}
/**
* the publishConfig plugin.
* If your commandModule requires extra properties such as publishing for certain guilds, you would
* put those options in there.
* sets 'publish' on locals field for modules.
* @param {ValidPublishOptions} config options to configure how this module is published
*/
export const publishConfig = (config: ValidPublishOptions) => {
return CommandInitPlugin(({ module, absPath }) => {
if((module.type & PUBLISHABLE) === 0) {
//@ts-ignore
return controller.stop("Cannot publish this module; Not of type Both,Slash,CtxUsr,CtxMsg.");
}
let _config=config
if(typeof _config === 'function') {
_config = _config(absPath, module as CommandModule);
}
const { contexts, defaultMemberPermissions, integrationTypes:integration_types, guildIds } = _config
Reflect.set(module.locals, 'publish', {
guildIds,
contexts,
integration_types: integration_types?.map(i => Reflect.get(IntegrationType, i)),
default_member_permissions: serializePerms(defaultMemberPermissions),
})
return controller.next();
})
}

View File

@@ -0,0 +1 @@
{ "type" : "service" }

View File

@@ -0,0 +1,26 @@
{
"name": "@sern/publisher",
"version": "1.1.0",
"description": "Localizer",
"main": "dist/index.js",
"scripts": {
"build": "tsc",
"watch": "tsc --watch",
"test": "exit 0"
},
"devDependencies": {
"@sern/handler": "^3.3.0",
"discord.js": "^14.15.3",
"vitest": "^1.2.2"
},
"keywords": [],
"author": "",
"license": "ISC",
"publishConfig": {
"access": "public",
"tag": "alpha"
},
"dependencies": {
"@parcel/watcher": "^2.4.1"
}
}

View File

@@ -0,0 +1,112 @@
{
"files": [
"./index.ts"
],
"compilerOptions": {
/* Visit https://aka.ms/tsconfig to read more about this file */
/* Projects */
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */
"target": "ESNext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
/* Modules */
"module": "CommonJS", /* Specify what module code is generated. */
// "rootDir": "./", /* Specify the root folder within your source files. */
"moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
// "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
// "resolveJsonModule": true, /* Enable importing .json files. */
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
/* JavaScript Support */
"allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
/* Emit */
"declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
"outDir": "./dist", /* Specify an output folder for all emitted files. */
// "removeComments": true, /* Disable emitting comments. */
// "noEmit": true, /* Disable emitting files from a compilation. */
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
// "newLine": "crlf", /* Set the newline character for emitting files. */
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
/* Interop Constraints */
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
/* Type Checking */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}

178
yarn.lock
View File

@@ -331,6 +331,140 @@ __metadata:
languageName: node
linkType: hard
"@parcel/watcher-android-arm64@npm:2.4.1":
version: 2.4.1
resolution: "@parcel/watcher-android-arm64@npm:2.4.1"
conditions: os=android & cpu=arm64
languageName: node
linkType: hard
"@parcel/watcher-darwin-arm64@npm:2.4.1":
version: 2.4.1
resolution: "@parcel/watcher-darwin-arm64@npm:2.4.1"
conditions: os=darwin & cpu=arm64
languageName: node
linkType: hard
"@parcel/watcher-darwin-x64@npm:2.4.1":
version: 2.4.1
resolution: "@parcel/watcher-darwin-x64@npm:2.4.1"
conditions: os=darwin & cpu=x64
languageName: node
linkType: hard
"@parcel/watcher-freebsd-x64@npm:2.4.1":
version: 2.4.1
resolution: "@parcel/watcher-freebsd-x64@npm:2.4.1"
conditions: os=freebsd & cpu=x64
languageName: node
linkType: hard
"@parcel/watcher-linux-arm-glibc@npm:2.4.1":
version: 2.4.1
resolution: "@parcel/watcher-linux-arm-glibc@npm:2.4.1"
conditions: os=linux & cpu=arm & libc=glibc
languageName: node
linkType: hard
"@parcel/watcher-linux-arm64-glibc@npm:2.4.1":
version: 2.4.1
resolution: "@parcel/watcher-linux-arm64-glibc@npm:2.4.1"
conditions: os=linux & cpu=arm64 & libc=glibc
languageName: node
linkType: hard
"@parcel/watcher-linux-arm64-musl@npm:2.4.1":
version: 2.4.1
resolution: "@parcel/watcher-linux-arm64-musl@npm:2.4.1"
conditions: os=linux & cpu=arm64 & libc=musl
languageName: node
linkType: hard
"@parcel/watcher-linux-x64-glibc@npm:2.4.1":
version: 2.4.1
resolution: "@parcel/watcher-linux-x64-glibc@npm:2.4.1"
conditions: os=linux & cpu=x64 & libc=glibc
languageName: node
linkType: hard
"@parcel/watcher-linux-x64-musl@npm:2.4.1":
version: 2.4.1
resolution: "@parcel/watcher-linux-x64-musl@npm:2.4.1"
conditions: os=linux & cpu=x64 & libc=musl
languageName: node
linkType: hard
"@parcel/watcher-win32-arm64@npm:2.4.1":
version: 2.4.1
resolution: "@parcel/watcher-win32-arm64@npm:2.4.1"
conditions: os=win32 & cpu=arm64
languageName: node
linkType: hard
"@parcel/watcher-win32-ia32@npm:2.4.1":
version: 2.4.1
resolution: "@parcel/watcher-win32-ia32@npm:2.4.1"
conditions: os=win32 & cpu=ia32
languageName: node
linkType: hard
"@parcel/watcher-win32-x64@npm:2.4.1":
version: 2.4.1
resolution: "@parcel/watcher-win32-x64@npm:2.4.1"
conditions: os=win32 & cpu=x64
languageName: node
linkType: hard
"@parcel/watcher@npm:^2.4.1":
version: 2.4.1
resolution: "@parcel/watcher@npm:2.4.1"
dependencies:
"@parcel/watcher-android-arm64": 2.4.1
"@parcel/watcher-darwin-arm64": 2.4.1
"@parcel/watcher-darwin-x64": 2.4.1
"@parcel/watcher-freebsd-x64": 2.4.1
"@parcel/watcher-linux-arm-glibc": 2.4.1
"@parcel/watcher-linux-arm64-glibc": 2.4.1
"@parcel/watcher-linux-arm64-musl": 2.4.1
"@parcel/watcher-linux-x64-glibc": 2.4.1
"@parcel/watcher-linux-x64-musl": 2.4.1
"@parcel/watcher-win32-arm64": 2.4.1
"@parcel/watcher-win32-ia32": 2.4.1
"@parcel/watcher-win32-x64": 2.4.1
detect-libc: ^1.0.3
is-glob: ^4.0.3
micromatch: ^4.0.5
node-addon-api: ^7.0.0
node-gyp: latest
dependenciesMeta:
"@parcel/watcher-android-arm64":
optional: true
"@parcel/watcher-darwin-arm64":
optional: true
"@parcel/watcher-darwin-x64":
optional: true
"@parcel/watcher-freebsd-x64":
optional: true
"@parcel/watcher-linux-arm-glibc":
optional: true
"@parcel/watcher-linux-arm64-glibc":
optional: true
"@parcel/watcher-linux-arm64-musl":
optional: true
"@parcel/watcher-linux-x64-glibc":
optional: true
"@parcel/watcher-linux-x64-musl":
optional: true
"@parcel/watcher-win32-arm64":
optional: true
"@parcel/watcher-win32-ia32":
optional: true
"@parcel/watcher-win32-x64":
optional: true
checksum: 4da70551da27e565c726b0bbd5ba5afcb2bca36dfd8619a649f0eaa41f693ddd1d630c36e53bc083895d71a3e28bc4199013e557cd13c7af6ccccab28ceecbff
languageName: node
linkType: hard
"@pkgjs/parseargs@npm:^0.11.0":
version: 0.11.0
resolution: "@pkgjs/parseargs@npm:0.11.0"
@@ -522,6 +656,17 @@ __metadata:
languageName: unknown
linkType: soft
"@sern/publisher@workspace:packages/publisher":
version: 0.0.0-use.local
resolution: "@sern/publisher@workspace:packages/publisher"
dependencies:
"@parcel/watcher": ^2.4.1
"@sern/handler": ^3.3.0
discord.js: ^14.15.3
vitest: ^1.2.2
languageName: unknown
linkType: soft
"@sinclair/typebox@npm:^0.27.8":
version: 0.27.8
resolution: "@sinclair/typebox@npm:0.27.8"
@@ -921,6 +1066,15 @@ __metadata:
languageName: node
linkType: hard
"detect-libc@npm:^1.0.3":
version: 1.0.3
resolution: "detect-libc@npm:1.0.3"
bin:
detect-libc: ./bin/detect-libc.js
checksum: daaaed925ffa7889bd91d56e9624e6c8033911bb60f3a50a74a87500680652969dbaab9526d1e200a4c94acf80fc862a22131841145a0a8482d60a99c24f4a3e
languageName: node
linkType: hard
"diff-sequences@npm:^29.6.3":
version: 29.6.3
resolution: "diff-sequences@npm:29.6.3"
@@ -1339,7 +1493,7 @@ __metadata:
languageName: node
linkType: hard
"is-glob@npm:^4.0.1, is-glob@npm:~4.0.1":
"is-glob@npm:^4.0.1, is-glob@npm:^4.0.3, is-glob@npm:~4.0.1":
version: 4.0.3
resolution: "is-glob@npm:4.0.3"
dependencies:
@@ -1520,7 +1674,7 @@ __metadata:
languageName: node
linkType: hard
"micromatch@npm:^4.0.4":
"micromatch@npm:^4.0.4, micromatch@npm:^4.0.5":
version: 4.0.7
resolution: "micromatch@npm:4.0.7"
dependencies:
@@ -1674,6 +1828,15 @@ __metadata:
languageName: node
linkType: hard
"node-addon-api@npm:^7.0.0":
version: 7.1.0
resolution: "node-addon-api@npm:7.1.0"
dependencies:
node-gyp: latest
checksum: 26640c8d2ed7e2059e2ed65ee79e2a195306b3f1fc27ad11448943ba91d37767bd717a9a0453cc97e83a1109194dced8336a55f8650000458ef625c0b8b5e3df
languageName: node
linkType: hard
"node-gyp@npm:latest":
version: 10.1.0
resolution: "node-gyp@npm:10.1.0"
@@ -2252,13 +2415,20 @@ __metadata:
languageName: node
linkType: hard
"tslib@npm:2.6.2, tslib@npm:^2.1.0, tslib@npm:^2.6.2":
"tslib@npm:2.6.2, tslib@npm:^2.6.2":
version: 2.6.2
resolution: "tslib@npm:2.6.2"
checksum: 329ea56123005922f39642318e3d1f0f8265d1e7fcb92c633e0809521da75eeaca28d2cf96d7248229deb40e5c19adf408259f4b9640afd20d13aecc1430f3ad
languageName: node
linkType: hard
"tslib@npm:^2.1.0":
version: 2.6.3
resolution: "tslib@npm:2.6.3"
checksum: 74fce0e100f1ebd95b8995fbbd0e6c91bdd8f4c35c00d4da62e285a3363aaa534de40a80db30ecfd388ed7c313c42d930ee0eaf108e8114214b180eec3dbe6f5
languageName: node
linkType: hard
"type-detect@npm:^4.0.0, type-detect@npm:^4.0.8":
version: 4.0.8
resolution: "type-detect@npm:4.0.8"
@@ -2278,7 +2448,7 @@ __metadata:
"typescript@patch:typescript@^5.0.0#~builtin<compat/typescript>":
version: 5.4.5
resolution: "typescript@patch:typescript@npm%3A5.4.5#~builtin<compat/typescript>::version=5.4.5&hash=f3b441"
resolution: "typescript@patch:typescript@npm%3A5.4.5#~builtin<compat/typescript>::version=5.4.5&hash=14eedb"
bin:
tsc: bin/tsc
tsserver: bin/tsserver