@willbooster/shared-lib 10.19.1 → 10.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/appEnv.cjs.map +1 -1
- package/dist/appEnv.js.map +1 -1
- package/dist/array.cjs.map +1 -1
- package/dist/array.js.map +1 -1
- package/dist/assert.cjs.map +1 -1
- package/dist/assert.js.map +1 -1
- package/dist/concurrency.cjs.map +1 -1
- package/dist/concurrency.js.map +1 -1
- package/dist/crypto.cjs.map +1 -1
- package/dist/crypto.js.map +1 -1
- package/dist/error.cjs.map +1 -1
- package/dist/error.js.map +1 -1
- package/dist/filePath.cjs.map +1 -1
- package/dist/filePath.js.map +1 -1
- package/dist/foreignCjk.cjs.map +1 -1
- package/dist/foreignCjk.js.map +1 -1
- package/dist/formatIsoDate.cjs.map +1 -1
- package/dist/formatIsoDate.js.map +1 -1
- package/dist/github.cjs.map +1 -1
- package/dist/github.js.map +1 -1
- package/dist/githubTemplates.cjs.map +1 -1
- package/dist/githubTemplates.js.map +1 -1
- package/dist/http.cjs.map +1 -1
- package/dist/http.js.map +1 -1
- package/dist/humanize.cjs.map +1 -1
- package/dist/humanize.js.map +1 -1
- package/dist/mail.cjs.map +1 -1
- package/dist/mail.js.map +1 -1
- package/dist/markdown.cjs.map +1 -1
- package/dist/markdown.js.map +1 -1
- package/dist/number.cjs.map +1 -1
- package/dist/number.js.map +1 -1
- package/dist/object.cjs.map +1 -1
- package/dist/object.js.map +1 -1
- package/dist/parseCommandLineArgs.cjs.map +1 -1
- package/dist/parseCommandLineArgs.js.map +1 -1
- package/dist/parseIsoDate.cjs.map +1 -1
- package/dist/parseIsoDate.js.map +1 -1
- package/dist/prompt.cjs +8 -8
- package/dist/prompt.cjs.map +1 -1
- package/dist/prompt.js +8 -8
- package/dist/prompt.js.map +1 -1
- package/dist/promptFormatters.cjs.map +1 -1
- package/dist/promptFormatters.js.map +1 -1
- package/dist/recoverJson.cjs +2 -2
- package/dist/recoverJson.cjs.map +1 -1
- package/dist/recoverJson.js +1 -1
- package/dist/recoverJson.js.map +1 -1
- package/dist/shuffle.cjs.map +1 -1
- package/dist/shuffle.js.map +1 -1
- package/dist/sleep.cjs.map +1 -1
- package/dist/sleep.js.map +1 -1
- package/dist/sqlite.cjs.map +1 -1
- package/dist/sqlite.js.map +1 -1
- package/dist/testingRules.js.map +1 -1
- package/dist/text.cjs.map +1 -1
- package/dist/text.js.map +1 -1
- package/dist/throttledFetch.cjs.map +1 -1
- package/dist/throttledFetch.js.map +1 -1
- package/dist/timeout.cjs.map +1 -1
- package/dist/timeout.js.map +1 -1
- package/dist/url.cjs.map +1 -1
- package/dist/url.js.map +1 -1
- package/dist/yaml.cjs.map +1 -1
- package/dist/yaml.js.map +1 -1
- package/dist/zenkaku.cjs.map +1 -1
- package/dist/zenkaku.js.map +1 -1
- package/package.json +7 -8
package/dist/appEnv.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"appEnv.cjs","names":[],"sources":["../src/appEnv.ts"],"sourcesContent":["export type AppEnv = 'development' | 'test' | 'staging' | 'production';\n\n/**\n * The runtime `WB_ENV` widened to the full domain wb standardizes.\n *\n * `wrangler types` narrows `process.env.WB_ENV` to the literal values the wrangler configuration\n * declares (typically staging and production only), while local development, E2E, and scripts\n * supply `development` and `test` through fnox (`wb gen-dev-vars` writes them into the gitignored\n * `.dev.vars` that `wrangler dev` reads). This accessor widens the generated type back to the real\n * runtime domain so environment detection stays in one place.\n *\n * Exposed as a function rather than a constant because Cloudflare Workers may not provide\n * environment variables during module evaluation; callers choose when to read. Returns undefined\n * where `process` does not exist (e.g. browsers).\n */\nexport function getAppEnv(): AppEnv | undefined {\n return typeof process === 'undefined' ? undefined : (process.env.WB_ENV as AppEnv | undefined);\n}\n"],"mappings":"aAeA,
|
|
1
|
+
{"version":3,"file":"appEnv.cjs","names":["getAppEnv"],"sources":["../src/appEnv.ts"],"sourcesContent":["export type AppEnv = 'development' | 'test' | 'staging' | 'production';\n\n/**\n * The runtime `WB_ENV` widened to the full domain wb standardizes.\n *\n * `wrangler types` narrows `process.env.WB_ENV` to the literal values the wrangler configuration\n * declares (typically staging and production only), while local development, E2E, and scripts\n * supply `development` and `test` through fnox (`wb gen-dev-vars` writes them into the gitignored\n * `.dev.vars` that `wrangler dev` reads). This accessor widens the generated type back to the real\n * runtime domain so environment detection stays in one place.\n *\n * Exposed as a function rather than a constant because Cloudflare Workers may not provide\n * environment variables during module evaluation; callers choose when to read. Returns undefined\n * where `process` does not exist (e.g. browsers).\n */\nexport function getAppEnv(): AppEnv | undefined {\n return typeof process === 'undefined' ? undefined : (process.env.WB_ENV as AppEnv | undefined);\n}\n"],"mappings":"aAeA,SAAgBA,GAAgC,CAC9C,OAAO,OAAO,QAAY,IAAc,IAAA,GAAa,QAAQ,IAAI,MACnE"}
|
package/dist/appEnv.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"appEnv.js","names":[],"sources":["../src/appEnv.ts"],"sourcesContent":["export type AppEnv = 'development' | 'test' | 'staging' | 'production';\n\n/**\n * The runtime `WB_ENV` widened to the full domain wb standardizes.\n *\n * `wrangler types` narrows `process.env.WB_ENV` to the literal values the wrangler configuration\n * declares (typically staging and production only), while local development, E2E, and scripts\n * supply `development` and `test` through fnox (`wb gen-dev-vars` writes them into the gitignored\n * `.dev.vars` that `wrangler dev` reads). This accessor widens the generated type back to the real\n * runtime domain so environment detection stays in one place.\n *\n * Exposed as a function rather than a constant because Cloudflare Workers may not provide\n * environment variables during module evaluation; callers choose when to read. Returns undefined\n * where `process` does not exist (e.g. browsers).\n */\nexport function getAppEnv(): AppEnv | undefined {\n return typeof process === 'undefined' ? undefined : (process.env.WB_ENV as AppEnv | undefined);\n}\n"],"mappings":"AAeA,
|
|
1
|
+
{"version":3,"file":"appEnv.js","names":["getAppEnv"],"sources":["../src/appEnv.ts"],"sourcesContent":["export type AppEnv = 'development' | 'test' | 'staging' | 'production';\n\n/**\n * The runtime `WB_ENV` widened to the full domain wb standardizes.\n *\n * `wrangler types` narrows `process.env.WB_ENV` to the literal values the wrangler configuration\n * declares (typically staging and production only), while local development, E2E, and scripts\n * supply `development` and `test` through fnox (`wb gen-dev-vars` writes them into the gitignored\n * `.dev.vars` that `wrangler dev` reads). This accessor widens the generated type back to the real\n * runtime domain so environment detection stays in one place.\n *\n * Exposed as a function rather than a constant because Cloudflare Workers may not provide\n * environment variables during module evaluation; callers choose when to read. Returns undefined\n * where `process` does not exist (e.g. browsers).\n */\nexport function getAppEnv(): AppEnv | undefined {\n return typeof process === 'undefined' ? undefined : (process.env.WB_ENV as AppEnv | undefined);\n}\n"],"mappings":"AAeA,SAAgBA,GAAgC,CAC9C,OAAO,OAAO,QAAY,IAAc,IAAA,GAAa,QAAQ,IAAI,MACnE"}
|
package/dist/array.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"array.cjs","names":[],"sources":["../src/array.ts"],"sourcesContent":["/** Splits items into consecutive chunks of `size` items; the last chunk holds the remainder. */\nexport function chunk<T>(items: readonly T[], size: number): T[][] {\n if (!Number.isInteger(size) || size < 1) {\n throw new RangeError(`size must be a positive integer: ${size}`);\n }\n return Array.from({ length: Math.ceil(items.length / size) }, (_, index) =>\n items.slice(index * size, (index + 1) * size)\n );\n}\n"],"mappings":"aACA,
|
|
1
|
+
{"version":3,"file":"array.cjs","names":["chunk","size","items","index"],"sources":["../src/array.ts"],"sourcesContent":["/** Splits items into consecutive chunks of `size` items; the last chunk holds the remainder. */\nexport function chunk<T>(items: readonly T[], size: number): T[][] {\n if (!Number.isInteger(size) || size < 1) {\n throw new RangeError(`size must be a positive integer: ${size}`);\n }\n return Array.from({ length: Math.ceil(items.length / size) }, (_, index) =>\n items.slice(index * size, (index + 1) * size)\n );\n}\n"],"mappings":"aACA,SAAgBA,EAAS,EAAqB,EAAqB,CACjE,GAAI,CAAC,OAAO,UAAUC,CAAI,GAAKA,EAAO,EACpC,MAAU,WAAW,oCAAoCA,GAAM,EAEjE,OAAO,MAAM,KAAK,CAAE,OAAQ,KAAK,KAAKC,EAAM,OAASD,CAAI,CAAE,GAAI,EAAG,IAChEC,EAAM,MAAMC,EAAQF,GAAOE,EAAQ,GAAKF,CAAI,CAC9C,CACF"}
|
package/dist/array.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"array.js","names":[],"sources":["../src/array.ts"],"sourcesContent":["/** Splits items into consecutive chunks of `size` items; the last chunk holds the remainder. */\nexport function chunk<T>(items: readonly T[], size: number): T[][] {\n if (!Number.isInteger(size) || size < 1) {\n throw new RangeError(`size must be a positive integer: ${size}`);\n }\n return Array.from({ length: Math.ceil(items.length / size) }, (_, index) =>\n items.slice(index * size, (index + 1) * size)\n );\n}\n"],"mappings":"AACA,
|
|
1
|
+
{"version":3,"file":"array.js","names":["chunk","size","items","index"],"sources":["../src/array.ts"],"sourcesContent":["/** Splits items into consecutive chunks of `size` items; the last chunk holds the remainder. */\nexport function chunk<T>(items: readonly T[], size: number): T[][] {\n if (!Number.isInteger(size) || size < 1) {\n throw new RangeError(`size must be a positive integer: ${size}`);\n }\n return Array.from({ length: Math.ceil(items.length / size) }, (_, index) =>\n items.slice(index * size, (index + 1) * size)\n );\n}\n"],"mappings":"AACA,SAAgBA,EAAS,EAAqB,EAAqB,CACjE,GAAI,CAAC,OAAO,UAAUC,CAAI,GAAKA,EAAO,EACpC,MAAU,WAAW,oCAAoCA,GAAM,EAEjE,OAAO,MAAM,KAAK,CAAE,OAAQ,KAAK,KAAKC,EAAM,OAASD,CAAI,CAAE,GAAI,EAAG,IAChEC,EAAM,MAAMC,EAAQF,GAAOE,EAAQ,GAAKF,CAAI,CAC9C,CACF"}
|
package/dist/assert.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assert.cjs","names":[],"sources":["../src/assert.ts"],"sourcesContent":["export function ensureTruthy<T>(name: string, value: T): NonNullable<T> {\n if (!value) {\n throw new Error(`The value of \"${name}\" must be truthy.`);\n }\n return value;\n}\n"],"mappings":"aAAA,
|
|
1
|
+
{"version":3,"file":"assert.cjs","names":["ensureTruthy","value","name"],"sources":["../src/assert.ts"],"sourcesContent":["export function ensureTruthy<T>(name: string, value: T): NonNullable<T> {\n if (!value) {\n throw new Error(`The value of \"${name}\" must be truthy.`);\n }\n return value;\n}\n"],"mappings":"aAAA,SAAgBA,EAAgB,EAAc,EAA0B,CACtE,GAAI,CAACC,EACH,MAAU,MAAM,iBAAiBC,EAAK,kBAAkB,EAE1D,OAAOD,CACT"}
|
package/dist/assert.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assert.js","names":[],"sources":["../src/assert.ts"],"sourcesContent":["export function ensureTruthy<T>(name: string, value: T): NonNullable<T> {\n if (!value) {\n throw new Error(`The value of \"${name}\" must be truthy.`);\n }\n return value;\n}\n"],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"assert.js","names":["ensureTruthy","value","name"],"sources":["../src/assert.ts"],"sourcesContent":["export function ensureTruthy<T>(name: string, value: T): NonNullable<T> {\n if (!value) {\n throw new Error(`The value of \"${name}\" must be truthy.`);\n }\n return value;\n}\n"],"mappings":"AAAA,SAAgBA,EAAgB,EAAc,EAA0B,CACtE,GAAI,CAACC,EACH,MAAU,MAAM,iBAAiBC,EAAK,kBAAkB,EAE1D,OAAOD,CACT"}
|
package/dist/concurrency.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"concurrency.cjs","names":[],"sources":["../src/concurrency.ts"],"sourcesContent":["/**\n * Runs `action` for every item with at most `concurrency` actions in flight, starting the next item as soon as any\n * action settles, so one slow item does not hold back a whole batch. After an action rejects, no further item is\n * started, and once every started action has settled, the returned promise rejects with the first error.\n * The returned promise rejects with a `RangeError` when `concurrency` is not a positive integer.\n */\nexport async function forEachConcurrently<T>(\n items: readonly T[],\n concurrency: number,\n action: (item: T, index: number) => Promise<unknown>\n): Promise<void> {\n await runConcurrently(items.length, concurrency, (index) => action(items[index] as T, index));\n}\n\n/**\n * Like `forEachConcurrently`, but resolves with the results of `mapper` in the order of `items`.\n */\nexport async function mapConcurrently<T, R>(\n items: readonly T[],\n concurrency: number,\n mapper: (item: T, index: number) => Promise<R>\n): Promise<R[]> {\n const results = Array.from<R>({ length: items.length });\n await runConcurrently(items.length, concurrency, async (index) => {\n results[index] = await mapper(items[index] as T, index);\n });\n return results;\n}\n\nasync function runConcurrently(\n length: number,\n concurrency: number,\n run: (index: number) => Promise<unknown>\n): Promise<void> {\n if (!Number.isInteger(concurrency) || concurrency < 1) {\n throw new RangeError(`concurrency must be a positive integer: ${concurrency}`);\n }\n let nextIndex = 0;\n let failure: { error: unknown } | undefined;\n // Workers beyond the item count would find nothing to take, so they are not created.\n await Promise.all(\n Array.from({ length: Math.min(concurrency, length) }, async () => {\n while (!failure && nextIndex < length) {\n try {\n await run(nextIndex++);\n } catch (error) {\n failure ??= { error };\n }\n }\n })\n );\n if (failure) throw failure.error;\n}\n"],"mappings":"aAMA,
|
|
1
|
+
{"version":3,"file":"concurrency.cjs","names":["forEachConcurrently","runConcurrently","items","concurrency","action","index","mapConcurrently","results","mapper","nextIndex","failure","length","run","error"],"sources":["../src/concurrency.ts"],"sourcesContent":["/**\n * Runs `action` for every item with at most `concurrency` actions in flight, starting the next item as soon as any\n * action settles, so one slow item does not hold back a whole batch. After an action rejects, no further item is\n * started, and once every started action has settled, the returned promise rejects with the first error.\n * The returned promise rejects with a `RangeError` when `concurrency` is not a positive integer.\n */\nexport async function forEachConcurrently<T>(\n items: readonly T[],\n concurrency: number,\n action: (item: T, index: number) => Promise<unknown>\n): Promise<void> {\n await runConcurrently(items.length, concurrency, (index) => action(items[index] as T, index));\n}\n\n/**\n * Like `forEachConcurrently`, but resolves with the results of `mapper` in the order of `items`.\n */\nexport async function mapConcurrently<T, R>(\n items: readonly T[],\n concurrency: number,\n mapper: (item: T, index: number) => Promise<R>\n): Promise<R[]> {\n const results = Array.from<R>({ length: items.length });\n await runConcurrently(items.length, concurrency, async (index) => {\n results[index] = await mapper(items[index] as T, index);\n });\n return results;\n}\n\nasync function runConcurrently(\n length: number,\n concurrency: number,\n run: (index: number) => Promise<unknown>\n): Promise<void> {\n if (!Number.isInteger(concurrency) || concurrency < 1) {\n throw new RangeError(`concurrency must be a positive integer: ${concurrency}`);\n }\n let nextIndex = 0;\n let failure: { error: unknown } | undefined;\n // Workers beyond the item count would find nothing to take, so they are not created.\n await Promise.all(\n Array.from({ length: Math.min(concurrency, length) }, async () => {\n while (!failure && nextIndex < length) {\n try {\n await run(nextIndex++);\n } catch (error) {\n failure ??= { error };\n }\n }\n })\n );\n if (failure) throw failure.error;\n}\n"],"mappings":"aAMA,eAAsBA,EACpB,EACA,EACA,EACe,CACf,MAAMC,EAAgBC,EAAM,OAAQC,EAAc,GAAUC,EAAOF,EAAMG,GAAaA,CAAK,CAAC,CAC9F,CAKA,eAAsBC,EACpB,EACA,EACA,EACc,CACd,IAAMC,EAAU,MAAM,KAAQ,CAAE,OAAQL,EAAM,MAAO,CAAC,EAItD,OAHA,MAAMD,EAAgBC,EAAM,OAAQC,EAAa,KAAO,IAAU,CAChE,EAAQE,GAAS,MAAMG,EAAON,EAAMG,GAAaA,CAAK,CACxD,CAAC,EACME,CACT,CAEA,eAAeN,EACb,EACA,EACA,EACe,CACf,GAAI,CAAC,OAAO,UAAUE,CAAW,GAAKA,EAAc,EAClD,MAAU,WAAW,2CAA2CA,GAAa,EAE/E,IAAIM,EAAY,EACZC,EAaJ,GAXA,MAAM,QAAQ,IACZ,MAAM,KAAK,CAAE,OAAQ,KAAK,IAAIP,EAAaQ,CAAM,CAAE,EAAG,SAAY,CAChE,KAAO,CAACD,GAAWD,EAAYE,GAC7B,GAAI,CACF,MAAMC,EAAI,GAAW,CACvB,OAASC,EAAO,CACd,IAAY,CAAE,OAAM,CACtB,CAEJ,CAAC,CACH,EACIH,EAAS,MAAMA,EAAQ,KAC7B"}
|
package/dist/concurrency.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"concurrency.js","names":[],"sources":["../src/concurrency.ts"],"sourcesContent":["/**\n * Runs `action` for every item with at most `concurrency` actions in flight, starting the next item as soon as any\n * action settles, so one slow item does not hold back a whole batch. After an action rejects, no further item is\n * started, and once every started action has settled, the returned promise rejects with the first error.\n * The returned promise rejects with a `RangeError` when `concurrency` is not a positive integer.\n */\nexport async function forEachConcurrently<T>(\n items: readonly T[],\n concurrency: number,\n action: (item: T, index: number) => Promise<unknown>\n): Promise<void> {\n await runConcurrently(items.length, concurrency, (index) => action(items[index] as T, index));\n}\n\n/**\n * Like `forEachConcurrently`, but resolves with the results of `mapper` in the order of `items`.\n */\nexport async function mapConcurrently<T, R>(\n items: readonly T[],\n concurrency: number,\n mapper: (item: T, index: number) => Promise<R>\n): Promise<R[]> {\n const results = Array.from<R>({ length: items.length });\n await runConcurrently(items.length, concurrency, async (index) => {\n results[index] = await mapper(items[index] as T, index);\n });\n return results;\n}\n\nasync function runConcurrently(\n length: number,\n concurrency: number,\n run: (index: number) => Promise<unknown>\n): Promise<void> {\n if (!Number.isInteger(concurrency) || concurrency < 1) {\n throw new RangeError(`concurrency must be a positive integer: ${concurrency}`);\n }\n let nextIndex = 0;\n let failure: { error: unknown } | undefined;\n // Workers beyond the item count would find nothing to take, so they are not created.\n await Promise.all(\n Array.from({ length: Math.min(concurrency, length) }, async () => {\n while (!failure && nextIndex < length) {\n try {\n await run(nextIndex++);\n } catch (error) {\n failure ??= { error };\n }\n }\n })\n );\n if (failure) throw failure.error;\n}\n"],"mappings":"AAMA,
|
|
1
|
+
{"version":3,"file":"concurrency.js","names":["forEachConcurrently","runConcurrently","items","concurrency","action","index","mapConcurrently","results","mapper","nextIndex","failure","length","run","error"],"sources":["../src/concurrency.ts"],"sourcesContent":["/**\n * Runs `action` for every item with at most `concurrency` actions in flight, starting the next item as soon as any\n * action settles, so one slow item does not hold back a whole batch. After an action rejects, no further item is\n * started, and once every started action has settled, the returned promise rejects with the first error.\n * The returned promise rejects with a `RangeError` when `concurrency` is not a positive integer.\n */\nexport async function forEachConcurrently<T>(\n items: readonly T[],\n concurrency: number,\n action: (item: T, index: number) => Promise<unknown>\n): Promise<void> {\n await runConcurrently(items.length, concurrency, (index) => action(items[index] as T, index));\n}\n\n/**\n * Like `forEachConcurrently`, but resolves with the results of `mapper` in the order of `items`.\n */\nexport async function mapConcurrently<T, R>(\n items: readonly T[],\n concurrency: number,\n mapper: (item: T, index: number) => Promise<R>\n): Promise<R[]> {\n const results = Array.from<R>({ length: items.length });\n await runConcurrently(items.length, concurrency, async (index) => {\n results[index] = await mapper(items[index] as T, index);\n });\n return results;\n}\n\nasync function runConcurrently(\n length: number,\n concurrency: number,\n run: (index: number) => Promise<unknown>\n): Promise<void> {\n if (!Number.isInteger(concurrency) || concurrency < 1) {\n throw new RangeError(`concurrency must be a positive integer: ${concurrency}`);\n }\n let nextIndex = 0;\n let failure: { error: unknown } | undefined;\n // Workers beyond the item count would find nothing to take, so they are not created.\n await Promise.all(\n Array.from({ length: Math.min(concurrency, length) }, async () => {\n while (!failure && nextIndex < length) {\n try {\n await run(nextIndex++);\n } catch (error) {\n failure ??= { error };\n }\n }\n })\n );\n if (failure) throw failure.error;\n}\n"],"mappings":"AAMA,eAAsBA,EACpB,EACA,EACA,EACe,CACf,MAAMC,EAAgBC,EAAM,OAAQC,EAAc,GAAUC,EAAOF,EAAMG,GAAaA,CAAK,CAAC,CAC9F,CAKA,eAAsBC,EACpB,EACA,EACA,EACc,CACd,IAAMC,EAAU,MAAM,KAAQ,CAAE,OAAQL,EAAM,MAAO,CAAC,EAItD,OAHA,MAAMD,EAAgBC,EAAM,OAAQC,EAAa,KAAO,IAAU,CAChE,EAAQE,GAAS,MAAMG,EAAON,EAAMG,GAAaA,CAAK,CACxD,CAAC,EACME,CACT,CAEA,eAAeN,EACb,EACA,EACA,EACe,CACf,GAAI,CAAC,OAAO,UAAUE,CAAW,GAAKA,EAAc,EAClD,MAAU,WAAW,2CAA2CA,GAAa,EAE/E,IAAIM,EAAY,EACZC,EAaJ,GAXA,MAAM,QAAQ,IACZ,MAAM,KAAK,CAAE,OAAQ,KAAK,IAAIP,EAAaQ,CAAM,CAAE,EAAG,SAAY,CAChE,KAAO,CAACD,GAAWD,EAAYE,GAC7B,GAAI,CACF,MAAMC,EAAI,GAAW,CACvB,OAASC,EAAO,CACd,IAAY,CAAE,OAAM,CACtB,CAEJ,CAAC,CACH,EACIH,EAAS,MAAMA,EAAQ,KAC7B"}
|
package/dist/crypto.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crypto.cjs","names":[],"sources":["../src/crypto.ts"],"sourcesContent":["/**\n * Answers the SHA-256 hex digest of `data` (a string is encoded as UTF-8) with WebCrypto, so it runs in browsers and\n * Cloudflare Workers; the result equals Node.js's `createHash('sha256').update(data).digest('hex')`.\n */\nexport async function sha256HexAsync(data: string | Uint8Array): Promise<string> {\n // Copying into a fresh `ArrayBuffer` also accepts a `SharedArrayBuffer`-backed view, which WebCrypto rejects.\n const bytes = typeof data === 'string' ? new TextEncoder().encode(data) : new Uint8Array(data);\n const digest = new Uint8Array(await crypto.subtle.digest('SHA-256', bytes));\n return Array.from(digest, (byte) => byte.toString(16).padStart(2, '0')).join('');\n}\n\n/**\n * Compares strings, e.g. a received API key against the expected one, with WebCrypto, so it runs in browsers and\n * Cloudflare Workers. Neither a length check nor an early exit reveals where they differ; only hashing each string\n * takes time proportional to its length, as in `shared-lib-node`'s `timingSafeEqualString`.\n */\nexport async function timingSafeEqualStringAsync(actual: string, expected: string): Promise<boolean> {\n // Hashing gives equal-length inputs, so the loop below needs no length check and never exits early.\n const [actualDigest, expectedDigest] = await Promise.all([digestCodeUnits(actual), digestCodeUnits(expected)]);\n let difference = 0;\n for (let i = 0; i < actualDigest.length; i++) {\n difference |= (actualDigest[i] ?? 0) ^ (expectedDigest[i] ?? 0);\n }\n return difference === 0;\n}\n\nasync function digestCodeUnits(text: string): Promise<Uint8Array> {\n // UTF-16 keeps every code unit, while UTF-8 would turn distinct lone surrogates into the same U+FFFD.\n const codeUnits = new Uint16Array(text.length);\n for (let i = 0; i < text.length; i++) {\n // oxlint-disable-next-line unicorn/prefer-code-point -- a code point would merge a surrogate pair into one value.\n codeUnits[i] = text.charCodeAt(i);\n }\n return new Uint8Array(await crypto.subtle.digest('SHA-256', codeUnits));\n}\n"],"mappings":"aAIA,
|
|
1
|
+
{"version":3,"file":"crypto.cjs","names":["sha256HexAsync","bytes","data","digest","byte","timingSafeEqualStringAsync","actualDigest","expectedDigest","digestCodeUnits","actual","expected","difference","i","codeUnits","text"],"sources":["../src/crypto.ts"],"sourcesContent":["/**\n * Answers the SHA-256 hex digest of `data` (a string is encoded as UTF-8) with WebCrypto, so it runs in browsers and\n * Cloudflare Workers; the result equals Node.js's `createHash('sha256').update(data).digest('hex')`.\n */\nexport async function sha256HexAsync(data: string | Uint8Array): Promise<string> {\n // Copying into a fresh `ArrayBuffer` also accepts a `SharedArrayBuffer`-backed view, which WebCrypto rejects.\n const bytes = typeof data === 'string' ? new TextEncoder().encode(data) : new Uint8Array(data);\n const digest = new Uint8Array(await crypto.subtle.digest('SHA-256', bytes));\n return Array.from(digest, (byte) => byte.toString(16).padStart(2, '0')).join('');\n}\n\n/**\n * Compares strings, e.g. a received API key against the expected one, with WebCrypto, so it runs in browsers and\n * Cloudflare Workers. Neither a length check nor an early exit reveals where they differ; only hashing each string\n * takes time proportional to its length, as in `shared-lib-node`'s `timingSafeEqualString`.\n */\nexport async function timingSafeEqualStringAsync(actual: string, expected: string): Promise<boolean> {\n // Hashing gives equal-length inputs, so the loop below needs no length check and never exits early.\n const [actualDigest, expectedDigest] = await Promise.all([digestCodeUnits(actual), digestCodeUnits(expected)]);\n let difference = 0;\n for (let i = 0; i < actualDigest.length; i++) {\n difference |= (actualDigest[i] ?? 0) ^ (expectedDigest[i] ?? 0);\n }\n return difference === 0;\n}\n\nasync function digestCodeUnits(text: string): Promise<Uint8Array> {\n // UTF-16 keeps every code unit, while UTF-8 would turn distinct lone surrogates into the same U+FFFD.\n const codeUnits = new Uint16Array(text.length);\n for (let i = 0; i < text.length; i++) {\n // oxlint-disable-next-line unicorn/prefer-code-point -- a code point would merge a surrogate pair into one value.\n codeUnits[i] = text.charCodeAt(i);\n }\n return new Uint8Array(await crypto.subtle.digest('SHA-256', codeUnits));\n}\n"],"mappings":"aAIA,eAAsBA,EAAe,EAA4C,CAE/E,IAAMC,EAAQ,OAAOC,GAAS,SAAW,IAAI,YAAY,CAAC,CAAC,OAAOA,CAAI,EAAI,IAAI,WAAWA,CAAI,EACvFC,EAAS,IAAI,WAAW,MAAM,OAAO,OAAO,OAAO,UAAWF,CAAK,CAAC,EAC1E,OAAO,MAAM,KAAKE,EAAS,GAASC,EAAK,SAAS,EAAE,CAAC,CAAC,SAAS,EAAG,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,CACjF,CAOA,eAAsBC,EAA2B,EAAgB,EAAoC,CAEnG,GAAM,CAACC,EAAcC,GAAkB,MAAM,QAAQ,IAAI,CAACC,EAAgBC,CAAM,EAAGD,EAAgBE,CAAQ,CAAC,CAAC,EACzGC,EAAa,EACjB,IAAK,IAAIC,EAAI,EAAGA,EAAIN,EAAa,OAAQ,IACvC,IAAeA,EAAaM,IAAM,IAAML,EAAeK,IAAM,GAE/D,OAAOD,IAAe,CACxB,CAEA,eAAeH,EAAgB,EAAmC,CAEhE,IAAMK,EAAY,IAAI,YAAYC,EAAK,MAAM,EAC7C,IAAK,IAAIF,EAAI,EAAGA,EAAIE,EAAK,OAAQ,IAE/B,EAAUF,GAAKE,EAAK,WAAWF,CAAC,EAElC,OAAO,IAAI,WAAW,MAAM,OAAO,OAAO,OAAO,UAAWC,CAAS,CAAC,CACxE"}
|
package/dist/crypto.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crypto.js","names":[],"sources":["../src/crypto.ts"],"sourcesContent":["/**\n * Answers the SHA-256 hex digest of `data` (a string is encoded as UTF-8) with WebCrypto, so it runs in browsers and\n * Cloudflare Workers; the result equals Node.js's `createHash('sha256').update(data).digest('hex')`.\n */\nexport async function sha256HexAsync(data: string | Uint8Array): Promise<string> {\n // Copying into a fresh `ArrayBuffer` also accepts a `SharedArrayBuffer`-backed view, which WebCrypto rejects.\n const bytes = typeof data === 'string' ? new TextEncoder().encode(data) : new Uint8Array(data);\n const digest = new Uint8Array(await crypto.subtle.digest('SHA-256', bytes));\n return Array.from(digest, (byte) => byte.toString(16).padStart(2, '0')).join('');\n}\n\n/**\n * Compares strings, e.g. a received API key against the expected one, with WebCrypto, so it runs in browsers and\n * Cloudflare Workers. Neither a length check nor an early exit reveals where they differ; only hashing each string\n * takes time proportional to its length, as in `shared-lib-node`'s `timingSafeEqualString`.\n */\nexport async function timingSafeEqualStringAsync(actual: string, expected: string): Promise<boolean> {\n // Hashing gives equal-length inputs, so the loop below needs no length check and never exits early.\n const [actualDigest, expectedDigest] = await Promise.all([digestCodeUnits(actual), digestCodeUnits(expected)]);\n let difference = 0;\n for (let i = 0; i < actualDigest.length; i++) {\n difference |= (actualDigest[i] ?? 0) ^ (expectedDigest[i] ?? 0);\n }\n return difference === 0;\n}\n\nasync function digestCodeUnits(text: string): Promise<Uint8Array> {\n // UTF-16 keeps every code unit, while UTF-8 would turn distinct lone surrogates into the same U+FFFD.\n const codeUnits = new Uint16Array(text.length);\n for (let i = 0; i < text.length; i++) {\n // oxlint-disable-next-line unicorn/prefer-code-point -- a code point would merge a surrogate pair into one value.\n codeUnits[i] = text.charCodeAt(i);\n }\n return new Uint8Array(await crypto.subtle.digest('SHA-256', codeUnits));\n}\n"],"mappings":"AAIA,
|
|
1
|
+
{"version":3,"file":"crypto.js","names":["sha256HexAsync","bytes","data","digest","byte","timingSafeEqualStringAsync","actualDigest","expectedDigest","digestCodeUnits","actual","expected","difference","i","codeUnits","text"],"sources":["../src/crypto.ts"],"sourcesContent":["/**\n * Answers the SHA-256 hex digest of `data` (a string is encoded as UTF-8) with WebCrypto, so it runs in browsers and\n * Cloudflare Workers; the result equals Node.js's `createHash('sha256').update(data).digest('hex')`.\n */\nexport async function sha256HexAsync(data: string | Uint8Array): Promise<string> {\n // Copying into a fresh `ArrayBuffer` also accepts a `SharedArrayBuffer`-backed view, which WebCrypto rejects.\n const bytes = typeof data === 'string' ? new TextEncoder().encode(data) : new Uint8Array(data);\n const digest = new Uint8Array(await crypto.subtle.digest('SHA-256', bytes));\n return Array.from(digest, (byte) => byte.toString(16).padStart(2, '0')).join('');\n}\n\n/**\n * Compares strings, e.g. a received API key against the expected one, with WebCrypto, so it runs in browsers and\n * Cloudflare Workers. Neither a length check nor an early exit reveals where they differ; only hashing each string\n * takes time proportional to its length, as in `shared-lib-node`'s `timingSafeEqualString`.\n */\nexport async function timingSafeEqualStringAsync(actual: string, expected: string): Promise<boolean> {\n // Hashing gives equal-length inputs, so the loop below needs no length check and never exits early.\n const [actualDigest, expectedDigest] = await Promise.all([digestCodeUnits(actual), digestCodeUnits(expected)]);\n let difference = 0;\n for (let i = 0; i < actualDigest.length; i++) {\n difference |= (actualDigest[i] ?? 0) ^ (expectedDigest[i] ?? 0);\n }\n return difference === 0;\n}\n\nasync function digestCodeUnits(text: string): Promise<Uint8Array> {\n // UTF-16 keeps every code unit, while UTF-8 would turn distinct lone surrogates into the same U+FFFD.\n const codeUnits = new Uint16Array(text.length);\n for (let i = 0; i < text.length; i++) {\n // oxlint-disable-next-line unicorn/prefer-code-point -- a code point would merge a surrogate pair into one value.\n codeUnits[i] = text.charCodeAt(i);\n }\n return new Uint8Array(await crypto.subtle.digest('SHA-256', codeUnits));\n}\n"],"mappings":"AAIA,eAAsBA,EAAe,EAA4C,CAE/E,IAAMC,EAAQ,OAAOC,GAAS,SAAW,IAAI,YAAY,CAAC,CAAC,OAAOA,CAAI,EAAI,IAAI,WAAWA,CAAI,EACvFC,EAAS,IAAI,WAAW,MAAM,OAAO,OAAO,OAAO,UAAWF,CAAK,CAAC,EAC1E,OAAO,MAAM,KAAKE,EAAS,GAASC,EAAK,SAAS,EAAE,CAAC,CAAC,SAAS,EAAG,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,CACjF,CAOA,eAAsBC,EAA2B,EAAgB,EAAoC,CAEnG,GAAM,CAACC,EAAcC,GAAkB,MAAM,QAAQ,IAAI,CAACC,EAAgBC,CAAM,EAAGD,EAAgBE,CAAQ,CAAC,CAAC,EACzGC,EAAa,EACjB,IAAK,IAAIC,EAAI,EAAGA,EAAIN,EAAa,OAAQ,IACvC,IAAeA,EAAaM,IAAM,IAAML,EAAeK,IAAM,GAE/D,OAAOD,IAAe,CACxB,CAEA,eAAeH,EAAgB,EAAmC,CAEhE,IAAMK,EAAY,IAAI,YAAYC,EAAK,MAAM,EAC7C,IAAK,IAAIF,EAAI,EAAGA,EAAIE,EAAK,OAAQ,IAE/B,EAAUF,GAAKE,EAAK,WAAWF,CAAC,EAElC,OAAO,IAAI,WAAW,MAAM,OAAO,OAAO,OAAO,UAAWC,CAAS,CAAC,CACxE"}
|
package/dist/error.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error.cjs","names":["sleep"],"sources":["../src/error.ts"],"sourcesContent":["import { sleep } from './sleep.js';\n\n/**\n * Convert an object to an error.\n * @param obj The object to convert.\n */\nexport function errorify(obj: unknown): Error {\n if (obj instanceof Error) return obj;\n if (typeof obj === 'string') return new Error(obj);\n // `JSON.stringify` answers `undefined` for `undefined`, a symbol and a function, and throws\n // on a bigint or a cycle; `new Error(undefined)` has an empty message, which would drop the\n // only diagnostic a caller has about such a thrown value. Neither representation may throw\n // out of here: this runs in a catch block, where it would replace the error it converts.\n return new Error(ignoreError(() => JSON.stringify(obj)) ?? ignoreError(() => String(obj)));\n}\n\n/**\n * Returns the message of a thrown `Error`; for any other thrown value, `fallback` if given, or else its string form.\n * `fallback` is also returned when reading the message throws. Never throws, so it is safe to call in a catch block.\n */\nexport function getErrorMessage(error: unknown, fallback?: string): string {\n // `String` throws for an object without a prototype, and even `instanceof`, a `message` getter, and\n // `Object.prototype.toString` throw for a proxy or object whose traps or getters throw.\n return (\n ignoreError(() => (error instanceof Error ? error.message : (fallback ?? String(error)))) ??\n fallback ??\n ignoreError(() => Object.prototype.toString.call(error)) ??\n 'Unknown error'\n );\n}\n\n/** Whether a thrown value carries the given `code`, such as a Node.js errno code (`ENOENT`, `EEXIST`, ...). */\nexport function hasErrorCode<C extends string>(error: unknown, code: C): error is { code: C } {\n return typeof error === 'object' && error !== null && 'code' in error && error.code === code;\n}\n\nexport function ignoreError<T>(fn: () => T): T | undefined {\n try {\n return fn();\n } catch {\n // do nothing\n }\n}\n\nexport function ignoreEnoent<T>(fn: () => T): T | undefined {\n try {\n return fn();\n } catch (error) {\n if (hasErrorCode(error, 'ENOENT')) return;\n throw error;\n }\n}\n\nexport async function ignoreErrorAsync<T>(fn: () => Promise<T>): Promise<T | undefined> {\n try {\n return await fn();\n } catch {\n // do nothing\n }\n}\n\nexport async function ignoreEnoentAsync<T>(fn: () => Promise<T>): Promise<T | undefined> {\n try {\n return await fn();\n } catch (error) {\n if (hasErrorCode(error, 'ENOENT')) return;\n throw error;\n }\n}\n\nexport interface RetryOptions {\n beforeRetry?: (error: unknown) => Promise<void>;\n handleError?: (error: unknown) => Promise<void>;\n /**\n * The maximum number of total attempts, including the initial attempt.\n * For example, `retryCount: 3` runs the function at most 3 times: 1 initial attempt and up to 2 retries.\n */\n retryCount?: number;\n retryLogger?: (message: string) => void;\n shouldRetry?: (error: unknown) => boolean;\n /**\n * Decides how long to wait before retrying after `error`, given the backoff `sleepMilliseconds` it would otherwise wait,\n * e.g. `(error, ms) => Math.max(ms, retryAtOf(error) - Date.now())` to honor a retry time sent by a server.\n * It does not affect the backoff that `updateSleepMilliseconds` advances.\n */\n getSleepMilliseconds?: (error: unknown, sleepMilliseconds: number) => number;\n sleepMilliseconds?: number;\n updateSleepMilliseconds?: (sleepMilliseconds: number) => number;\n}\n\n/**\n * Retry the given function.\n * @param func The function to retry.\n * @param beforeRetry The function to call immediately before retrying.\n * @param handleError The function to call when an error occurs.\n * @param retryCount The maximum number of total attempts, including the initial attempt.\n * @param retryLogger The function to log retrying.\n * @param sleepMilliseconds The number of milliseconds to sleep before retrying.\n * @param updateSleepMilliseconds The function to update sleep milliseconds after each retry.\n */\nexport async function withRetry<T>(\n func: (failedCount: number) => T | Promise<T>,\n {\n beforeRetry,\n handleError,\n retryCount = 3,\n retryLogger,\n shouldRetry,\n getSleepMilliseconds,\n sleepMilliseconds = 0,\n updateSleepMilliseconds,\n }: RetryOptions = {}\n): Promise<T> {\n let failedCount = 0;\n for (;;) {\n try {\n return await func(failedCount);\n } catch (error) {\n await handleError?.(error);\n failedCount++;\n if (failedCount >= retryCount) {\n throw error;\n }\n if (shouldRetry && !shouldRetry(error)) {\n throw error;\n }\n const currentSleepMilliseconds = getSleepMilliseconds?.(error, sleepMilliseconds) ?? sleepMilliseconds;\n if (currentSleepMilliseconds > 0) {\n await sleep(currentSleepMilliseconds);\n }\n if (updateSleepMilliseconds) {\n sleepMilliseconds = updateSleepMilliseconds(sleepMilliseconds);\n }\n retryLogger?.(`Retry due to: ${error}\n${error instanceof Error ? '---\\n' + (error.stack ?? '') : ''}`);\n await beforeRetry?.(error);\n }\n }\n}\n"],"mappings":"4CAMA,
|
|
1
|
+
{"version":3,"file":"error.cjs","names":["sleep","require_sleep","errorify","obj","ignoreError","getErrorMessage","error","fallback","hasErrorCode","code","fn","ignoreEnoent","ignoreErrorAsync","ignoreEnoentAsync","withRetry","failedCount","func","handleError","retryCount","shouldRetry","currentSleepMilliseconds","getSleepMilliseconds","sleepMilliseconds","updateSleepMilliseconds","retryLogger","beforeRetry"],"sources":["../src/error.ts"],"sourcesContent":["import { sleep } from './sleep.js';\n\n/**\n * Convert an object to an error.\n * @param obj The object to convert.\n */\nexport function errorify(obj: unknown): Error {\n if (obj instanceof Error) return obj;\n if (typeof obj === 'string') return new Error(obj);\n // `JSON.stringify` answers `undefined` for `undefined`, a symbol and a function, and throws\n // on a bigint or a cycle; `new Error(undefined)` has an empty message, which would drop the\n // only diagnostic a caller has about such a thrown value. Neither representation may throw\n // out of here: this runs in a catch block, where it would replace the error it converts.\n return new Error(ignoreError(() => JSON.stringify(obj)) ?? ignoreError(() => String(obj)));\n}\n\n/**\n * Returns the message of a thrown `Error`; for any other thrown value, `fallback` if given, or else its string form.\n * `fallback` is also returned when reading the message throws. Never throws, so it is safe to call in a catch block.\n */\nexport function getErrorMessage(error: unknown, fallback?: string): string {\n // `String` throws for an object without a prototype, and even `instanceof`, a `message` getter, and\n // `Object.prototype.toString` throw for a proxy or object whose traps or getters throw.\n return (\n ignoreError(() => (error instanceof Error ? error.message : (fallback ?? String(error)))) ??\n fallback ??\n ignoreError(() => Object.prototype.toString.call(error)) ??\n 'Unknown error'\n );\n}\n\n/** Whether a thrown value carries the given `code`, such as a Node.js errno code (`ENOENT`, `EEXIST`, ...). */\nexport function hasErrorCode<C extends string>(error: unknown, code: C): error is { code: C } {\n return typeof error === 'object' && error !== null && 'code' in error && error.code === code;\n}\n\nexport function ignoreError<T>(fn: () => T): T | undefined {\n try {\n return fn();\n } catch {\n // do nothing\n }\n}\n\nexport function ignoreEnoent<T>(fn: () => T): T | undefined {\n try {\n return fn();\n } catch (error) {\n if (hasErrorCode(error, 'ENOENT')) return;\n throw error;\n }\n}\n\nexport async function ignoreErrorAsync<T>(fn: () => Promise<T>): Promise<T | undefined> {\n try {\n return await fn();\n } catch {\n // do nothing\n }\n}\n\nexport async function ignoreEnoentAsync<T>(fn: () => Promise<T>): Promise<T | undefined> {\n try {\n return await fn();\n } catch (error) {\n if (hasErrorCode(error, 'ENOENT')) return;\n throw error;\n }\n}\n\nexport interface RetryOptions {\n beforeRetry?: (error: unknown) => Promise<void>;\n handleError?: (error: unknown) => Promise<void>;\n /**\n * The maximum number of total attempts, including the initial attempt.\n * For example, `retryCount: 3` runs the function at most 3 times: 1 initial attempt and up to 2 retries.\n */\n retryCount?: number;\n retryLogger?: (message: string) => void;\n shouldRetry?: (error: unknown) => boolean;\n /**\n * Decides how long to wait before retrying after `error`, given the backoff `sleepMilliseconds` it would otherwise wait,\n * e.g. `(error, ms) => Math.max(ms, retryAtOf(error) - Date.now())` to honor a retry time sent by a server.\n * It does not affect the backoff that `updateSleepMilliseconds` advances.\n */\n getSleepMilliseconds?: (error: unknown, sleepMilliseconds: number) => number;\n sleepMilliseconds?: number;\n updateSleepMilliseconds?: (sleepMilliseconds: number) => number;\n}\n\n/**\n * Retry the given function.\n * @param func The function to retry.\n * @param beforeRetry The function to call immediately before retrying.\n * @param handleError The function to call when an error occurs.\n * @param retryCount The maximum number of total attempts, including the initial attempt.\n * @param retryLogger The function to log retrying.\n * @param sleepMilliseconds The number of milliseconds to sleep before retrying.\n * @param updateSleepMilliseconds The function to update sleep milliseconds after each retry.\n */\nexport async function withRetry<T>(\n func: (failedCount: number) => T | Promise<T>,\n {\n beforeRetry,\n handleError,\n retryCount = 3,\n retryLogger,\n shouldRetry,\n getSleepMilliseconds,\n sleepMilliseconds = 0,\n updateSleepMilliseconds,\n }: RetryOptions = {}\n): Promise<T> {\n let failedCount = 0;\n for (;;) {\n try {\n return await func(failedCount);\n } catch (error) {\n await handleError?.(error);\n failedCount++;\n if (failedCount >= retryCount) {\n throw error;\n }\n if (shouldRetry && !shouldRetry(error)) {\n throw error;\n }\n const currentSleepMilliseconds = getSleepMilliseconds?.(error, sleepMilliseconds) ?? sleepMilliseconds;\n if (currentSleepMilliseconds > 0) {\n await sleep(currentSleepMilliseconds);\n }\n if (updateSleepMilliseconds) {\n sleepMilliseconds = updateSleepMilliseconds(sleepMilliseconds);\n }\n retryLogger?.(`Retry due to: ${error}\n${error instanceof Error ? '---\\n' + (error.stack ?? '') : ''}`);\n await beforeRetry?.(error);\n }\n }\n}\n"],"mappings":"4CAMA,SAAgBE,EAAS,EAAqB,CAO5C,OANIC,aAAe,MAAcA,EACO,MAApC,OAAOA,GAAQ,SAA2BA,EAK7BC,MAAkB,KAAK,UAAUD,CAAG,CAAC,GAAKC,MAAkB,OAAOD,CAAG,CAAC,CALvC,CAMnD,CAMA,SAAgBE,EAAgB,EAAgB,EAA2B,CAGzE,OACED,MAAmBE,aAAiB,MAAQA,EAAM,QAAWC,GAAY,OAAOD,CAAK,CAAG,GACxFC,GACAH,MAAkB,OAAO,UAAU,SAAS,KAAKE,CAAK,CAAC,GACvD,eAEJ,CAGA,SAAgBE,EAA+B,EAAgB,EAA+B,CAC5F,OAAO,OAAOF,GAAU,YAAYA,GAAkB,SAAUA,GAASA,EAAM,OAASG,CAC1F,CAEA,SAAgBL,EAAe,EAA4B,CACzD,GAAI,CACF,OAAOM,EAAG,CACZ,MAAQ,CAER,CACF,CAEA,SAAgBC,EAAgB,EAA4B,CAC1D,GAAI,CACF,OAAOD,EAAG,CACZ,OAASJ,EAAO,CACd,GAAIE,EAAaF,EAAO,QAAQ,EAAG,OACnC,MAAMA,CACR,CACF,CAEA,eAAsBM,EAAoB,EAA8C,CACtF,GAAI,CACF,OAAO,MAAMF,EAAG,CAClB,MAAQ,CAER,CACF,CAEA,eAAsBG,EAAqB,EAA8C,CACvF,GAAI,CACF,OAAO,MAAMH,EAAG,CAClB,OAASJ,EAAO,CACd,GAAIE,EAAaF,EAAO,QAAQ,EAAG,OACnC,MAAMA,CACR,CACF,CAgCA,eAAsBQ,EACpB,EACA,CACE,cACA,cACA,aAAa,EACb,cACA,cACA,uBACA,oBAAoB,EACpB,2BACgB,CAAC,EACP,CACZ,IAAIC,EAAc,EAClB,OACE,GAAI,CACF,OAAO,MAAMC,EAAKD,CAAW,CAC/B,OAAST,EAAO,CAMd,GALA,MAAMW,IAAcX,CAAK,EACzB,IACIS,GAAeG,GAGfC,GAAe,CAACA,EAAYb,CAAK,EACnC,MAAMA,EAER,IAAMc,EAA2BC,IAAuBf,EAAOgB,CAAiB,GAAKA,EACjFF,EAA2B,GAC7B,MAAMpB,EAAAA,MAAMoB,CAAwB,EAElCG,IACF,EAAoBA,EAAwBD,CAAiB,GAE/DE,IAAc,iBAAiBlB,EAAM;EACzCA,aAAiB,MAAQ;GAAWA,EAAM,OAAS,IAAM,IAAI,EACzD,MAAMmB,IAAcnB,CAAK,CAC3B,CAEJ"}
|
package/dist/error.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error.js","names":[],"sources":["../src/error.ts"],"sourcesContent":["import { sleep } from './sleep.js';\n\n/**\n * Convert an object to an error.\n * @param obj The object to convert.\n */\nexport function errorify(obj: unknown): Error {\n if (obj instanceof Error) return obj;\n if (typeof obj === 'string') return new Error(obj);\n // `JSON.stringify` answers `undefined` for `undefined`, a symbol and a function, and throws\n // on a bigint or a cycle; `new Error(undefined)` has an empty message, which would drop the\n // only diagnostic a caller has about such a thrown value. Neither representation may throw\n // out of here: this runs in a catch block, where it would replace the error it converts.\n return new Error(ignoreError(() => JSON.stringify(obj)) ?? ignoreError(() => String(obj)));\n}\n\n/**\n * Returns the message of a thrown `Error`; for any other thrown value, `fallback` if given, or else its string form.\n * `fallback` is also returned when reading the message throws. Never throws, so it is safe to call in a catch block.\n */\nexport function getErrorMessage(error: unknown, fallback?: string): string {\n // `String` throws for an object without a prototype, and even `instanceof`, a `message` getter, and\n // `Object.prototype.toString` throw for a proxy or object whose traps or getters throw.\n return (\n ignoreError(() => (error instanceof Error ? error.message : (fallback ?? String(error)))) ??\n fallback ??\n ignoreError(() => Object.prototype.toString.call(error)) ??\n 'Unknown error'\n );\n}\n\n/** Whether a thrown value carries the given `code`, such as a Node.js errno code (`ENOENT`, `EEXIST`, ...). */\nexport function hasErrorCode<C extends string>(error: unknown, code: C): error is { code: C } {\n return typeof error === 'object' && error !== null && 'code' in error && error.code === code;\n}\n\nexport function ignoreError<T>(fn: () => T): T | undefined {\n try {\n return fn();\n } catch {\n // do nothing\n }\n}\n\nexport function ignoreEnoent<T>(fn: () => T): T | undefined {\n try {\n return fn();\n } catch (error) {\n if (hasErrorCode(error, 'ENOENT')) return;\n throw error;\n }\n}\n\nexport async function ignoreErrorAsync<T>(fn: () => Promise<T>): Promise<T | undefined> {\n try {\n return await fn();\n } catch {\n // do nothing\n }\n}\n\nexport async function ignoreEnoentAsync<T>(fn: () => Promise<T>): Promise<T | undefined> {\n try {\n return await fn();\n } catch (error) {\n if (hasErrorCode(error, 'ENOENT')) return;\n throw error;\n }\n}\n\nexport interface RetryOptions {\n beforeRetry?: (error: unknown) => Promise<void>;\n handleError?: (error: unknown) => Promise<void>;\n /**\n * The maximum number of total attempts, including the initial attempt.\n * For example, `retryCount: 3` runs the function at most 3 times: 1 initial attempt and up to 2 retries.\n */\n retryCount?: number;\n retryLogger?: (message: string) => void;\n shouldRetry?: (error: unknown) => boolean;\n /**\n * Decides how long to wait before retrying after `error`, given the backoff `sleepMilliseconds` it would otherwise wait,\n * e.g. `(error, ms) => Math.max(ms, retryAtOf(error) - Date.now())` to honor a retry time sent by a server.\n * It does not affect the backoff that `updateSleepMilliseconds` advances.\n */\n getSleepMilliseconds?: (error: unknown, sleepMilliseconds: number) => number;\n sleepMilliseconds?: number;\n updateSleepMilliseconds?: (sleepMilliseconds: number) => number;\n}\n\n/**\n * Retry the given function.\n * @param func The function to retry.\n * @param beforeRetry The function to call immediately before retrying.\n * @param handleError The function to call when an error occurs.\n * @param retryCount The maximum number of total attempts, including the initial attempt.\n * @param retryLogger The function to log retrying.\n * @param sleepMilliseconds The number of milliseconds to sleep before retrying.\n * @param updateSleepMilliseconds The function to update sleep milliseconds after each retry.\n */\nexport async function withRetry<T>(\n func: (failedCount: number) => T | Promise<T>,\n {\n beforeRetry,\n handleError,\n retryCount = 3,\n retryLogger,\n shouldRetry,\n getSleepMilliseconds,\n sleepMilliseconds = 0,\n updateSleepMilliseconds,\n }: RetryOptions = {}\n): Promise<T> {\n let failedCount = 0;\n for (;;) {\n try {\n return await func(failedCount);\n } catch (error) {\n await handleError?.(error);\n failedCount++;\n if (failedCount >= retryCount) {\n throw error;\n }\n if (shouldRetry && !shouldRetry(error)) {\n throw error;\n }\n const currentSleepMilliseconds = getSleepMilliseconds?.(error, sleepMilliseconds) ?? sleepMilliseconds;\n if (currentSleepMilliseconds > 0) {\n await sleep(currentSleepMilliseconds);\n }\n if (updateSleepMilliseconds) {\n sleepMilliseconds = updateSleepMilliseconds(sleepMilliseconds);\n }\n retryLogger?.(`Retry due to: ${error}\n${error instanceof Error ? '---\\n' + (error.stack ?? '') : ''}`);\n await beforeRetry?.(error);\n }\n }\n}\n"],"mappings":"mCAMA,
|
|
1
|
+
{"version":3,"file":"error.js","names":["errorify","obj","ignoreError","getErrorMessage","error","fallback","hasErrorCode","code","fn","ignoreEnoent","ignoreErrorAsync","ignoreEnoentAsync","withRetry","failedCount","func","handleError","retryCount","shouldRetry","currentSleepMilliseconds","getSleepMilliseconds","sleepMilliseconds","sleep","updateSleepMilliseconds","retryLogger","beforeRetry"],"sources":["../src/error.ts"],"sourcesContent":["import { sleep } from './sleep.js';\n\n/**\n * Convert an object to an error.\n * @param obj The object to convert.\n */\nexport function errorify(obj: unknown): Error {\n if (obj instanceof Error) return obj;\n if (typeof obj === 'string') return new Error(obj);\n // `JSON.stringify` answers `undefined` for `undefined`, a symbol and a function, and throws\n // on a bigint or a cycle; `new Error(undefined)` has an empty message, which would drop the\n // only diagnostic a caller has about such a thrown value. Neither representation may throw\n // out of here: this runs in a catch block, where it would replace the error it converts.\n return new Error(ignoreError(() => JSON.stringify(obj)) ?? ignoreError(() => String(obj)));\n}\n\n/**\n * Returns the message of a thrown `Error`; for any other thrown value, `fallback` if given, or else its string form.\n * `fallback` is also returned when reading the message throws. Never throws, so it is safe to call in a catch block.\n */\nexport function getErrorMessage(error: unknown, fallback?: string): string {\n // `String` throws for an object without a prototype, and even `instanceof`, a `message` getter, and\n // `Object.prototype.toString` throw for a proxy or object whose traps or getters throw.\n return (\n ignoreError(() => (error instanceof Error ? error.message : (fallback ?? String(error)))) ??\n fallback ??\n ignoreError(() => Object.prototype.toString.call(error)) ??\n 'Unknown error'\n );\n}\n\n/** Whether a thrown value carries the given `code`, such as a Node.js errno code (`ENOENT`, `EEXIST`, ...). */\nexport function hasErrorCode<C extends string>(error: unknown, code: C): error is { code: C } {\n return typeof error === 'object' && error !== null && 'code' in error && error.code === code;\n}\n\nexport function ignoreError<T>(fn: () => T): T | undefined {\n try {\n return fn();\n } catch {\n // do nothing\n }\n}\n\nexport function ignoreEnoent<T>(fn: () => T): T | undefined {\n try {\n return fn();\n } catch (error) {\n if (hasErrorCode(error, 'ENOENT')) return;\n throw error;\n }\n}\n\nexport async function ignoreErrorAsync<T>(fn: () => Promise<T>): Promise<T | undefined> {\n try {\n return await fn();\n } catch {\n // do nothing\n }\n}\n\nexport async function ignoreEnoentAsync<T>(fn: () => Promise<T>): Promise<T | undefined> {\n try {\n return await fn();\n } catch (error) {\n if (hasErrorCode(error, 'ENOENT')) return;\n throw error;\n }\n}\n\nexport interface RetryOptions {\n beforeRetry?: (error: unknown) => Promise<void>;\n handleError?: (error: unknown) => Promise<void>;\n /**\n * The maximum number of total attempts, including the initial attempt.\n * For example, `retryCount: 3` runs the function at most 3 times: 1 initial attempt and up to 2 retries.\n */\n retryCount?: number;\n retryLogger?: (message: string) => void;\n shouldRetry?: (error: unknown) => boolean;\n /**\n * Decides how long to wait before retrying after `error`, given the backoff `sleepMilliseconds` it would otherwise wait,\n * e.g. `(error, ms) => Math.max(ms, retryAtOf(error) - Date.now())` to honor a retry time sent by a server.\n * It does not affect the backoff that `updateSleepMilliseconds` advances.\n */\n getSleepMilliseconds?: (error: unknown, sleepMilliseconds: number) => number;\n sleepMilliseconds?: number;\n updateSleepMilliseconds?: (sleepMilliseconds: number) => number;\n}\n\n/**\n * Retry the given function.\n * @param func The function to retry.\n * @param beforeRetry The function to call immediately before retrying.\n * @param handleError The function to call when an error occurs.\n * @param retryCount The maximum number of total attempts, including the initial attempt.\n * @param retryLogger The function to log retrying.\n * @param sleepMilliseconds The number of milliseconds to sleep before retrying.\n * @param updateSleepMilliseconds The function to update sleep milliseconds after each retry.\n */\nexport async function withRetry<T>(\n func: (failedCount: number) => T | Promise<T>,\n {\n beforeRetry,\n handleError,\n retryCount = 3,\n retryLogger,\n shouldRetry,\n getSleepMilliseconds,\n sleepMilliseconds = 0,\n updateSleepMilliseconds,\n }: RetryOptions = {}\n): Promise<T> {\n let failedCount = 0;\n for (;;) {\n try {\n return await func(failedCount);\n } catch (error) {\n await handleError?.(error);\n failedCount++;\n if (failedCount >= retryCount) {\n throw error;\n }\n if (shouldRetry && !shouldRetry(error)) {\n throw error;\n }\n const currentSleepMilliseconds = getSleepMilliseconds?.(error, sleepMilliseconds) ?? sleepMilliseconds;\n if (currentSleepMilliseconds > 0) {\n await sleep(currentSleepMilliseconds);\n }\n if (updateSleepMilliseconds) {\n sleepMilliseconds = updateSleepMilliseconds(sleepMilliseconds);\n }\n retryLogger?.(`Retry due to: ${error}\n${error instanceof Error ? '---\\n' + (error.stack ?? '') : ''}`);\n await beforeRetry?.(error);\n }\n }\n}\n"],"mappings":"mCAMA,SAAgBA,EAAS,EAAqB,CAO5C,OANIC,aAAe,MAAcA,EACO,MAApC,OAAOA,GAAQ,SAA2BA,EAK7BC,MAAkB,KAAK,UAAUD,CAAG,CAAC,GAAKC,MAAkB,OAAOD,CAAG,CAAC,CALvC,CAMnD,CAMA,SAAgBE,EAAgB,EAAgB,EAA2B,CAGzE,OACED,MAAmBE,aAAiB,MAAQA,EAAM,QAAWC,GAAY,OAAOD,CAAK,CAAG,GACxFC,GACAH,MAAkB,OAAO,UAAU,SAAS,KAAKE,CAAK,CAAC,GACvD,eAEJ,CAGA,SAAgBE,EAA+B,EAAgB,EAA+B,CAC5F,OAAO,OAAOF,GAAU,YAAYA,GAAkB,SAAUA,GAASA,EAAM,OAASG,CAC1F,CAEA,SAAgBL,EAAe,EAA4B,CACzD,GAAI,CACF,OAAOM,EAAG,CACZ,MAAQ,CAER,CACF,CAEA,SAAgBC,EAAgB,EAA4B,CAC1D,GAAI,CACF,OAAOD,EAAG,CACZ,OAASJ,EAAO,CACd,GAAIE,EAAaF,EAAO,QAAQ,EAAG,OACnC,MAAMA,CACR,CACF,CAEA,eAAsBM,EAAoB,EAA8C,CACtF,GAAI,CACF,OAAO,MAAMF,EAAG,CAClB,MAAQ,CAER,CACF,CAEA,eAAsBG,EAAqB,EAA8C,CACvF,GAAI,CACF,OAAO,MAAMH,EAAG,CAClB,OAASJ,EAAO,CACd,GAAIE,EAAaF,EAAO,QAAQ,EAAG,OACnC,MAAMA,CACR,CACF,CAgCA,eAAsBQ,EACpB,EACA,CACE,cACA,cACA,aAAa,EACb,cACA,cACA,uBACA,oBAAoB,EACpB,2BACgB,CAAC,EACP,CACZ,IAAIC,EAAc,EAClB,OACE,GAAI,CACF,OAAO,MAAMC,EAAKD,CAAW,CAC/B,OAAST,EAAO,CAMd,GALA,MAAMW,IAAcX,CAAK,EACzB,IACIS,GAAeG,GAGfC,GAAe,CAACA,EAAYb,CAAK,EACnC,MAAMA,EAER,IAAMc,EAA2BC,IAAuBf,EAAOgB,CAAiB,GAAKA,EACjFF,EAA2B,GAC7B,MAAMG,EAAMH,CAAwB,EAElCI,IACF,EAAoBA,EAAwBF,CAAiB,GAE/DG,IAAc,iBAAiBnB,EAAM;EACzCA,aAAiB,MAAQ;GAAWA,EAAM,OAAS,IAAM,IAAI,EACzD,MAAMoB,IAAcpB,CAAK,CAC3B,CAEJ"}
|
package/dist/filePath.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filePath.cjs","names":[],"sources":["../src/filePath.ts"],"sourcesContent":["/**\n * Returns the lower-cased extension of the last path segment without the dot, or an empty string when the segment\n * has none. As with `path.extname`, a leading dot (e.g. `.gitignore`) does not start an extension.\n */\nexport function getFileExtension(filePath: string): string {\n const dotIndex = filePath.lastIndexOf('.');\n return dotIndex > filePath.lastIndexOf('/') + 1 ? filePath.slice(dotIndex + 1).toLowerCase() : '';\n}\n"],"mappings":"aAIA,
|
|
1
|
+
{"version":3,"file":"filePath.cjs","names":["getFileExtension","dotIndex","filePath"],"sources":["../src/filePath.ts"],"sourcesContent":["/**\n * Returns the lower-cased extension of the last path segment without the dot, or an empty string when the segment\n * has none. As with `path.extname`, a leading dot (e.g. `.gitignore`) does not start an extension.\n */\nexport function getFileExtension(filePath: string): string {\n const dotIndex = filePath.lastIndexOf('.');\n return dotIndex > filePath.lastIndexOf('/') + 1 ? filePath.slice(dotIndex + 1).toLowerCase() : '';\n}\n"],"mappings":"aAIA,SAAgBA,EAAiB,EAA0B,CACzD,IAAMC,EAAWC,EAAS,YAAY,GAAG,EACzC,OAAOD,EAAWC,EAAS,YAAY,GAAG,EAAI,EAAIA,EAAS,MAAMD,EAAW,CAAC,CAAC,CAAC,YAAY,EAAI,EACjG"}
|
package/dist/filePath.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filePath.js","names":[],"sources":["../src/filePath.ts"],"sourcesContent":["/**\n * Returns the lower-cased extension of the last path segment without the dot, or an empty string when the segment\n * has none. As with `path.extname`, a leading dot (e.g. `.gitignore`) does not start an extension.\n */\nexport function getFileExtension(filePath: string): string {\n const dotIndex = filePath.lastIndexOf('.');\n return dotIndex > filePath.lastIndexOf('/') + 1 ? filePath.slice(dotIndex + 1).toLowerCase() : '';\n}\n"],"mappings":"AAIA,
|
|
1
|
+
{"version":3,"file":"filePath.js","names":["getFileExtension","dotIndex","filePath"],"sources":["../src/filePath.ts"],"sourcesContent":["/**\n * Returns the lower-cased extension of the last path segment without the dot, or an empty string when the segment\n * has none. As with `path.extname`, a leading dot (e.g. `.gitignore`) does not start an extension.\n */\nexport function getFileExtension(filePath: string): string {\n const dotIndex = filePath.lastIndexOf('.');\n return dotIndex > filePath.lastIndexOf('/') + 1 ? filePath.slice(dotIndex + 1).toLowerCase() : '';\n}\n"],"mappings":"AAIA,SAAgBA,EAAiB,EAA0B,CACzD,IAAMC,EAAWC,EAAS,YAAY,GAAG,EACzC,OAAOD,EAAWC,EAAS,YAAY,GAAG,EAAI,EAAIA,EAAS,MAAMD,EAAW,CAAC,CAAC,CAAC,YAAY,EAAI,EACjG"}
|
package/dist/foreignCjk.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"foreignCjk.cjs","names":[],"sources":["../src/foreignCjk.ts"],"sourcesContent":["export type ForeignCjkLanguage = 'chinese' | 'korean';\n\nconst HANGUL_REGEX = /\\p{Script=Hangul}/u;\nconst HAN_REGEX = /\\p{Script=Han}/u;\n// JIS X 0208 cannot encode these Jōyō kanji, which were added in 2010.\nconst JOYO_KANJI_OUTSIDE_JIS_X_0208 = '𠮟塡剝頰';\n// Kanji for Japanese names (e.g., 髙, 﨑, 濵, 栁) that Chinese prose rarely contains: the NFKC forms of the\n// Windows-31J NEC-selected IBM and IBM extension kanji that neither JIS X 0208, Big5, nor GB2312 encodes, plus 𠮷 from\n// JIS X 0213. NFKC forms are listed because the lookup runs after NFKC normalization (e.g., U+FA20 becomes U+8612).\n// Some also appear in Chinese names (e.g., 喆), but prose that mixes Chinese into Japanese hardly contains them.\n// Extension kanji that Big5 or GB2312 encodes (e.g., 德, 瀨) stay Chinese because Chinese prose uses them.\nconst JAPANESE_NAME_KANJI_OUTSIDE_JIS_X_0208 =\n '仼伃伹侊俿偂僘僴兊兤冝凬刕劜劯匇匤厓叝咊咜喆坙坥垬埈墲夋奛奝奣尞岺峵嵓嵭嶹巐弡弴彅惞愑愰憘戓抦敎昞昻晳暿曺曻朎杦栁桒棏樰橳櫢櫤涖淸澵濵焏犱犾猤玽甁硺礰竧箞絈緖罇荢葈蓜蘒蠇裵褜訷贒郞鄕釞釥鈼鉙鉷鍈鏆隝隯霳霻靍靏靑靕髙魲鮏鮱鮻鰀﨎﨏﨑﨓﨔﨟﨡﨣﨤﨧﨨﨩𠮷';\n// Characters JIS X 0208 can encode but modern Japanese prose does not use:\n// - old forms (kyūjitai) of Jōyō kanji, from the `kyujitai` npm package and Unihan's kJapaneseOldVariant,\n// except those still common in Japanese words or names (e.g., 應 in 慶應, 澤, 邊, 龍, 藝 in 東京藝術大学, 糺す)\n// - Chinese function words that Japanese writes in kana (e.g., 們, 這, 麼, 很, 怎, 跟, 嘛) and stray simplified forms (e.g., 个)\n// - traditional Chinese characters that neither table covers but Japanese never uses: 碼, 舉, 鏈, 雖\n// Other Chinese-only content characters (e.g., 媽, 豬) are not listed because separating them from rare kanji\n// that Japanese prose does use (e.g., 乖離, 瑕疵, 齟齬, 俯瞰) needs Japanese usage frequencies.\nconst CHINESE_CHARACTERS_IN_JIS_X_0208 = new Set(\n '个乘亂亞从佛來們假傳僞價儉兒兩册刄剩劍劑勞勳勵勸區卷參听呀咯哇哦唔單嘛嚴囑囘圈國圍圓圖團墮壓壘壞壤壯壹壻壽奧奬姙孃學寢實寫寶將專對屆屬峽巖帶广廢廣廳彈很徑從怎恆惡惱愼慘懷戀戰戲拂拔拜挾插搖搜擇擔據擧擴攜攝收效敍敕數斷晝曉會條棧榮樂樓樞樣檢權歐歡歸殘殼毆氣沒淨淺滯滿潛澁濕濟灣燈燒營爐爭爲犧狹獨獵獸獻瓣畫當疊癡發盜盡碎碼祕禪禮稱稻穗穩竊竝粹絲經縣縱總繩繪繼續纖缺罐聲聽肅腦膽臟臺與舉舊舍舖艷莊莖萬藏藥處號螢蟲蠶蠻衞裝襃覺覽觀觸謠證譯譽讀變讓讚豐豫貳賣贊跟踐輕轉辨辭辯這遙遞遲鄰醉醫釀釋錢鎭鏈鐵鑄鑛關陷隨險隱隸雖雙雜霸靈靜顏顯飜飮餘餠騷驅驗驛髓體髮鬪鷄鹽麥麼黏默點黨齊齒齡龜'\n);\n\nlet japaneseKanji: Set<string> | undefined;\n\n/**\n * Detects Chinese or Korean characters mixed into Japanese text, such as LLM-generated Japanese prose.\n * See {@link findForeignCjkCharactersInJapanese} for which characters count and how `allowedText` works.\n * Returns the detected languages, or an empty array if none are found.\n */\nexport function detectForeignCjkInJapanese(text: string, allowedText?: string): ForeignCjkLanguage[] {\n const characters = findForeignCjkCharactersInJapanese(text, allowedText);\n const languages: ForeignCjkLanguage[] = [];\n if (characters.some((char) => !HANGUL_REGEX.test(char))) languages.push('chinese');\n if (characters.some((char) => HANGUL_REGEX.test(char))) languages.push('korean');\n return languages;\n}\n\n/**\n * Returns the distinct Chinese or Korean characters mixed into Japanese text, NFKC-normalized, in order of appearance.\n * A kanji is treated as Chinese when it is outside JIS X 0208 (except the four Jōyō kanji 𠮟塡剝頰 and Japanese name\n * kanji that neither Big5 nor GB2312 encodes, e.g., 髙, 﨑, 𠮷) or is one of the listed old forms, Chinese function\n * words, or other Chinese-only characters that modern Japanese prose does not use (e.g., 對, 國, 這, 們, 很, 碼).\n * Old forms in names that Chinese prose also uses (e.g., 國, 與, 德) are therefore also reported, while Chinese\n * containing none of these characters (e.g., 最后返回答案, 豬肉) is not.\n * Characters that also occur in `allowedText` are not reported, so passing the input that an LLM may quote\n * (e.g., the prompt, a question, or an error message) keeps quoted names and code from being reported.\n */\nexport function findForeignCjkCharactersInJapanese(text: string, allowedText = ''): string[] {\n const allowedCharacters = new Set(allowedText.normalize('NFKC'));\n const kanjiSet = (japaneseKanji ??= buildJapaneseKanjiSet());\n const characters = new Set<string>();\n for (const char of text.normalize('NFKC')) {\n if (allowedCharacters.has(char)) continue;\n if (\n HANGUL_REGEX.test(char) ||\n (HAN_REGEX.test(char) && (!kanjiSet.has(char) || CHINESE_CHARACTERS_IN_JIS_X_0208.has(char)))\n ) {\n characters.add(char);\n }\n }\n return [...characters];\n}\n\nfunction buildJapaneseKanjiSet(): Set<string> {\n // The WHATWG `shift_jis` decoder is Windows-31J, so skip its vendor-extension lead bytes (0xED-0xFC) to keep JIS X 0208 only.\n const decoder = new TextDecoder('shift_jis');\n const kanjiSet = new Set(JOYO_KANJI_OUTSIDE_JIS_X_0208 + JAPANESE_NAME_KANJI_OUTSIDE_JIS_X_0208);\n for (let lead = 0x81; lead <= 0xEA; lead++) {\n if (lead >= 0xA0 && lead <= 0xDF) continue;\n for (let trail = 0x40; trail <= 0xFC; trail++) {\n kanjiSet.add(decoder.decode(new Uint8Array([lead, trail])));\n }\n }\n return kanjiSet;\n}\n"],"mappings":"aAEA,
|
|
1
|
+
{"version":3,"file":"foreignCjk.cjs","names":["HANGUL_REGEX","HAN_REGEX","CHINESE_CHARACTERS_IN_JIS_X_0208","japaneseKanji","detectForeignCjkInJapanese","characters","findForeignCjkCharactersInJapanese","text","allowedText","languages","char","allowedCharacters","kanjiSet","buildJapaneseKanjiSet","decoder","lead","trail"],"sources":["../src/foreignCjk.ts"],"sourcesContent":["export type ForeignCjkLanguage = 'chinese' | 'korean';\n\nconst HANGUL_REGEX = /\\p{Script=Hangul}/u;\nconst HAN_REGEX = /\\p{Script=Han}/u;\n// JIS X 0208 cannot encode these Jōyō kanji, which were added in 2010.\nconst JOYO_KANJI_OUTSIDE_JIS_X_0208 = '𠮟塡剝頰';\n// Kanji for Japanese names (e.g., 髙, 﨑, 濵, 栁) that Chinese prose rarely contains: the NFKC forms of the\n// Windows-31J NEC-selected IBM and IBM extension kanji that neither JIS X 0208, Big5, nor GB2312 encodes, plus 𠮷 from\n// JIS X 0213. NFKC forms are listed because the lookup runs after NFKC normalization (e.g., U+FA20 becomes U+8612).\n// Some also appear in Chinese names (e.g., 喆), but prose that mixes Chinese into Japanese hardly contains them.\n// Extension kanji that Big5 or GB2312 encodes (e.g., 德, 瀨) stay Chinese because Chinese prose uses them.\nconst JAPANESE_NAME_KANJI_OUTSIDE_JIS_X_0208 =\n '仼伃伹侊俿偂僘僴兊兤冝凬刕劜劯匇匤厓叝咊咜喆坙坥垬埈墲夋奛奝奣尞岺峵嵓嵭嶹巐弡弴彅惞愑愰憘戓抦敎昞昻晳暿曺曻朎杦栁桒棏樰橳櫢櫤涖淸澵濵焏犱犾猤玽甁硺礰竧箞絈緖罇荢葈蓜蘒蠇裵褜訷贒郞鄕釞釥鈼鉙鉷鍈鏆隝隯霳霻靍靏靑靕髙魲鮏鮱鮻鰀﨎﨏﨑﨓﨔﨟﨡﨣﨤﨧﨨﨩𠮷';\n// Characters JIS X 0208 can encode but modern Japanese prose does not use:\n// - old forms (kyūjitai) of Jōyō kanji, from the `kyujitai` npm package and Unihan's kJapaneseOldVariant,\n// except those still common in Japanese words or names (e.g., 應 in 慶應, 澤, 邊, 龍, 藝 in 東京藝術大学, 糺す)\n// - Chinese function words that Japanese writes in kana (e.g., 們, 這, 麼, 很, 怎, 跟, 嘛) and stray simplified forms (e.g., 个)\n// - traditional Chinese characters that neither table covers but Japanese never uses: 碼, 舉, 鏈, 雖\n// Other Chinese-only content characters (e.g., 媽, 豬) are not listed because separating them from rare kanji\n// that Japanese prose does use (e.g., 乖離, 瑕疵, 齟齬, 俯瞰) needs Japanese usage frequencies.\nconst CHINESE_CHARACTERS_IN_JIS_X_0208 = new Set(\n '个乘亂亞从佛來們假傳僞價儉兒兩册刄剩劍劑勞勳勵勸區卷參听呀咯哇哦唔單嘛嚴囑囘圈國圍圓圖團墮壓壘壞壤壯壹壻壽奧奬姙孃學寢實寫寶將專對屆屬峽巖帶广廢廣廳彈很徑從怎恆惡惱愼慘懷戀戰戲拂拔拜挾插搖搜擇擔據擧擴攜攝收效敍敕數斷晝曉會條棧榮樂樓樞樣檢權歐歡歸殘殼毆氣沒淨淺滯滿潛澁濕濟灣燈燒營爐爭爲犧狹獨獵獸獻瓣畫當疊癡發盜盡碎碼祕禪禮稱稻穗穩竊竝粹絲經縣縱總繩繪繼續纖缺罐聲聽肅腦膽臟臺與舉舊舍舖艷莊莖萬藏藥處號螢蟲蠶蠻衞裝襃覺覽觀觸謠證譯譽讀變讓讚豐豫貳賣贊跟踐輕轉辨辭辯這遙遞遲鄰醉醫釀釋錢鎭鏈鐵鑄鑛關陷隨險隱隸雖雙雜霸靈靜顏顯飜飮餘餠騷驅驗驛髓體髮鬪鷄鹽麥麼黏默點黨齊齒齡龜'\n);\n\nlet japaneseKanji: Set<string> | undefined;\n\n/**\n * Detects Chinese or Korean characters mixed into Japanese text, such as LLM-generated Japanese prose.\n * See {@link findForeignCjkCharactersInJapanese} for which characters count and how `allowedText` works.\n * Returns the detected languages, or an empty array if none are found.\n */\nexport function detectForeignCjkInJapanese(text: string, allowedText?: string): ForeignCjkLanguage[] {\n const characters = findForeignCjkCharactersInJapanese(text, allowedText);\n const languages: ForeignCjkLanguage[] = [];\n if (characters.some((char) => !HANGUL_REGEX.test(char))) languages.push('chinese');\n if (characters.some((char) => HANGUL_REGEX.test(char))) languages.push('korean');\n return languages;\n}\n\n/**\n * Returns the distinct Chinese or Korean characters mixed into Japanese text, NFKC-normalized, in order of appearance.\n * A kanji is treated as Chinese when it is outside JIS X 0208 (except the four Jōyō kanji 𠮟塡剝頰 and Japanese name\n * kanji that neither Big5 nor GB2312 encodes, e.g., 髙, 﨑, 𠮷) or is one of the listed old forms, Chinese function\n * words, or other Chinese-only characters that modern Japanese prose does not use (e.g., 對, 國, 這, 們, 很, 碼).\n * Old forms in names that Chinese prose also uses (e.g., 國, 與, 德) are therefore also reported, while Chinese\n * containing none of these characters (e.g., 最后返回答案, 豬肉) is not.\n * Characters that also occur in `allowedText` are not reported, so passing the input that an LLM may quote\n * (e.g., the prompt, a question, or an error message) keeps quoted names and code from being reported.\n */\nexport function findForeignCjkCharactersInJapanese(text: string, allowedText = ''): string[] {\n const allowedCharacters = new Set(allowedText.normalize('NFKC'));\n const kanjiSet = (japaneseKanji ??= buildJapaneseKanjiSet());\n const characters = new Set<string>();\n for (const char of text.normalize('NFKC')) {\n if (allowedCharacters.has(char)) continue;\n if (\n HANGUL_REGEX.test(char) ||\n (HAN_REGEX.test(char) && (!kanjiSet.has(char) || CHINESE_CHARACTERS_IN_JIS_X_0208.has(char)))\n ) {\n characters.add(char);\n }\n }\n return [...characters];\n}\n\nfunction buildJapaneseKanjiSet(): Set<string> {\n // The WHATWG `shift_jis` decoder is Windows-31J, so skip its vendor-extension lead bytes (0xED-0xFC) to keep JIS X 0208 only.\n const decoder = new TextDecoder('shift_jis');\n const kanjiSet = new Set(JOYO_KANJI_OUTSIDE_JIS_X_0208 + JAPANESE_NAME_KANJI_OUTSIDE_JIS_X_0208);\n for (let lead = 0x81; lead <= 0xEA; lead++) {\n if (lead >= 0xA0 && lead <= 0xDF) continue;\n for (let trail = 0x40; trail <= 0xFC; trail++) {\n kanjiSet.add(decoder.decode(new Uint8Array([lead, trail])));\n }\n }\n return kanjiSet;\n}\n"],"mappings":"aAEA,MAAMA,EAAe,qBACfC,EAAY,kBAiBZC,kBAAmC,IAAI,IAC3C,+RACF,EAEA,IAAIC,EAOJ,SAAgBC,EAA2B,EAAc,EAA4C,CACnG,IAAMC,EAAaC,EAAmCC,EAAMC,CAAW,EACjEC,EAAkC,CAAC,EAGzC,OAFIJ,EAAW,KAAM,GAAS,CAACL,EAAa,KAAKU,CAAI,CAAC,GAAGD,EAAU,KAAK,SAAS,EAC7EJ,EAAW,KAAM,GAASL,EAAa,KAAKU,CAAI,CAAC,GAAGD,EAAU,KAAK,QAAQ,EACxEA,CACT,CAYA,SAAgBH,EAAmC,EAAc,EAAc,GAAc,CAC3F,IAAMK,EAAoB,IAAI,IAAIH,EAAY,UAAU,MAAM,CAAC,EACzDI,EAAY,IAAkBC,EAAsB,EACpDR,kBAAa,IAAI,IACvB,IAAK,IAAMK,KAAQH,EAAK,UAAU,MAAM,EAClCI,EAAkB,IAAID,CAAI,IAE5BV,EAAa,KAAKU,CAAI,GACrBT,EAAU,KAAKS,CAAI,IAAM,CAACE,EAAS,IAAIF,CAAI,GAAKR,EAAiC,IAAIQ,CAAI,KAE1FL,EAAW,IAAIK,CAAI,EAGvB,MAAO,CAAC,GAAGL,CAAU,CACvB,CAEA,SAASQ,GAAqC,CAE5C,IAAMC,EAAU,IAAI,YAAY,WAAW,EACrCF,kBAAW,IAAI,IAAI,qIAAsE,EAC/F,IAAK,IAAIG,EAAO,IAAMA,GAAQ,IAAM,IAClC,GAAI,KAAQ,KAAQA,GAAQ,KAC5B,IAAK,IAAIC,EAAQ,GAAMA,GAAS,IAAM,IACpCJ,EAAS,IAAIE,EAAQ,OAAO,IAAI,WAAW,CAACC,EAAMC,CAAK,CAAC,CAAC,CAAC,EAG9D,OAAOJ,CACT"}
|
package/dist/foreignCjk.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"foreignCjk.js","names":[],"sources":["../src/foreignCjk.ts"],"sourcesContent":["export type ForeignCjkLanguage = 'chinese' | 'korean';\n\nconst HANGUL_REGEX = /\\p{Script=Hangul}/u;\nconst HAN_REGEX = /\\p{Script=Han}/u;\n// JIS X 0208 cannot encode these Jōyō kanji, which were added in 2010.\nconst JOYO_KANJI_OUTSIDE_JIS_X_0208 = '𠮟塡剝頰';\n// Kanji for Japanese names (e.g., 髙, 﨑, 濵, 栁) that Chinese prose rarely contains: the NFKC forms of the\n// Windows-31J NEC-selected IBM and IBM extension kanji that neither JIS X 0208, Big5, nor GB2312 encodes, plus 𠮷 from\n// JIS X 0213. NFKC forms are listed because the lookup runs after NFKC normalization (e.g., U+FA20 becomes U+8612).\n// Some also appear in Chinese names (e.g., 喆), but prose that mixes Chinese into Japanese hardly contains them.\n// Extension kanji that Big5 or GB2312 encodes (e.g., 德, 瀨) stay Chinese because Chinese prose uses them.\nconst JAPANESE_NAME_KANJI_OUTSIDE_JIS_X_0208 =\n '仼伃伹侊俿偂僘僴兊兤冝凬刕劜劯匇匤厓叝咊咜喆坙坥垬埈墲夋奛奝奣尞岺峵嵓嵭嶹巐弡弴彅惞愑愰憘戓抦敎昞昻晳暿曺曻朎杦栁桒棏樰橳櫢櫤涖淸澵濵焏犱犾猤玽甁硺礰竧箞絈緖罇荢葈蓜蘒蠇裵褜訷贒郞鄕釞釥鈼鉙鉷鍈鏆隝隯霳霻靍靏靑靕髙魲鮏鮱鮻鰀﨎﨏﨑﨓﨔﨟﨡﨣﨤﨧﨨﨩𠮷';\n// Characters JIS X 0208 can encode but modern Japanese prose does not use:\n// - old forms (kyūjitai) of Jōyō kanji, from the `kyujitai` npm package and Unihan's kJapaneseOldVariant,\n// except those still common in Japanese words or names (e.g., 應 in 慶應, 澤, 邊, 龍, 藝 in 東京藝術大学, 糺す)\n// - Chinese function words that Japanese writes in kana (e.g., 們, 這, 麼, 很, 怎, 跟, 嘛) and stray simplified forms (e.g., 个)\n// - traditional Chinese characters that neither table covers but Japanese never uses: 碼, 舉, 鏈, 雖\n// Other Chinese-only content characters (e.g., 媽, 豬) are not listed because separating them from rare kanji\n// that Japanese prose does use (e.g., 乖離, 瑕疵, 齟齬, 俯瞰) needs Japanese usage frequencies.\nconst CHINESE_CHARACTERS_IN_JIS_X_0208 = new Set(\n '个乘亂亞从佛來們假傳僞價儉兒兩册刄剩劍劑勞勳勵勸區卷參听呀咯哇哦唔單嘛嚴囑囘圈國圍圓圖團墮壓壘壞壤壯壹壻壽奧奬姙孃學寢實寫寶將專對屆屬峽巖帶广廢廣廳彈很徑從怎恆惡惱愼慘懷戀戰戲拂拔拜挾插搖搜擇擔據擧擴攜攝收效敍敕數斷晝曉會條棧榮樂樓樞樣檢權歐歡歸殘殼毆氣沒淨淺滯滿潛澁濕濟灣燈燒營爐爭爲犧狹獨獵獸獻瓣畫當疊癡發盜盡碎碼祕禪禮稱稻穗穩竊竝粹絲經縣縱總繩繪繼續纖缺罐聲聽肅腦膽臟臺與舉舊舍舖艷莊莖萬藏藥處號螢蟲蠶蠻衞裝襃覺覽觀觸謠證譯譽讀變讓讚豐豫貳賣贊跟踐輕轉辨辭辯這遙遞遲鄰醉醫釀釋錢鎭鏈鐵鑄鑛關陷隨險隱隸雖雙雜霸靈靜顏顯飜飮餘餠騷驅驗驛髓體髮鬪鷄鹽麥麼黏默點黨齊齒齡龜'\n);\n\nlet japaneseKanji: Set<string> | undefined;\n\n/**\n * Detects Chinese or Korean characters mixed into Japanese text, such as LLM-generated Japanese prose.\n * See {@link findForeignCjkCharactersInJapanese} for which characters count and how `allowedText` works.\n * Returns the detected languages, or an empty array if none are found.\n */\nexport function detectForeignCjkInJapanese(text: string, allowedText?: string): ForeignCjkLanguage[] {\n const characters = findForeignCjkCharactersInJapanese(text, allowedText);\n const languages: ForeignCjkLanguage[] = [];\n if (characters.some((char) => !HANGUL_REGEX.test(char))) languages.push('chinese');\n if (characters.some((char) => HANGUL_REGEX.test(char))) languages.push('korean');\n return languages;\n}\n\n/**\n * Returns the distinct Chinese or Korean characters mixed into Japanese text, NFKC-normalized, in order of appearance.\n * A kanji is treated as Chinese when it is outside JIS X 0208 (except the four Jōyō kanji 𠮟塡剝頰 and Japanese name\n * kanji that neither Big5 nor GB2312 encodes, e.g., 髙, 﨑, 𠮷) or is one of the listed old forms, Chinese function\n * words, or other Chinese-only characters that modern Japanese prose does not use (e.g., 對, 國, 這, 們, 很, 碼).\n * Old forms in names that Chinese prose also uses (e.g., 國, 與, 德) are therefore also reported, while Chinese\n * containing none of these characters (e.g., 最后返回答案, 豬肉) is not.\n * Characters that also occur in `allowedText` are not reported, so passing the input that an LLM may quote\n * (e.g., the prompt, a question, or an error message) keeps quoted names and code from being reported.\n */\nexport function findForeignCjkCharactersInJapanese(text: string, allowedText = ''): string[] {\n const allowedCharacters = new Set(allowedText.normalize('NFKC'));\n const kanjiSet = (japaneseKanji ??= buildJapaneseKanjiSet());\n const characters = new Set<string>();\n for (const char of text.normalize('NFKC')) {\n if (allowedCharacters.has(char)) continue;\n if (\n HANGUL_REGEX.test(char) ||\n (HAN_REGEX.test(char) && (!kanjiSet.has(char) || CHINESE_CHARACTERS_IN_JIS_X_0208.has(char)))\n ) {\n characters.add(char);\n }\n }\n return [...characters];\n}\n\nfunction buildJapaneseKanjiSet(): Set<string> {\n // The WHATWG `shift_jis` decoder is Windows-31J, so skip its vendor-extension lead bytes (0xED-0xFC) to keep JIS X 0208 only.\n const decoder = new TextDecoder('shift_jis');\n const kanjiSet = new Set(JOYO_KANJI_OUTSIDE_JIS_X_0208 + JAPANESE_NAME_KANJI_OUTSIDE_JIS_X_0208);\n for (let lead = 0x81; lead <= 0xEA; lead++) {\n if (lead >= 0xA0 && lead <= 0xDF) continue;\n for (let trail = 0x40; trail <= 0xFC; trail++) {\n kanjiSet.add(decoder.decode(new Uint8Array([lead, trail])));\n }\n }\n return kanjiSet;\n}\n"],"mappings":"AAEA,
|
|
1
|
+
{"version":3,"file":"foreignCjk.js","names":["HANGUL_REGEX","HAN_REGEX","CHINESE_CHARACTERS_IN_JIS_X_0208","japaneseKanji","detectForeignCjkInJapanese","characters","findForeignCjkCharactersInJapanese","text","allowedText","languages","char","allowedCharacters","kanjiSet","buildJapaneseKanjiSet","decoder","lead","trail"],"sources":["../src/foreignCjk.ts"],"sourcesContent":["export type ForeignCjkLanguage = 'chinese' | 'korean';\n\nconst HANGUL_REGEX = /\\p{Script=Hangul}/u;\nconst HAN_REGEX = /\\p{Script=Han}/u;\n// JIS X 0208 cannot encode these Jōyō kanji, which were added in 2010.\nconst JOYO_KANJI_OUTSIDE_JIS_X_0208 = '𠮟塡剝頰';\n// Kanji for Japanese names (e.g., 髙, 﨑, 濵, 栁) that Chinese prose rarely contains: the NFKC forms of the\n// Windows-31J NEC-selected IBM and IBM extension kanji that neither JIS X 0208, Big5, nor GB2312 encodes, plus 𠮷 from\n// JIS X 0213. NFKC forms are listed because the lookup runs after NFKC normalization (e.g., U+FA20 becomes U+8612).\n// Some also appear in Chinese names (e.g., 喆), but prose that mixes Chinese into Japanese hardly contains them.\n// Extension kanji that Big5 or GB2312 encodes (e.g., 德, 瀨) stay Chinese because Chinese prose uses them.\nconst JAPANESE_NAME_KANJI_OUTSIDE_JIS_X_0208 =\n '仼伃伹侊俿偂僘僴兊兤冝凬刕劜劯匇匤厓叝咊咜喆坙坥垬埈墲夋奛奝奣尞岺峵嵓嵭嶹巐弡弴彅惞愑愰憘戓抦敎昞昻晳暿曺曻朎杦栁桒棏樰橳櫢櫤涖淸澵濵焏犱犾猤玽甁硺礰竧箞絈緖罇荢葈蓜蘒蠇裵褜訷贒郞鄕釞釥鈼鉙鉷鍈鏆隝隯霳霻靍靏靑靕髙魲鮏鮱鮻鰀﨎﨏﨑﨓﨔﨟﨡﨣﨤﨧﨨﨩𠮷';\n// Characters JIS X 0208 can encode but modern Japanese prose does not use:\n// - old forms (kyūjitai) of Jōyō kanji, from the `kyujitai` npm package and Unihan's kJapaneseOldVariant,\n// except those still common in Japanese words or names (e.g., 應 in 慶應, 澤, 邊, 龍, 藝 in 東京藝術大学, 糺す)\n// - Chinese function words that Japanese writes in kana (e.g., 們, 這, 麼, 很, 怎, 跟, 嘛) and stray simplified forms (e.g., 个)\n// - traditional Chinese characters that neither table covers but Japanese never uses: 碼, 舉, 鏈, 雖\n// Other Chinese-only content characters (e.g., 媽, 豬) are not listed because separating them from rare kanji\n// that Japanese prose does use (e.g., 乖離, 瑕疵, 齟齬, 俯瞰) needs Japanese usage frequencies.\nconst CHINESE_CHARACTERS_IN_JIS_X_0208 = new Set(\n '个乘亂亞从佛來們假傳僞價儉兒兩册刄剩劍劑勞勳勵勸區卷參听呀咯哇哦唔單嘛嚴囑囘圈國圍圓圖團墮壓壘壞壤壯壹壻壽奧奬姙孃學寢實寫寶將專對屆屬峽巖帶广廢廣廳彈很徑從怎恆惡惱愼慘懷戀戰戲拂拔拜挾插搖搜擇擔據擧擴攜攝收效敍敕數斷晝曉會條棧榮樂樓樞樣檢權歐歡歸殘殼毆氣沒淨淺滯滿潛澁濕濟灣燈燒營爐爭爲犧狹獨獵獸獻瓣畫當疊癡發盜盡碎碼祕禪禮稱稻穗穩竊竝粹絲經縣縱總繩繪繼續纖缺罐聲聽肅腦膽臟臺與舉舊舍舖艷莊莖萬藏藥處號螢蟲蠶蠻衞裝襃覺覽觀觸謠證譯譽讀變讓讚豐豫貳賣贊跟踐輕轉辨辭辯這遙遞遲鄰醉醫釀釋錢鎭鏈鐵鑄鑛關陷隨險隱隸雖雙雜霸靈靜顏顯飜飮餘餠騷驅驗驛髓體髮鬪鷄鹽麥麼黏默點黨齊齒齡龜'\n);\n\nlet japaneseKanji: Set<string> | undefined;\n\n/**\n * Detects Chinese or Korean characters mixed into Japanese text, such as LLM-generated Japanese prose.\n * See {@link findForeignCjkCharactersInJapanese} for which characters count and how `allowedText` works.\n * Returns the detected languages, or an empty array if none are found.\n */\nexport function detectForeignCjkInJapanese(text: string, allowedText?: string): ForeignCjkLanguage[] {\n const characters = findForeignCjkCharactersInJapanese(text, allowedText);\n const languages: ForeignCjkLanguage[] = [];\n if (characters.some((char) => !HANGUL_REGEX.test(char))) languages.push('chinese');\n if (characters.some((char) => HANGUL_REGEX.test(char))) languages.push('korean');\n return languages;\n}\n\n/**\n * Returns the distinct Chinese or Korean characters mixed into Japanese text, NFKC-normalized, in order of appearance.\n * A kanji is treated as Chinese when it is outside JIS X 0208 (except the four Jōyō kanji 𠮟塡剝頰 and Japanese name\n * kanji that neither Big5 nor GB2312 encodes, e.g., 髙, 﨑, 𠮷) or is one of the listed old forms, Chinese function\n * words, or other Chinese-only characters that modern Japanese prose does not use (e.g., 對, 國, 這, 們, 很, 碼).\n * Old forms in names that Chinese prose also uses (e.g., 國, 與, 德) are therefore also reported, while Chinese\n * containing none of these characters (e.g., 最后返回答案, 豬肉) is not.\n * Characters that also occur in `allowedText` are not reported, so passing the input that an LLM may quote\n * (e.g., the prompt, a question, or an error message) keeps quoted names and code from being reported.\n */\nexport function findForeignCjkCharactersInJapanese(text: string, allowedText = ''): string[] {\n const allowedCharacters = new Set(allowedText.normalize('NFKC'));\n const kanjiSet = (japaneseKanji ??= buildJapaneseKanjiSet());\n const characters = new Set<string>();\n for (const char of text.normalize('NFKC')) {\n if (allowedCharacters.has(char)) continue;\n if (\n HANGUL_REGEX.test(char) ||\n (HAN_REGEX.test(char) && (!kanjiSet.has(char) || CHINESE_CHARACTERS_IN_JIS_X_0208.has(char)))\n ) {\n characters.add(char);\n }\n }\n return [...characters];\n}\n\nfunction buildJapaneseKanjiSet(): Set<string> {\n // The WHATWG `shift_jis` decoder is Windows-31J, so skip its vendor-extension lead bytes (0xED-0xFC) to keep JIS X 0208 only.\n const decoder = new TextDecoder('shift_jis');\n const kanjiSet = new Set(JOYO_KANJI_OUTSIDE_JIS_X_0208 + JAPANESE_NAME_KANJI_OUTSIDE_JIS_X_0208);\n for (let lead = 0x81; lead <= 0xEA; lead++) {\n if (lead >= 0xA0 && lead <= 0xDF) continue;\n for (let trail = 0x40; trail <= 0xFC; trail++) {\n kanjiSet.add(decoder.decode(new Uint8Array([lead, trail])));\n }\n }\n return kanjiSet;\n}\n"],"mappings":"AAEA,MAAMA,EAAe,qBACfC,EAAY,kBAiBZC,kBAAmC,IAAI,IAC3C,+RACF,EAEA,IAAIC,EAOJ,SAAgBC,EAA2B,EAAc,EAA4C,CACnG,IAAMC,EAAaC,EAAmCC,EAAMC,CAAW,EACjEC,EAAkC,CAAC,EAGzC,OAFIJ,EAAW,KAAM,GAAS,CAACL,EAAa,KAAKU,CAAI,CAAC,GAAGD,EAAU,KAAK,SAAS,EAC7EJ,EAAW,KAAM,GAASL,EAAa,KAAKU,CAAI,CAAC,GAAGD,EAAU,KAAK,QAAQ,EACxEA,CACT,CAYA,SAAgBH,EAAmC,EAAc,EAAc,GAAc,CAC3F,IAAMK,EAAoB,IAAI,IAAIH,EAAY,UAAU,MAAM,CAAC,EACzDI,EAAY,IAAkBC,EAAsB,EACpDR,kBAAa,IAAI,IACvB,IAAK,IAAMK,KAAQH,EAAK,UAAU,MAAM,EAClCI,EAAkB,IAAID,CAAI,IAE5BV,EAAa,KAAKU,CAAI,GACrBT,EAAU,KAAKS,CAAI,IAAM,CAACE,EAAS,IAAIF,CAAI,GAAKR,EAAiC,IAAIQ,CAAI,KAE1FL,EAAW,IAAIK,CAAI,EAGvB,MAAO,CAAC,GAAGL,CAAU,CACvB,CAEA,SAASQ,GAAqC,CAE5C,IAAMC,EAAU,IAAI,YAAY,WAAW,EACrCF,kBAAW,IAAI,IAAI,qIAAsE,EAC/F,IAAK,IAAIG,EAAO,IAAMA,GAAQ,IAAM,IAClC,GAAI,KAAQ,KAAQA,GAAQ,KAC5B,IAAK,IAAIC,EAAQ,GAAMA,GAAS,IAAM,IACpCJ,EAAS,IAAIE,EAAQ,OAAO,IAAI,WAAW,CAACC,EAAMC,CAAK,CAAC,CAAC,CAAC,EAG9D,OAAOJ,CACT"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"formatIsoDate.cjs","names":[],"sources":["../src/formatIsoDate.ts"],"sourcesContent":["const formatters = new Map<string, Intl.DateTimeFormat>();\n\n/**\n * Formats the calendar date of `date` in an IANA `timeZone` (e.g. `Asia/Tokyo`) as YYYY-MM-DD, independent of the\n * runtime's local time zone, which is UTC on Cloudflare Workers and most servers.\n * Supports ISO years 0000–9999, like `parseIsoDate`. Throws a `RangeError` for a date outside them in `timeZone` and\n * for an unknown time zone.\n */\nexport function formatIsoDateInTimeZone(date: Date, timeZone: string): string {\n let formatter = formatters.get(timeZone);\n if (!formatter) {\n // Constructing a formatter costs far more than formatting, so one is kept per time zone.\n formatter = new Intl.DateTimeFormat('en-US', {\n timeZone,\n era: 'short',\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n });\n formatters.set(timeZone, formatter);\n }\n let year = 0;\n let isBeforeCommonEra = false;\n let month = '';\n let day = '';\n // `formatToParts` is used instead of a locale whose format happens to be YYYY-MM-DD (e.g. `en-CA`), since such\n // formats follow CLDR updates.\n for (const part of formatter.formatToParts(date)) {\n if (part.type === 'year') year = Number(part.value);\n else if (part.type === 'era') isBeforeCommonEra = part.value === 'BC';\n else if (part.type === 'month') month = part.value;\n else if (part.type === 'day') day = part.value;\n }\n // The Gregorian calendar has no year 0: ISO year 0000 is 1 BC.\n const isoYear = isBeforeCommonEra ? 1 - year : year;\n if (isoYear < 0 || isoYear > 9999) throw new RangeError(`The year ${isoYear} in ${timeZone} is outside 0000–9999`);\n return `${String(isoYear).padStart(4, '0')}-${month}-${day}`;\n}\n"],"mappings":"aAAA,
|
|
1
|
+
{"version":3,"file":"formatIsoDate.cjs","names":["formatters","formatIsoDateInTimeZone","formatter","timeZone","year","isBeforeCommonEra","month","day","part","date","isoYear"],"sources":["../src/formatIsoDate.ts"],"sourcesContent":["const formatters = new Map<string, Intl.DateTimeFormat>();\n\n/**\n * Formats the calendar date of `date` in an IANA `timeZone` (e.g. `Asia/Tokyo`) as YYYY-MM-DD, independent of the\n * runtime's local time zone, which is UTC on Cloudflare Workers and most servers.\n * Supports ISO years 0000–9999, like `parseIsoDate`. Throws a `RangeError` for a date outside them in `timeZone` and\n * for an unknown time zone.\n */\nexport function formatIsoDateInTimeZone(date: Date, timeZone: string): string {\n let formatter = formatters.get(timeZone);\n if (!formatter) {\n // Constructing a formatter costs far more than formatting, so one is kept per time zone.\n formatter = new Intl.DateTimeFormat('en-US', {\n timeZone,\n era: 'short',\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n });\n formatters.set(timeZone, formatter);\n }\n let year = 0;\n let isBeforeCommonEra = false;\n let month = '';\n let day = '';\n // `formatToParts` is used instead of a locale whose format happens to be YYYY-MM-DD (e.g. `en-CA`), since such\n // formats follow CLDR updates.\n for (const part of formatter.formatToParts(date)) {\n if (part.type === 'year') year = Number(part.value);\n else if (part.type === 'era') isBeforeCommonEra = part.value === 'BC';\n else if (part.type === 'month') month = part.value;\n else if (part.type === 'day') day = part.value;\n }\n // The Gregorian calendar has no year 0: ISO year 0000 is 1 BC.\n const isoYear = isBeforeCommonEra ? 1 - year : year;\n if (isoYear < 0 || isoYear > 9999) throw new RangeError(`The year ${isoYear} in ${timeZone} is outside 0000–9999`);\n return `${String(isoYear).padStart(4, '0')}-${month}-${day}`;\n}\n"],"mappings":"aAAA,MAAMA,kBAAa,IAAI,IAQvB,SAAgBC,EAAwB,EAAY,EAA0B,CAC5E,IAAIC,EAAYF,EAAW,IAAIG,CAAQ,EAClCD,IAEH,EAAY,IAAI,KAAK,eAAe,QAAS,CAC3C,WACA,IAAK,QACL,KAAM,UACN,MAAO,UACP,IAAK,SACP,CAAC,EACDF,EAAW,IAAIG,EAAUD,CAAS,GAEpC,IAAIE,EAAO,EACPC,EAAoB,GACpBC,EAAQ,GACRC,EAAM,GAGV,IAAK,IAAMC,KAAQN,EAAU,cAAcO,CAAI,EACzCD,EAAK,OAAS,OAAQ,EAAO,OAAOA,EAAK,KAAK,EACzCA,EAAK,OAAS,MAAO,EAAoBA,EAAK,QAAU,KACxDA,EAAK,OAAS,QAAS,EAAQA,EAAK,MACpCA,EAAK,OAAS,QAAO,EAAMA,EAAK,OAG3C,IAAME,EAAUL,EAAoB,EAAID,EAAOA,EAC/C,GAAIM,EAAU,GAAKA,EAAU,KAAM,MAAU,WAAW,YAAYA,EAAQ,MAAMP,EAAS,sBAAsB,EACjH,MAAO,GAAG,OAAOO,CAAO,CAAC,CAAC,SAAS,EAAG,GAAG,EAAE,GAAGJ,EAAM,GAAGC,GACzD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"formatIsoDate.js","names":[],"sources":["../src/formatIsoDate.ts"],"sourcesContent":["const formatters = new Map<string, Intl.DateTimeFormat>();\n\n/**\n * Formats the calendar date of `date` in an IANA `timeZone` (e.g. `Asia/Tokyo`) as YYYY-MM-DD, independent of the\n * runtime's local time zone, which is UTC on Cloudflare Workers and most servers.\n * Supports ISO years 0000–9999, like `parseIsoDate`. Throws a `RangeError` for a date outside them in `timeZone` and\n * for an unknown time zone.\n */\nexport function formatIsoDateInTimeZone(date: Date, timeZone: string): string {\n let formatter = formatters.get(timeZone);\n if (!formatter) {\n // Constructing a formatter costs far more than formatting, so one is kept per time zone.\n formatter = new Intl.DateTimeFormat('en-US', {\n timeZone,\n era: 'short',\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n });\n formatters.set(timeZone, formatter);\n }\n let year = 0;\n let isBeforeCommonEra = false;\n let month = '';\n let day = '';\n // `formatToParts` is used instead of a locale whose format happens to be YYYY-MM-DD (e.g. `en-CA`), since such\n // formats follow CLDR updates.\n for (const part of formatter.formatToParts(date)) {\n if (part.type === 'year') year = Number(part.value);\n else if (part.type === 'era') isBeforeCommonEra = part.value === 'BC';\n else if (part.type === 'month') month = part.value;\n else if (part.type === 'day') day = part.value;\n }\n // The Gregorian calendar has no year 0: ISO year 0000 is 1 BC.\n const isoYear = isBeforeCommonEra ? 1 - year : year;\n if (isoYear < 0 || isoYear > 9999) throw new RangeError(`The year ${isoYear} in ${timeZone} is outside 0000–9999`);\n return `${String(isoYear).padStart(4, '0')}-${month}-${day}`;\n}\n"],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"formatIsoDate.js","names":["formatters","formatIsoDateInTimeZone","formatter","timeZone","year","isBeforeCommonEra","month","day","part","date","isoYear"],"sources":["../src/formatIsoDate.ts"],"sourcesContent":["const formatters = new Map<string, Intl.DateTimeFormat>();\n\n/**\n * Formats the calendar date of `date` in an IANA `timeZone` (e.g. `Asia/Tokyo`) as YYYY-MM-DD, independent of the\n * runtime's local time zone, which is UTC on Cloudflare Workers and most servers.\n * Supports ISO years 0000–9999, like `parseIsoDate`. Throws a `RangeError` for a date outside them in `timeZone` and\n * for an unknown time zone.\n */\nexport function formatIsoDateInTimeZone(date: Date, timeZone: string): string {\n let formatter = formatters.get(timeZone);\n if (!formatter) {\n // Constructing a formatter costs far more than formatting, so one is kept per time zone.\n formatter = new Intl.DateTimeFormat('en-US', {\n timeZone,\n era: 'short',\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n });\n formatters.set(timeZone, formatter);\n }\n let year = 0;\n let isBeforeCommonEra = false;\n let month = '';\n let day = '';\n // `formatToParts` is used instead of a locale whose format happens to be YYYY-MM-DD (e.g. `en-CA`), since such\n // formats follow CLDR updates.\n for (const part of formatter.formatToParts(date)) {\n if (part.type === 'year') year = Number(part.value);\n else if (part.type === 'era') isBeforeCommonEra = part.value === 'BC';\n else if (part.type === 'month') month = part.value;\n else if (part.type === 'day') day = part.value;\n }\n // The Gregorian calendar has no year 0: ISO year 0000 is 1 BC.\n const isoYear = isBeforeCommonEra ? 1 - year : year;\n if (isoYear < 0 || isoYear > 9999) throw new RangeError(`The year ${isoYear} in ${timeZone} is outside 0000–9999`);\n return `${String(isoYear).padStart(4, '0')}-${month}-${day}`;\n}\n"],"mappings":"AAAA,MAAMA,kBAAa,IAAI,IAQvB,SAAgBC,EAAwB,EAAY,EAA0B,CAC5E,IAAIC,EAAYF,EAAW,IAAIG,CAAQ,EAClCD,IAEH,EAAY,IAAI,KAAK,eAAe,QAAS,CAC3C,WACA,IAAK,QACL,KAAM,UACN,MAAO,UACP,IAAK,SACP,CAAC,EACDF,EAAW,IAAIG,EAAUD,CAAS,GAEpC,IAAIE,EAAO,EACPC,EAAoB,GACpBC,EAAQ,GACRC,EAAM,GAGV,IAAK,IAAMC,KAAQN,EAAU,cAAcO,CAAI,EACzCD,EAAK,OAAS,OAAQ,EAAO,OAAOA,EAAK,KAAK,EACzCA,EAAK,OAAS,MAAO,EAAoBA,EAAK,QAAU,KACxDA,EAAK,OAAS,QAAS,EAAQA,EAAK,MACpCA,EAAK,OAAS,QAAO,EAAMA,EAAK,OAG3C,IAAME,EAAUL,EAAoB,EAAID,EAAOA,EAC/C,GAAIM,EAAU,GAAKA,EAAU,KAAM,MAAU,WAAW,YAAYA,EAAQ,MAAMP,EAAS,sBAAsB,EACjH,MAAO,GAAG,OAAOO,CAAO,CAAC,CAAC,SAAS,EAAG,GAAG,EAAE,GAAGJ,EAAM,GAAGC,GACzD"}
|
package/dist/github.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"github.cjs","names":[],"sources":["../src/github.ts"],"sourcesContent":["/**\n * Parses a GitHub repository full name such as `WillBooster/shared`. Returns undefined unless the owner consists of\n * alphanumerics and hyphens and the repository name of alphanumerics, `-`, `_`, and `.` other than `.` and `..`.\n */\nexport function parseGitHubRepositoryFullName(fullName: string): { owner: string; repo: string } | undefined {\n const match = /^([\\dA-Za-z-]+)\\/([\\w.-]+)$/u.exec(fullName);\n if (!match) return undefined;\n const [, owner, repo] = match as unknown as [string, string, string];\n return repo === '.' || repo === '..' ? undefined : { owner, repo };\n}\n"],"mappings":"aAIA,
|
|
1
|
+
{"version":3,"file":"github.cjs","names":["parseGitHubRepositoryFullName","match","fullName","owner","repo"],"sources":["../src/github.ts"],"sourcesContent":["/**\n * Parses a GitHub repository full name such as `WillBooster/shared`. Returns undefined unless the owner consists of\n * alphanumerics and hyphens and the repository name of alphanumerics, `-`, `_`, and `.` other than `.` and `..`.\n */\nexport function parseGitHubRepositoryFullName(fullName: string): { owner: string; repo: string } | undefined {\n const match = /^([\\dA-Za-z-]+)\\/([\\w.-]+)$/u.exec(fullName);\n if (!match) return undefined;\n const [, owner, repo] = match as unknown as [string, string, string];\n return repo === '.' || repo === '..' ? undefined : { owner, repo };\n}\n"],"mappings":"aAIA,SAAgBA,EAA8B,EAA+D,CAC3G,IAAMC,EAAQ,+BAA+B,KAAKC,CAAQ,EAC1D,GAAI,CAACD,EAAO,OACZ,GAAM,EAAGE,EAAOC,GAAQH,EACxB,OAAOG,IAAS,KAAOA,IAAS,KAAO,IAAA,GAAY,CAAE,QAAO,MAAK,CACnE"}
|
package/dist/github.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"github.js","names":[],"sources":["../src/github.ts"],"sourcesContent":["/**\n * Parses a GitHub repository full name such as `WillBooster/shared`. Returns undefined unless the owner consists of\n * alphanumerics and hyphens and the repository name of alphanumerics, `-`, `_`, and `.` other than `.` and `..`.\n */\nexport function parseGitHubRepositoryFullName(fullName: string): { owner: string; repo: string } | undefined {\n const match = /^([\\dA-Za-z-]+)\\/([\\w.-]+)$/u.exec(fullName);\n if (!match) return undefined;\n const [, owner, repo] = match as unknown as [string, string, string];\n return repo === '.' || repo === '..' ? undefined : { owner, repo };\n}\n"],"mappings":"AAIA,
|
|
1
|
+
{"version":3,"file":"github.js","names":["parseGitHubRepositoryFullName","match","fullName","owner","repo"],"sources":["../src/github.ts"],"sourcesContent":["/**\n * Parses a GitHub repository full name such as `WillBooster/shared`. Returns undefined unless the owner consists of\n * alphanumerics and hyphens and the repository name of alphanumerics, `-`, `_`, and `.` other than `.` and `..`.\n */\nexport function parseGitHubRepositoryFullName(fullName: string): { owner: string; repo: string } | undefined {\n const match = /^([\\dA-Za-z-]+)\\/([\\w.-]+)$/u.exec(fullName);\n if (!match) return undefined;\n const [, owner, repo] = match as unknown as [string, string, string];\n return repo === '.' || repo === '..' ? undefined : { owner, repo };\n}\n"],"mappings":"AAIA,SAAgBA,EAA8B,EAA+D,CAC3G,IAAMC,EAAQ,+BAA+B,KAAKC,CAAQ,EAC1D,GAAI,CAACD,EAAO,OACZ,GAAM,EAAGE,EAAOC,GAAQH,EACxB,OAAOG,IAAS,KAAOA,IAAS,KAAO,IAAA,GAAY,CAAE,QAAO,MAAK,CACnE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"githubTemplates.cjs","names":[],"sources":["../src/githubTemplates.ts"],"sourcesContent":["/**\n * A section of a PR or issue body: its heading and what the section must state. `requirement`\n * starts lowercase and ends without punctuation, so that it reads both as a placeholder sentence\n * (capitalized, with a period appended) and as a checklist clause after the heading.\n */\nexport interface TemplateSection {\n readonly heading: string;\n readonly requirement: string;\n}\n\n/** Sections of a pull request body, in the order they appear in the PR template. */\nexport const PULL_REQUEST_SECTIONS: readonly TemplateSection[] = [\n {\n heading: 'Why',\n requirement: 'the problem, with the numbers or observations behind it, and why this approach over the alternatives',\n },\n {\n heading: 'Requirements',\n requirement:\n \"the requirements the change must satisfy and the guarantees it must keep as they stand now, one per line, each marked `required` (asked for by the requester, or an existing contract callers depend on) or `chosen` (the implementer's own decision with the reason that decided it, which a simpler design may replace); state the requester's instructions in the requester's terms, whether they came from an issue or a conversation, and keep each line as it is until the requester overrides or withdraws the instruction behind it or the decision behind it changes, and then rewrite or remove that line instead of adding one beside it\",\n },\n {\n heading: 'Customer Summary',\n requirement:\n 'behavior, workflow, or user-visible changes, written for readers who know nothing about the implementation',\n },\n {\n heading: 'Technical Summary',\n requirement:\n 'decisions and their reasons, data flow, where to look (the files that matter most), and what to check hardest: the parts whose correctness is argued rather than proven',\n },\n {\n heading: 'Testing',\n requirement: 'commands run, tests added, and what was NOT exercised and why',\n },\n {\n heading: 'Notes',\n requirement: 'known limitations, compatibility and migration, follow-up work; delete this section if there is none',\n },\n];\n\n/**\n * How a tool that writes or rewrites a PR body derives it from the repository's PR template, as\n * markdown bullets for an agent's instructions. Stated once here and rendered by every writer (the\n * generated agent instructions, the PR-drafting skills) so the writers cannot drift apart; the\n * template-less branch lets the same text serve repositories outside the organization.\n */\nconst PULL_REQUEST_TEMPLATE_RULES = `- Base the PR body on \\`.github/pull_request_template.md\\` when creating or updating a PR, even when a skill or workflow supplies its own skeleton: keep the template's headings in order, fill each section with what its placeholder comment asks for at a length fitting the change (a sentence for a small change, numbered subsections for a large one), and delete the placeholder comments and an empty Notes section. Without a template, use no fixed headings: state the scope, the motivation, and the verification concisely.\n- Start the body with \\`Close #<n>\\` only when the PR resolves an existing issue.`;\n\n/**\n * How a tool that writes or rewrites a PR body fills the Requirements section of\n * `PULL_REQUEST_SECTIONS`, as markdown bullets for an agent's instructions. Skills that draft PR\n * bodies render this text instead of restating it, so the writers and the reviewers that read the\n * section as the boundary a fix must keep never disagree on what a line means.\n */\nexport const PULL_REQUEST_REQUIREMENTS_RULES = `- Requirements section: the requirements in force now, one line per requirement, taken from the requester's instructions in the requester's terms (the issue, the conversation that asked for the change), each marked \\`required\\` (asked for, or an existing contract callers depend on) or \\`chosen\\` (your own decision with the reason that decided it, which a simpler design may replace). Never infer a requirement from the diff: when no issue, PR message, or conversation states the request, write \\`required: not recorded — ask the requester\\` as the only request-derived line (a \\`required\\` line for an existing contract callers depend on may still be listed) and say so in Notes.\n- Current-state rule: an existing Requirements section records what the requester asked for and what was decided as it stands now; it is neither a description of the diff nor a log of how the request evolved. Keep each line as it is until the requester overrides or withdraws the instruction behind it or the decision behind it changes, and then rewrite or remove that line instead of appending a line beside it; with no instructions available, keep the requester-derived lines unchanged. Never mark when a line was added (e.g., \"(added)\"), and add a \\`chosen\\` line for each new decision. When the current design cannot be understood without the change of direction, state it in one sentence in Notes. A section that merely describes the implementation, or holds only the template's placeholder or a \\`required: not recorded\\` line, is not a record: write it afresh by the rule above.\n- Place the section where the template puts it; when the template has no such heading, right after the issue-closing line, or at the top of the body when there is none.\n- Never drop or weaken a \\`required\\` line to fit what was implemented: when one cannot hold in the diff, keep it and say so in Notes.`;\n\n/** The complete rules for writing a PR body: the template rules followed by the Requirements rules. */\nexport const PULL_REQUEST_BODY_RULES = `${PULL_REQUEST_TEMPLATE_RULES}\\n${PULL_REQUEST_REQUIREMENTS_RULES}`;\n\n/**\n * How a tool that creates an issue derives it from the repository's issue templates, as markdown\n * bullets for an agent's instructions; shared by the same writers as `PULL_REQUEST_BODY_RULES`.\n * Guidance the templates state in their own placeholder comments (which sections to keep, deleting\n * the comments) is deliberately not repeated here, since the writer reads the template anyway.\n */\nexport const ISSUE_TEMPLATE_RULES = `- Follow the closest template under \\`.github/ISSUE_TEMPLATE/\\`: \\`bug.md\\` for wrong behavior, \\`change.md\\` for anything to build or alter; a question or note fitting neither, or a repository without templates, needs no template.\n- Title: a Conventional Commits prefix for the type that fits the change (\\`feat:\\`, \\`fix:\\`, \\`refactor:\\`, \\`docs:\\`, \\`chore:\\`, ...); when it differs from the template's \\`title\\` prefix, replace the template's type label (\\`t: ...\\`) with the one matching the type.\n- The YAML front matter between the \\`---\\` lines is metadata, not body text: pass its \\`labels\\` via \\`--label\\` and submit only the content below the closing \\`---\\` as the body.`;\n\n/** Sections of a bug report, in the order they appear in the issue template. */\nexport const BUG_ISSUE_SECTIONS: readonly TemplateSection[] = [\n { heading: 'Problem', requirement: 'what happens, and what should happen instead' },\n { heading: 'Evidence', requirement: 'reproduction steps, logs, run IDs, or links' },\n { heading: 'Impact', requirement: 'who or what is affected, and how badly' },\n { heading: 'Proposal', requirement: 'the fix you have in mind, and the root cause if known' },\n];\n\n/**\n * Sections a change specification must settle before a third party can implement it, defined\n * once so that the issue template and any tool that drafts or reviews specifications against\n * these sections cannot drift apart; checklist consumers render it with `renderSectionChecklist`.\n * The first three sections suffice for a small change, so they come first.\n */\nexport const CHANGE_ISSUE_SECTIONS: readonly TemplateSection[] = [\n {\n heading: 'Background and goal',\n requirement: 'the problem being solved, for whom, and what outcome counts as success',\n },\n {\n heading: 'Behavior',\n requirement:\n 'every user- or caller-observable behavior, including inputs, outputs, error cases, edge cases, and interactions with existing behavior, stated precisely enough that two implementers would build the same thing',\n },\n {\n heading: 'Acceptance criteria',\n requirement:\n 'checkable conditions (Given/When/Then or an equivalent) that decide whether the implementation is done',\n },\n {\n heading: 'Scope and non-goals',\n requirement: 'what the change covers and what it deliberately leaves out, so an implementer knows where to stop',\n },\n {\n heading: 'Design constraints',\n requirement:\n 'the data, API, schema, UI, and configuration changes, naming, and the existing code or conventions the implementation must fit — as far as the writer intends to constrain them, with everything else explicitly left to the implementer',\n },\n {\n heading: 'Compatibility and migration',\n requirement: 'what existing data, users, callers, or configurations are affected and how the transition happens',\n },\n {\n heading: 'Verification',\n requirement: 'how the change is tested or demonstrated, including what needs no test and why',\n },\n {\n heading: 'Open questions',\n requirement: 'undecided points, each with its options; empty once the specification is implementable',\n },\n];\n\n/** Renders sections as markdown bullets (`- **Heading**: requirement`). */\nexport function renderSectionChecklist(sections: readonly TemplateSection[]): string {\n return sections.map((section) => `- **${section.heading}**: ${section.requirement}`).join('\\n');\n}\n\n/** Renders sections as markdown headings, each followed by its requirement as a placeholder comment. */\nexport function renderSectionTemplate(sections: readonly TemplateSection[]): string {\n return sections\n .map(\n (section) =>\n `## ${section.heading}\\n\\n<!-- ${section.requirement.charAt(0).toUpperCase()}${section.requirement.slice(1)}. -->`\n )\n .join('\\n\\n');\n}\n"],"mappings":"aAWA,MAAa,EAAoD,CAC/D,CACE,QAAS,MACT,YAAa,sGACf,EACA,CACE,QAAS,eACT,YACE,qnBACJ,EACA,CACE,QAAS,mBACT,YACE,4GACJ,EACA,CACE,QAAS,oBACT,YACE,yKACJ,EACA,CACE,QAAS,UACT,YAAa,+DACf,EACA,CACE,QAAS,QACT,YAAa,sGACf,CACF,EAiBa,EAAkC,y0DAMlC,EAA0B;qFAAmC,IAa7D,EAAiD,CAC5D,CAAE,QAAS,UAAW,YAAa,8CAA+C,EAClF,CAAE,QAAS,WAAY,YAAa,6CAA8C,EAClF,CAAE,QAAS,SAAU,YAAa,wCAAyC,EAC3E,CAAE,QAAS,WAAY,YAAa,uDAAwD,CAC9F,EAQa,EAAoD,CAC/D,CACE,QAAS,sBACT,YAAa,wEACf,EACA,CACE,QAAS,WACT,YACE,kNACJ,EACA,CACE,QAAS,sBACT,YACE,wGACJ,EACA,CACE,QAAS,sBACT,YAAa,mGACf,EACA,CACE,QAAS,qBACT,YACE,0OACJ,EACA,CACE,QAAS,8BACT,YAAa,mGACf,EACA,CACE,QAAS,eACT,YAAa,gFACf,EACA,CACE,QAAS,iBACT,YAAa,wFACf,CACF,EAGA,SAAgB,EAAuB,EAA8C,CACnF,OAAO,EAAS,IAAK,GAAY,OAAO,EAAQ,QAAQ,MAAM,EAAQ,aAAa,CAAC,CAAC,KAAK;CAAI,CAChG,CAGA,SAAgB,EAAsB,EAA8C,CAClF,OAAO,EACJ,IACE,GACC,MAAM,EAAQ,QAAQ,WAAW,EAAQ,YAAY,OAAO,CAAC,CAAC,CAAC,YAAY,IAAI,EAAQ,YAAY,MAAM,CAAC,EAAE,MAChH,CAAC,CACA,KAAK;;CAAM,CAChB"}
|
|
1
|
+
{"version":3,"file":"githubTemplates.cjs","names":["PULL_REQUEST_SECTIONS","PULL_REQUEST_REQUIREMENTS_RULES","PULL_REQUEST_BODY_RULES","BUG_ISSUE_SECTIONS","CHANGE_ISSUE_SECTIONS","renderSectionChecklist","sections","section","renderSectionTemplate"],"sources":["../src/githubTemplates.ts"],"sourcesContent":["/**\n * A section of a PR or issue body: its heading and what the section must state. `requirement`\n * starts lowercase and ends without punctuation, so that it reads both as a placeholder sentence\n * (capitalized, with a period appended) and as a checklist clause after the heading.\n */\nexport interface TemplateSection {\n readonly heading: string;\n readonly requirement: string;\n}\n\n/** Sections of a pull request body, in the order they appear in the PR template. */\nexport const PULL_REQUEST_SECTIONS: readonly TemplateSection[] = [\n {\n heading: 'Why',\n requirement: 'the problem, with the numbers or observations behind it, and why this approach over the alternatives',\n },\n {\n heading: 'Requirements',\n requirement:\n \"the requirements the change must satisfy and the guarantees it must keep as they stand now, one per line, each marked `required` (asked for by the requester, or an existing contract callers depend on) or `chosen` (the implementer's own decision with the reason that decided it, which a simpler design may replace); state the requester's instructions in the requester's terms, whether they came from an issue or a conversation, and keep each line as it is until the requester overrides or withdraws the instruction behind it or the decision behind it changes, and then rewrite or remove that line instead of adding one beside it\",\n },\n {\n heading: 'Customer Summary',\n requirement:\n 'behavior, workflow, or user-visible changes, written for readers who know nothing about the implementation',\n },\n {\n heading: 'Technical Summary',\n requirement:\n 'decisions and their reasons, data flow, where to look (the files that matter most), and what to check hardest: the parts whose correctness is argued rather than proven',\n },\n {\n heading: 'Testing',\n requirement: 'commands run, tests added, and what was NOT exercised and why',\n },\n {\n heading: 'Notes',\n requirement: 'known limitations, compatibility and migration, follow-up work; delete this section if there is none',\n },\n];\n\n/**\n * How a tool that writes or rewrites a PR body derives it from the repository's PR template, as\n * markdown bullets for an agent's instructions. Stated once here and rendered by every writer (the\n * generated agent instructions, the PR-drafting skills) so the writers cannot drift apart; the\n * template-less branch lets the same text serve repositories outside the organization.\n */\nconst PULL_REQUEST_TEMPLATE_RULES = `- Base the PR body on \\`.github/pull_request_template.md\\` when creating or updating a PR, even when a skill or workflow supplies its own skeleton: keep the template's headings in order, fill each section with what its placeholder comment asks for at a length fitting the change (a sentence for a small change, numbered subsections for a large one), and delete the placeholder comments and an empty Notes section. Without a template, use no fixed headings: state the scope, the motivation, and the verification concisely.\n- Start the body with \\`Close #<n>\\` only when the PR resolves an existing issue.`;\n\n/**\n * How a tool that writes or rewrites a PR body fills the Requirements section of\n * `PULL_REQUEST_SECTIONS`, as markdown bullets for an agent's instructions. Skills that draft PR\n * bodies render this text instead of restating it, so the writers and the reviewers that read the\n * section as the boundary a fix must keep never disagree on what a line means.\n */\nexport const PULL_REQUEST_REQUIREMENTS_RULES = `- Requirements section: the requirements in force now, one line per requirement, taken from the requester's instructions in the requester's terms (the issue, the conversation that asked for the change), each marked \\`required\\` (asked for, or an existing contract callers depend on) or \\`chosen\\` (your own decision with the reason that decided it, which a simpler design may replace). Never infer a requirement from the diff: when no issue, PR message, or conversation states the request, write \\`required: not recorded — ask the requester\\` as the only request-derived line (a \\`required\\` line for an existing contract callers depend on may still be listed) and say so in Notes.\n- Current-state rule: an existing Requirements section records what the requester asked for and what was decided as it stands now; it is neither a description of the diff nor a log of how the request evolved. Keep each line as it is until the requester overrides or withdraws the instruction behind it or the decision behind it changes, and then rewrite or remove that line instead of appending a line beside it; with no instructions available, keep the requester-derived lines unchanged. Never mark when a line was added (e.g., \"(added)\"), and add a \\`chosen\\` line for each new decision. When the current design cannot be understood without the change of direction, state it in one sentence in Notes. A section that merely describes the implementation, or holds only the template's placeholder or a \\`required: not recorded\\` line, is not a record: write it afresh by the rule above.\n- Place the section where the template puts it; when the template has no such heading, right after the issue-closing line, or at the top of the body when there is none.\n- Never drop or weaken a \\`required\\` line to fit what was implemented: when one cannot hold in the diff, keep it and say so in Notes.`;\n\n/** The complete rules for writing a PR body: the template rules followed by the Requirements rules. */\nexport const PULL_REQUEST_BODY_RULES = `${PULL_REQUEST_TEMPLATE_RULES}\\n${PULL_REQUEST_REQUIREMENTS_RULES}`;\n\n/**\n * How a tool that creates an issue derives it from the repository's issue templates, as markdown\n * bullets for an agent's instructions; shared by the same writers as `PULL_REQUEST_BODY_RULES`.\n * Guidance the templates state in their own placeholder comments (which sections to keep, deleting\n * the comments) is deliberately not repeated here, since the writer reads the template anyway.\n */\nexport const ISSUE_TEMPLATE_RULES = `- Follow the closest template under \\`.github/ISSUE_TEMPLATE/\\`: \\`bug.md\\` for wrong behavior, \\`change.md\\` for anything to build or alter; a question or note fitting neither, or a repository without templates, needs no template.\n- Title: a Conventional Commits prefix for the type that fits the change (\\`feat:\\`, \\`fix:\\`, \\`refactor:\\`, \\`docs:\\`, \\`chore:\\`, ...); when it differs from the template's \\`title\\` prefix, replace the template's type label (\\`t: ...\\`) with the one matching the type.\n- The YAML front matter between the \\`---\\` lines is metadata, not body text: pass its \\`labels\\` via \\`--label\\` and submit only the content below the closing \\`---\\` as the body.`;\n\n/** Sections of a bug report, in the order they appear in the issue template. */\nexport const BUG_ISSUE_SECTIONS: readonly TemplateSection[] = [\n { heading: 'Problem', requirement: 'what happens, and what should happen instead' },\n { heading: 'Evidence', requirement: 'reproduction steps, logs, run IDs, or links' },\n { heading: 'Impact', requirement: 'who or what is affected, and how badly' },\n { heading: 'Proposal', requirement: 'the fix you have in mind, and the root cause if known' },\n];\n\n/**\n * Sections a change specification must settle before a third party can implement it, defined\n * once so that the issue template and any tool that drafts or reviews specifications against\n * these sections cannot drift apart; checklist consumers render it with `renderSectionChecklist`.\n * The first three sections suffice for a small change, so they come first.\n */\nexport const CHANGE_ISSUE_SECTIONS: readonly TemplateSection[] = [\n {\n heading: 'Background and goal',\n requirement: 'the problem being solved, for whom, and what outcome counts as success',\n },\n {\n heading: 'Behavior',\n requirement:\n 'every user- or caller-observable behavior, including inputs, outputs, error cases, edge cases, and interactions with existing behavior, stated precisely enough that two implementers would build the same thing',\n },\n {\n heading: 'Acceptance criteria',\n requirement:\n 'checkable conditions (Given/When/Then or an equivalent) that decide whether the implementation is done',\n },\n {\n heading: 'Scope and non-goals',\n requirement: 'what the change covers and what it deliberately leaves out, so an implementer knows where to stop',\n },\n {\n heading: 'Design constraints',\n requirement:\n 'the data, API, schema, UI, and configuration changes, naming, and the existing code or conventions the implementation must fit — as far as the writer intends to constrain them, with everything else explicitly left to the implementer',\n },\n {\n heading: 'Compatibility and migration',\n requirement: 'what existing data, users, callers, or configurations are affected and how the transition happens',\n },\n {\n heading: 'Verification',\n requirement: 'how the change is tested or demonstrated, including what needs no test and why',\n },\n {\n heading: 'Open questions',\n requirement: 'undecided points, each with its options; empty once the specification is implementable',\n },\n];\n\n/** Renders sections as markdown bullets (`- **Heading**: requirement`). */\nexport function renderSectionChecklist(sections: readonly TemplateSection[]): string {\n return sections.map((section) => `- **${section.heading}**: ${section.requirement}`).join('\\n');\n}\n\n/** Renders sections as markdown headings, each followed by its requirement as a placeholder comment. */\nexport function renderSectionTemplate(sections: readonly TemplateSection[]): string {\n return sections\n .map(\n (section) =>\n `## ${section.heading}\\n\\n<!-- ${section.requirement.charAt(0).toUpperCase()}${section.requirement.slice(1)}. -->`\n )\n .join('\\n\\n');\n}\n"],"mappings":"aAWA,MAAaA,EAAoD,CAC/D,CACE,QAAS,MACT,YAAa,sGACf,EACA,CACE,QAAS,eACT,YACE,qnBACJ,EACA,CACE,QAAS,mBACT,YACE,4GACJ,EACA,CACE,QAAS,oBACT,YACE,yKACJ,EACA,CACE,QAAS,UACT,YAAa,+DACf,EACA,CACE,QAAS,QACT,YAAa,sGACf,CACF,EAiBaC,EAAkC,y0DAMlCC,EAA0B;qFAAmCD,IAa7DE,EAAiD,CAC5D,CAAE,QAAS,UAAW,YAAa,8CAA+C,EAClF,CAAE,QAAS,WAAY,YAAa,6CAA8C,EAClF,CAAE,QAAS,SAAU,YAAa,wCAAyC,EAC3E,CAAE,QAAS,WAAY,YAAa,uDAAwD,CAC9F,EAQaC,EAAoD,CAC/D,CACE,QAAS,sBACT,YAAa,wEACf,EACA,CACE,QAAS,WACT,YACE,kNACJ,EACA,CACE,QAAS,sBACT,YACE,wGACJ,EACA,CACE,QAAS,sBACT,YAAa,mGACf,EACA,CACE,QAAS,qBACT,YACE,0OACJ,EACA,CACE,QAAS,8BACT,YAAa,mGACf,EACA,CACE,QAAS,eACT,YAAa,gFACf,EACA,CACE,QAAS,iBACT,YAAa,wFACf,CACF,EAGA,SAAgBC,EAAuB,EAA8C,CACnF,OAAOC,EAAS,IAAK,GAAY,OAAOC,EAAQ,QAAQ,MAAMA,EAAQ,aAAa,CAAC,CAAC,KAAK;CAAI,CAChG,CAGA,SAAgBC,EAAsB,EAA8C,CAClF,OAAOF,EACJ,IACE,GACC,MAAMC,EAAQ,QAAQ,WAAWA,EAAQ,YAAY,OAAO,CAAC,CAAC,CAAC,YAAY,IAAIA,EAAQ,YAAY,MAAM,CAAC,EAAE,MAChH,CAAC,CACA,KAAK;;CAAM,CAChB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"githubTemplates.js","names":[],"sources":["../src/githubTemplates.ts"],"sourcesContent":["/**\n * A section of a PR or issue body: its heading and what the section must state. `requirement`\n * starts lowercase and ends without punctuation, so that it reads both as a placeholder sentence\n * (capitalized, with a period appended) and as a checklist clause after the heading.\n */\nexport interface TemplateSection {\n readonly heading: string;\n readonly requirement: string;\n}\n\n/** Sections of a pull request body, in the order they appear in the PR template. */\nexport const PULL_REQUEST_SECTIONS: readonly TemplateSection[] = [\n {\n heading: 'Why',\n requirement: 'the problem, with the numbers or observations behind it, and why this approach over the alternatives',\n },\n {\n heading: 'Requirements',\n requirement:\n \"the requirements the change must satisfy and the guarantees it must keep as they stand now, one per line, each marked `required` (asked for by the requester, or an existing contract callers depend on) or `chosen` (the implementer's own decision with the reason that decided it, which a simpler design may replace); state the requester's instructions in the requester's terms, whether they came from an issue or a conversation, and keep each line as it is until the requester overrides or withdraws the instruction behind it or the decision behind it changes, and then rewrite or remove that line instead of adding one beside it\",\n },\n {\n heading: 'Customer Summary',\n requirement:\n 'behavior, workflow, or user-visible changes, written for readers who know nothing about the implementation',\n },\n {\n heading: 'Technical Summary',\n requirement:\n 'decisions and their reasons, data flow, where to look (the files that matter most), and what to check hardest: the parts whose correctness is argued rather than proven',\n },\n {\n heading: 'Testing',\n requirement: 'commands run, tests added, and what was NOT exercised and why',\n },\n {\n heading: 'Notes',\n requirement: 'known limitations, compatibility and migration, follow-up work; delete this section if there is none',\n },\n];\n\n/**\n * How a tool that writes or rewrites a PR body derives it from the repository's PR template, as\n * markdown bullets for an agent's instructions. Stated once here and rendered by every writer (the\n * generated agent instructions, the PR-drafting skills) so the writers cannot drift apart; the\n * template-less branch lets the same text serve repositories outside the organization.\n */\nconst PULL_REQUEST_TEMPLATE_RULES = `- Base the PR body on \\`.github/pull_request_template.md\\` when creating or updating a PR, even when a skill or workflow supplies its own skeleton: keep the template's headings in order, fill each section with what its placeholder comment asks for at a length fitting the change (a sentence for a small change, numbered subsections for a large one), and delete the placeholder comments and an empty Notes section. Without a template, use no fixed headings: state the scope, the motivation, and the verification concisely.\n- Start the body with \\`Close #<n>\\` only when the PR resolves an existing issue.`;\n\n/**\n * How a tool that writes or rewrites a PR body fills the Requirements section of\n * `PULL_REQUEST_SECTIONS`, as markdown bullets for an agent's instructions. Skills that draft PR\n * bodies render this text instead of restating it, so the writers and the reviewers that read the\n * section as the boundary a fix must keep never disagree on what a line means.\n */\nexport const PULL_REQUEST_REQUIREMENTS_RULES = `- Requirements section: the requirements in force now, one line per requirement, taken from the requester's instructions in the requester's terms (the issue, the conversation that asked for the change), each marked \\`required\\` (asked for, or an existing contract callers depend on) or \\`chosen\\` (your own decision with the reason that decided it, which a simpler design may replace). Never infer a requirement from the diff: when no issue, PR message, or conversation states the request, write \\`required: not recorded — ask the requester\\` as the only request-derived line (a \\`required\\` line for an existing contract callers depend on may still be listed) and say so in Notes.\n- Current-state rule: an existing Requirements section records what the requester asked for and what was decided as it stands now; it is neither a description of the diff nor a log of how the request evolved. Keep each line as it is until the requester overrides or withdraws the instruction behind it or the decision behind it changes, and then rewrite or remove that line instead of appending a line beside it; with no instructions available, keep the requester-derived lines unchanged. Never mark when a line was added (e.g., \"(added)\"), and add a \\`chosen\\` line for each new decision. When the current design cannot be understood without the change of direction, state it in one sentence in Notes. A section that merely describes the implementation, or holds only the template's placeholder or a \\`required: not recorded\\` line, is not a record: write it afresh by the rule above.\n- Place the section where the template puts it; when the template has no such heading, right after the issue-closing line, or at the top of the body when there is none.\n- Never drop or weaken a \\`required\\` line to fit what was implemented: when one cannot hold in the diff, keep it and say so in Notes.`;\n\n/** The complete rules for writing a PR body: the template rules followed by the Requirements rules. */\nexport const PULL_REQUEST_BODY_RULES = `${PULL_REQUEST_TEMPLATE_RULES}\\n${PULL_REQUEST_REQUIREMENTS_RULES}`;\n\n/**\n * How a tool that creates an issue derives it from the repository's issue templates, as markdown\n * bullets for an agent's instructions; shared by the same writers as `PULL_REQUEST_BODY_RULES`.\n * Guidance the templates state in their own placeholder comments (which sections to keep, deleting\n * the comments) is deliberately not repeated here, since the writer reads the template anyway.\n */\nexport const ISSUE_TEMPLATE_RULES = `- Follow the closest template under \\`.github/ISSUE_TEMPLATE/\\`: \\`bug.md\\` for wrong behavior, \\`change.md\\` for anything to build or alter; a question or note fitting neither, or a repository without templates, needs no template.\n- Title: a Conventional Commits prefix for the type that fits the change (\\`feat:\\`, \\`fix:\\`, \\`refactor:\\`, \\`docs:\\`, \\`chore:\\`, ...); when it differs from the template's \\`title\\` prefix, replace the template's type label (\\`t: ...\\`) with the one matching the type.\n- The YAML front matter between the \\`---\\` lines is metadata, not body text: pass its \\`labels\\` via \\`--label\\` and submit only the content below the closing \\`---\\` as the body.`;\n\n/** Sections of a bug report, in the order they appear in the issue template. */\nexport const BUG_ISSUE_SECTIONS: readonly TemplateSection[] = [\n { heading: 'Problem', requirement: 'what happens, and what should happen instead' },\n { heading: 'Evidence', requirement: 'reproduction steps, logs, run IDs, or links' },\n { heading: 'Impact', requirement: 'who or what is affected, and how badly' },\n { heading: 'Proposal', requirement: 'the fix you have in mind, and the root cause if known' },\n];\n\n/**\n * Sections a change specification must settle before a third party can implement it, defined\n * once so that the issue template and any tool that drafts or reviews specifications against\n * these sections cannot drift apart; checklist consumers render it with `renderSectionChecklist`.\n * The first three sections suffice for a small change, so they come first.\n */\nexport const CHANGE_ISSUE_SECTIONS: readonly TemplateSection[] = [\n {\n heading: 'Background and goal',\n requirement: 'the problem being solved, for whom, and what outcome counts as success',\n },\n {\n heading: 'Behavior',\n requirement:\n 'every user- or caller-observable behavior, including inputs, outputs, error cases, edge cases, and interactions with existing behavior, stated precisely enough that two implementers would build the same thing',\n },\n {\n heading: 'Acceptance criteria',\n requirement:\n 'checkable conditions (Given/When/Then or an equivalent) that decide whether the implementation is done',\n },\n {\n heading: 'Scope and non-goals',\n requirement: 'what the change covers and what it deliberately leaves out, so an implementer knows where to stop',\n },\n {\n heading: 'Design constraints',\n requirement:\n 'the data, API, schema, UI, and configuration changes, naming, and the existing code or conventions the implementation must fit — as far as the writer intends to constrain them, with everything else explicitly left to the implementer',\n },\n {\n heading: 'Compatibility and migration',\n requirement: 'what existing data, users, callers, or configurations are affected and how the transition happens',\n },\n {\n heading: 'Verification',\n requirement: 'how the change is tested or demonstrated, including what needs no test and why',\n },\n {\n heading: 'Open questions',\n requirement: 'undecided points, each with its options; empty once the specification is implementable',\n },\n];\n\n/** Renders sections as markdown bullets (`- **Heading**: requirement`). */\nexport function renderSectionChecklist(sections: readonly TemplateSection[]): string {\n return sections.map((section) => `- **${section.heading}**: ${section.requirement}`).join('\\n');\n}\n\n/** Renders sections as markdown headings, each followed by its requirement as a placeholder comment. */\nexport function renderSectionTemplate(sections: readonly TemplateSection[]): string {\n return sections\n .map(\n (section) =>\n `## ${section.heading}\\n\\n<!-- ${section.requirement.charAt(0).toUpperCase()}${section.requirement.slice(1)}. -->`\n )\n .join('\\n\\n');\n}\n"],"mappings":"AAWA,MAAa,EAAoD,CAC/D,CACE,QAAS,MACT,YAAa,sGACf,EACA,CACE,QAAS,eACT,YACE,qnBACJ,EACA,CACE,QAAS,mBACT,YACE,4GACJ,EACA,CACE,QAAS,oBACT,YACE,yKACJ,EACA,CACE,QAAS,UACT,YAAa,+DACf,EACA,CACE,QAAS,QACT,YAAa,sGACf,CACF,EAiBa,EAAkC,y0DAMlC,EAA0B;qFAAmC,IAQ7D,EAAuB,qpBAKvB,EAAiD,CAC5D,CAAE,QAAS,UAAW,YAAa,8CAA+C,EAClF,CAAE,QAAS,WAAY,YAAa,6CAA8C,EAClF,CAAE,QAAS,SAAU,YAAa,wCAAyC,EAC3E,CAAE,QAAS,WAAY,YAAa,uDAAwD,CAC9F,EAQa,EAAoD,CAC/D,CACE,QAAS,sBACT,YAAa,wEACf,EACA,CACE,QAAS,WACT,YACE,kNACJ,EACA,CACE,QAAS,sBACT,YACE,wGACJ,EACA,CACE,QAAS,sBACT,YAAa,mGACf,EACA,CACE,QAAS,qBACT,YACE,0OACJ,EACA,CACE,QAAS,8BACT,YAAa,mGACf,EACA,CACE,QAAS,eACT,YAAa,gFACf,EACA,CACE,QAAS,iBACT,YAAa,wFACf,CACF,EAGA,SAAgB,EAAuB,EAA8C,CACnF,OAAO,EAAS,IAAK,GAAY,OAAO,EAAQ,QAAQ,MAAM,EAAQ,aAAa,CAAC,CAAC,KAAK;CAAI,CAChG,CAGA,SAAgB,EAAsB,EAA8C,CAClF,OAAO,EACJ,IACE,GACC,MAAM,EAAQ,QAAQ,WAAW,EAAQ,YAAY,OAAO,CAAC,CAAC,CAAC,YAAY,IAAI,EAAQ,YAAY,MAAM,CAAC,EAAE,MAChH,CAAC,CACA,KAAK;;CAAM,CAChB"}
|
|
1
|
+
{"version":3,"file":"githubTemplates.js","names":["PULL_REQUEST_SECTIONS","PULL_REQUEST_REQUIREMENTS_RULES","PULL_REQUEST_BODY_RULES","ISSUE_TEMPLATE_RULES","BUG_ISSUE_SECTIONS","CHANGE_ISSUE_SECTIONS","renderSectionChecklist","sections","section","renderSectionTemplate"],"sources":["../src/githubTemplates.ts"],"sourcesContent":["/**\n * A section of a PR or issue body: its heading and what the section must state. `requirement`\n * starts lowercase and ends without punctuation, so that it reads both as a placeholder sentence\n * (capitalized, with a period appended) and as a checklist clause after the heading.\n */\nexport interface TemplateSection {\n readonly heading: string;\n readonly requirement: string;\n}\n\n/** Sections of a pull request body, in the order they appear in the PR template. */\nexport const PULL_REQUEST_SECTIONS: readonly TemplateSection[] = [\n {\n heading: 'Why',\n requirement: 'the problem, with the numbers or observations behind it, and why this approach over the alternatives',\n },\n {\n heading: 'Requirements',\n requirement:\n \"the requirements the change must satisfy and the guarantees it must keep as they stand now, one per line, each marked `required` (asked for by the requester, or an existing contract callers depend on) or `chosen` (the implementer's own decision with the reason that decided it, which a simpler design may replace); state the requester's instructions in the requester's terms, whether they came from an issue or a conversation, and keep each line as it is until the requester overrides or withdraws the instruction behind it or the decision behind it changes, and then rewrite or remove that line instead of adding one beside it\",\n },\n {\n heading: 'Customer Summary',\n requirement:\n 'behavior, workflow, or user-visible changes, written for readers who know nothing about the implementation',\n },\n {\n heading: 'Technical Summary',\n requirement:\n 'decisions and their reasons, data flow, where to look (the files that matter most), and what to check hardest: the parts whose correctness is argued rather than proven',\n },\n {\n heading: 'Testing',\n requirement: 'commands run, tests added, and what was NOT exercised and why',\n },\n {\n heading: 'Notes',\n requirement: 'known limitations, compatibility and migration, follow-up work; delete this section if there is none',\n },\n];\n\n/**\n * How a tool that writes or rewrites a PR body derives it from the repository's PR template, as\n * markdown bullets for an agent's instructions. Stated once here and rendered by every writer (the\n * generated agent instructions, the PR-drafting skills) so the writers cannot drift apart; the\n * template-less branch lets the same text serve repositories outside the organization.\n */\nconst PULL_REQUEST_TEMPLATE_RULES = `- Base the PR body on \\`.github/pull_request_template.md\\` when creating or updating a PR, even when a skill or workflow supplies its own skeleton: keep the template's headings in order, fill each section with what its placeholder comment asks for at a length fitting the change (a sentence for a small change, numbered subsections for a large one), and delete the placeholder comments and an empty Notes section. Without a template, use no fixed headings: state the scope, the motivation, and the verification concisely.\n- Start the body with \\`Close #<n>\\` only when the PR resolves an existing issue.`;\n\n/**\n * How a tool that writes or rewrites a PR body fills the Requirements section of\n * `PULL_REQUEST_SECTIONS`, as markdown bullets for an agent's instructions. Skills that draft PR\n * bodies render this text instead of restating it, so the writers and the reviewers that read the\n * section as the boundary a fix must keep never disagree on what a line means.\n */\nexport const PULL_REQUEST_REQUIREMENTS_RULES = `- Requirements section: the requirements in force now, one line per requirement, taken from the requester's instructions in the requester's terms (the issue, the conversation that asked for the change), each marked \\`required\\` (asked for, or an existing contract callers depend on) or \\`chosen\\` (your own decision with the reason that decided it, which a simpler design may replace). Never infer a requirement from the diff: when no issue, PR message, or conversation states the request, write \\`required: not recorded — ask the requester\\` as the only request-derived line (a \\`required\\` line for an existing contract callers depend on may still be listed) and say so in Notes.\n- Current-state rule: an existing Requirements section records what the requester asked for and what was decided as it stands now; it is neither a description of the diff nor a log of how the request evolved. Keep each line as it is until the requester overrides or withdraws the instruction behind it or the decision behind it changes, and then rewrite or remove that line instead of appending a line beside it; with no instructions available, keep the requester-derived lines unchanged. Never mark when a line was added (e.g., \"(added)\"), and add a \\`chosen\\` line for each new decision. When the current design cannot be understood without the change of direction, state it in one sentence in Notes. A section that merely describes the implementation, or holds only the template's placeholder or a \\`required: not recorded\\` line, is not a record: write it afresh by the rule above.\n- Place the section where the template puts it; when the template has no such heading, right after the issue-closing line, or at the top of the body when there is none.\n- Never drop or weaken a \\`required\\` line to fit what was implemented: when one cannot hold in the diff, keep it and say so in Notes.`;\n\n/** The complete rules for writing a PR body: the template rules followed by the Requirements rules. */\nexport const PULL_REQUEST_BODY_RULES = `${PULL_REQUEST_TEMPLATE_RULES}\\n${PULL_REQUEST_REQUIREMENTS_RULES}`;\n\n/**\n * How a tool that creates an issue derives it from the repository's issue templates, as markdown\n * bullets for an agent's instructions; shared by the same writers as `PULL_REQUEST_BODY_RULES`.\n * Guidance the templates state in their own placeholder comments (which sections to keep, deleting\n * the comments) is deliberately not repeated here, since the writer reads the template anyway.\n */\nexport const ISSUE_TEMPLATE_RULES = `- Follow the closest template under \\`.github/ISSUE_TEMPLATE/\\`: \\`bug.md\\` for wrong behavior, \\`change.md\\` for anything to build or alter; a question or note fitting neither, or a repository without templates, needs no template.\n- Title: a Conventional Commits prefix for the type that fits the change (\\`feat:\\`, \\`fix:\\`, \\`refactor:\\`, \\`docs:\\`, \\`chore:\\`, ...); when it differs from the template's \\`title\\` prefix, replace the template's type label (\\`t: ...\\`) with the one matching the type.\n- The YAML front matter between the \\`---\\` lines is metadata, not body text: pass its \\`labels\\` via \\`--label\\` and submit only the content below the closing \\`---\\` as the body.`;\n\n/** Sections of a bug report, in the order they appear in the issue template. */\nexport const BUG_ISSUE_SECTIONS: readonly TemplateSection[] = [\n { heading: 'Problem', requirement: 'what happens, and what should happen instead' },\n { heading: 'Evidence', requirement: 'reproduction steps, logs, run IDs, or links' },\n { heading: 'Impact', requirement: 'who or what is affected, and how badly' },\n { heading: 'Proposal', requirement: 'the fix you have in mind, and the root cause if known' },\n];\n\n/**\n * Sections a change specification must settle before a third party can implement it, defined\n * once so that the issue template and any tool that drafts or reviews specifications against\n * these sections cannot drift apart; checklist consumers render it with `renderSectionChecklist`.\n * The first three sections suffice for a small change, so they come first.\n */\nexport const CHANGE_ISSUE_SECTIONS: readonly TemplateSection[] = [\n {\n heading: 'Background and goal',\n requirement: 'the problem being solved, for whom, and what outcome counts as success',\n },\n {\n heading: 'Behavior',\n requirement:\n 'every user- or caller-observable behavior, including inputs, outputs, error cases, edge cases, and interactions with existing behavior, stated precisely enough that two implementers would build the same thing',\n },\n {\n heading: 'Acceptance criteria',\n requirement:\n 'checkable conditions (Given/When/Then or an equivalent) that decide whether the implementation is done',\n },\n {\n heading: 'Scope and non-goals',\n requirement: 'what the change covers and what it deliberately leaves out, so an implementer knows where to stop',\n },\n {\n heading: 'Design constraints',\n requirement:\n 'the data, API, schema, UI, and configuration changes, naming, and the existing code or conventions the implementation must fit — as far as the writer intends to constrain them, with everything else explicitly left to the implementer',\n },\n {\n heading: 'Compatibility and migration',\n requirement: 'what existing data, users, callers, or configurations are affected and how the transition happens',\n },\n {\n heading: 'Verification',\n requirement: 'how the change is tested or demonstrated, including what needs no test and why',\n },\n {\n heading: 'Open questions',\n requirement: 'undecided points, each with its options; empty once the specification is implementable',\n },\n];\n\n/** Renders sections as markdown bullets (`- **Heading**: requirement`). */\nexport function renderSectionChecklist(sections: readonly TemplateSection[]): string {\n return sections.map((section) => `- **${section.heading}**: ${section.requirement}`).join('\\n');\n}\n\n/** Renders sections as markdown headings, each followed by its requirement as a placeholder comment. */\nexport function renderSectionTemplate(sections: readonly TemplateSection[]): string {\n return sections\n .map(\n (section) =>\n `## ${section.heading}\\n\\n<!-- ${section.requirement.charAt(0).toUpperCase()}${section.requirement.slice(1)}. -->`\n )\n .join('\\n\\n');\n}\n"],"mappings":"AAWA,MAAaA,EAAoD,CAC/D,CACE,QAAS,MACT,YAAa,sGACf,EACA,CACE,QAAS,eACT,YACE,qnBACJ,EACA,CACE,QAAS,mBACT,YACE,4GACJ,EACA,CACE,QAAS,oBACT,YACE,yKACJ,EACA,CACE,QAAS,UACT,YAAa,+DACf,EACA,CACE,QAAS,QACT,YAAa,sGACf,CACF,EAiBaC,EAAkC,y0DAMlCC,EAA0B;qFAAmCD,IAQ7DE,EAAuB,qpBAKvBC,EAAiD,CAC5D,CAAE,QAAS,UAAW,YAAa,8CAA+C,EAClF,CAAE,QAAS,WAAY,YAAa,6CAA8C,EAClF,CAAE,QAAS,SAAU,YAAa,wCAAyC,EAC3E,CAAE,QAAS,WAAY,YAAa,uDAAwD,CAC9F,EAQaC,EAAoD,CAC/D,CACE,QAAS,sBACT,YAAa,wEACf,EACA,CACE,QAAS,WACT,YACE,kNACJ,EACA,CACE,QAAS,sBACT,YACE,wGACJ,EACA,CACE,QAAS,sBACT,YAAa,mGACf,EACA,CACE,QAAS,qBACT,YACE,0OACJ,EACA,CACE,QAAS,8BACT,YAAa,mGACf,EACA,CACE,QAAS,eACT,YAAa,gFACf,EACA,CACE,QAAS,iBACT,YAAa,wFACf,CACF,EAGA,SAAgBC,EAAuB,EAA8C,CACnF,OAAOC,EAAS,IAAK,GAAY,OAAOC,EAAQ,QAAQ,MAAMA,EAAQ,aAAa,CAAC,CAAC,KAAK;CAAI,CAChG,CAGA,SAAgBC,EAAsB,EAA8C,CAClF,OAAOF,EACJ,IACE,GACC,MAAMC,EAAQ,QAAQ,WAAWA,EAAQ,YAAY,OAAO,CAAC,CAAC,CAAC,YAAY,IAAIA,EAAQ,YAAY,MAAM,CAAC,EAAE,MAChH,CAAC,CACA,KAAK;;CAAM,CAChB"}
|
package/dist/http.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.cjs","names":[],"sources":["../src/http.ts"],"sourcesContent":["/**\n * Extracts the token from an `Authorization` header value of the Bearer scheme (RFC 6750), whose scheme name is\n * case-insensitive. Returns undefined for a missing header, another scheme, or a malformed value.\n */\nexport function parseBearerToken(authorization: string | null | undefined): string | undefined {\n if (!authorization) return undefined;\n // The scheme is compared after lowercasing instead of with the `i` flag, which together with `u` would let the token\n // class match non-ASCII case variants such as U+212A KELVIN SIGN for `k`.\n const match = /^(\\S+) +([\\w+./~-]+=*) *$/u.exec(authorization);\n return match?.[1]?.toLowerCase() === 'bearer' ? match[2] : undefined;\n}\n\n/** Returns a text field of `FormData`, or undefined when the field is missing or holds a file. */\nexport function getFormString(formData: FormData, name: string): string | undefined {\n const value = formData.get(name);\n return typeof value === 'string' ? value : undefined;\n}\n"],"mappings":"aAIA,
|
|
1
|
+
{"version":3,"file":"http.cjs","names":["parseBearerToken","authorization","match","getFormString","value","formData","name"],"sources":["../src/http.ts"],"sourcesContent":["/**\n * Extracts the token from an `Authorization` header value of the Bearer scheme (RFC 6750), whose scheme name is\n * case-insensitive. Returns undefined for a missing header, another scheme, or a malformed value.\n */\nexport function parseBearerToken(authorization: string | null | undefined): string | undefined {\n if (!authorization) return undefined;\n // The scheme is compared after lowercasing instead of with the `i` flag, which together with `u` would let the token\n // class match non-ASCII case variants such as U+212A KELVIN SIGN for `k`.\n const match = /^(\\S+) +([\\w+./~-]+=*) *$/u.exec(authorization);\n return match?.[1]?.toLowerCase() === 'bearer' ? match[2] : undefined;\n}\n\n/** Returns a text field of `FormData`, or undefined when the field is missing or holds a file. */\nexport function getFormString(formData: FormData, name: string): string | undefined {\n const value = formData.get(name);\n return typeof value === 'string' ? value : undefined;\n}\n"],"mappings":"aAIA,SAAgBA,EAAiB,EAA8D,CAC7F,GAAI,CAACC,EAAe,OAGpB,IAAMC,EAAQ,6BAA6B,KAAKD,CAAa,EAC7D,OAAOC,IAAQ,EAAE,EAAE,YAAY,IAAM,SAAWA,EAAM,GAAK,IAAA,EAC7D,CAGA,SAAgBC,EAAc,EAAoB,EAAkC,CAClF,IAAMC,EAAQC,EAAS,IAAIC,CAAI,EAC/B,OAAO,OAAOF,GAAU,SAAWA,EAAQ,IAAA,EAC7C"}
|
package/dist/http.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.js","names":[],"sources":["../src/http.ts"],"sourcesContent":["/**\n * Extracts the token from an `Authorization` header value of the Bearer scheme (RFC 6750), whose scheme name is\n * case-insensitive. Returns undefined for a missing header, another scheme, or a malformed value.\n */\nexport function parseBearerToken(authorization: string | null | undefined): string | undefined {\n if (!authorization) return undefined;\n // The scheme is compared after lowercasing instead of with the `i` flag, which together with `u` would let the token\n // class match non-ASCII case variants such as U+212A KELVIN SIGN for `k`.\n const match = /^(\\S+) +([\\w+./~-]+=*) *$/u.exec(authorization);\n return match?.[1]?.toLowerCase() === 'bearer' ? match[2] : undefined;\n}\n\n/** Returns a text field of `FormData`, or undefined when the field is missing or holds a file. */\nexport function getFormString(formData: FormData, name: string): string | undefined {\n const value = formData.get(name);\n return typeof value === 'string' ? value : undefined;\n}\n"],"mappings":"AAIA,
|
|
1
|
+
{"version":3,"file":"http.js","names":["parseBearerToken","authorization","match","getFormString","value","formData","name"],"sources":["../src/http.ts"],"sourcesContent":["/**\n * Extracts the token from an `Authorization` header value of the Bearer scheme (RFC 6750), whose scheme name is\n * case-insensitive. Returns undefined for a missing header, another scheme, or a malformed value.\n */\nexport function parseBearerToken(authorization: string | null | undefined): string | undefined {\n if (!authorization) return undefined;\n // The scheme is compared after lowercasing instead of with the `i` flag, which together with `u` would let the token\n // class match non-ASCII case variants such as U+212A KELVIN SIGN for `k`.\n const match = /^(\\S+) +([\\w+./~-]+=*) *$/u.exec(authorization);\n return match?.[1]?.toLowerCase() === 'bearer' ? match[2] : undefined;\n}\n\n/** Returns a text field of `FormData`, or undefined when the field is missing or holds a file. */\nexport function getFormString(formData: FormData, name: string): string | undefined {\n const value = formData.get(name);\n return typeof value === 'string' ? value : undefined;\n}\n"],"mappings":"AAIA,SAAgBA,EAAiB,EAA8D,CAC7F,GAAI,CAACC,EAAe,OAGpB,IAAMC,EAAQ,6BAA6B,KAAKD,CAAa,EAC7D,OAAOC,IAAQ,EAAE,EAAE,YAAY,IAAM,SAAWA,EAAM,GAAK,IAAA,EAC7D,CAGA,SAAgBC,EAAc,EAAoB,EAAkC,CAClF,IAAMC,EAAQC,EAAS,IAAIC,CAAI,EAC/B,OAAO,OAAOF,GAAU,SAAWA,EAAQ,IAAA,EAC7C"}
|
package/dist/humanize.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"humanize.cjs","names":[],"sources":["../src/humanize.ts"],"sourcesContent":["export function humanizeNumber(value: number, { base = 1000, units = ['K', 'M', 'B', 'T', 'P'] } = {}): string {\n if (value < base) {\n return value.toString();\n }\n\n let unitIndex = -1;\n while (value >= base && unitIndex < units.length - 1) {\n value /= base;\n unitIndex++;\n }\n\n return value.toFixed(2) + (units[unitIndex] ?? '');\n}\n\n/**\n * Formats a duration in Japanese: to a tenth of a second under a minute (`59.9秒`), otherwise in whole minutes and\n * seconds (`1分0秒`).\n */\nexport function formatElapsedTimeInJapanese(milliseconds: number): string {\n // Round to the displayed precision before choosing the format, or 59.98s would be shown as `60.0秒`.\n const roundedTenths = Math.round(milliseconds / 100) / 10;\n if (roundedTenths < 60) return `${roundedTenths.toFixed(1)}秒`;\n\n // Round before splitting into minutes and seconds, or 119.8s would be shown as `1分60秒`.\n const roundedSeconds = Math.round(milliseconds / 1000);\n return `${Math.floor(roundedSeconds / 60)}分${roundedSeconds % 60}秒`;\n}\n\n/**\n * Formats a duration compactly in English: whole milliseconds under a second (`850ms`), a tenth of a second under a\n * minute (`12.3s`), and whole seconds above (`1m 5s`, `2h 0m 7s`).\n */\nexport function formatDuration(milliseconds: number): string {\n // Each branch rounds to its displayed precision before choosing the format, e.g. 59.96s is shown as `1m 0s`.\n const roundedMilliseconds = Math.round(milliseconds);\n if (roundedMilliseconds < 1000) return `${roundedMilliseconds}ms`;\n const roundedTenths = Math.round(milliseconds / 100) / 10;\n if (roundedTenths < 60) return `${roundedTenths.toFixed(1)}s`;\n const totalSeconds = Math.round(milliseconds / 1000);\n const hours = Math.floor(totalSeconds / 3600);\n const minutesAndSeconds = `${Math.floor(totalSeconds / 60) % 60}m ${totalSeconds % 60}s`;\n return hours > 0 ? `${hours}h ${minutesAndSeconds}` : minutesAndSeconds;\n}\n"],"mappings":"aAAA,
|
|
1
|
+
{"version":3,"file":"humanize.cjs","names":["humanizeNumber","value","base","unitIndex","units","formatElapsedTimeInJapanese","roundedTenths","milliseconds","roundedSeconds","formatDuration","roundedMilliseconds","totalSeconds","hours","minutesAndSeconds"],"sources":["../src/humanize.ts"],"sourcesContent":["export function humanizeNumber(value: number, { base = 1000, units = ['K', 'M', 'B', 'T', 'P'] } = {}): string {\n if (value < base) {\n return value.toString();\n }\n\n let unitIndex = -1;\n while (value >= base && unitIndex < units.length - 1) {\n value /= base;\n unitIndex++;\n }\n\n return value.toFixed(2) + (units[unitIndex] ?? '');\n}\n\n/**\n * Formats a duration in Japanese: to a tenth of a second under a minute (`59.9秒`), otherwise in whole minutes and\n * seconds (`1分0秒`).\n */\nexport function formatElapsedTimeInJapanese(milliseconds: number): string {\n // Round to the displayed precision before choosing the format, or 59.98s would be shown as `60.0秒`.\n const roundedTenths = Math.round(milliseconds / 100) / 10;\n if (roundedTenths < 60) return `${roundedTenths.toFixed(1)}秒`;\n\n // Round before splitting into minutes and seconds, or 119.8s would be shown as `1分60秒`.\n const roundedSeconds = Math.round(milliseconds / 1000);\n return `${Math.floor(roundedSeconds / 60)}分${roundedSeconds % 60}秒`;\n}\n\n/**\n * Formats a duration compactly in English: whole milliseconds under a second (`850ms`), a tenth of a second under a\n * minute (`12.3s`), and whole seconds above (`1m 5s`, `2h 0m 7s`).\n */\nexport function formatDuration(milliseconds: number): string {\n // Each branch rounds to its displayed precision before choosing the format, e.g. 59.96s is shown as `1m 0s`.\n const roundedMilliseconds = Math.round(milliseconds);\n if (roundedMilliseconds < 1000) return `${roundedMilliseconds}ms`;\n const roundedTenths = Math.round(milliseconds / 100) / 10;\n if (roundedTenths < 60) return `${roundedTenths.toFixed(1)}s`;\n const totalSeconds = Math.round(milliseconds / 1000);\n const hours = Math.floor(totalSeconds / 3600);\n const minutesAndSeconds = `${Math.floor(totalSeconds / 60) % 60}m ${totalSeconds % 60}s`;\n return hours > 0 ? `${hours}h ${minutesAndSeconds}` : minutesAndSeconds;\n}\n"],"mappings":"aAAA,SAAgBA,EAAe,EAAe,CAAE,OAAO,IAAM,QAAQ,CAAC,IAAK,IAAK,IAAK,IAAK,GAAG,GAAM,CAAC,EAAW,CAC7G,GAAIC,EAAQC,EACV,OAAOD,EAAM,SAAS,EAGxB,IAAIE,EAAY,GAChB,KAAOF,GAASC,GAAQC,EAAYC,EAAM,OAAS,GACjD,GAASF,EACT,IAGF,OAAOD,EAAM,QAAQ,CAAC,GAAKG,EAAMD,IAAc,GACjD,CAMA,SAAgBE,EAA4B,EAA8B,CAExE,IAAMC,EAAgB,KAAK,MAAMC,EAAe,GAAG,EAAI,GACvD,GAAID,EAAgB,GAAI,MAAO,GAAGA,EAAc,QAAQ,CAAC,EAAE,GAG3D,IAAME,EAAiB,KAAK,MAAMD,EAAe,GAAI,EACrD,MAAO,GAAG,KAAK,MAAMC,EAAiB,EAAE,EAAE,GAAGA,EAAiB,GAAG,EACnE,CAMA,SAAgBC,EAAe,EAA8B,CAE3D,IAAMC,EAAsB,KAAK,MAAMH,CAAY,EACnD,GAAIG,EAAsB,IAAM,MAAO,GAAGA,EAAoB,IAC9D,IAAMJ,EAAgB,KAAK,MAAMC,EAAe,GAAG,EAAI,GACvD,GAAID,EAAgB,GAAI,MAAO,GAAGA,EAAc,QAAQ,CAAC,EAAE,GAC3D,IAAMK,EAAe,KAAK,MAAMJ,EAAe,GAAI,EAC7CK,EAAQ,KAAK,MAAMD,EAAe,IAAI,EACtCE,EAAoB,GAAG,KAAK,MAAMF,EAAe,EAAE,EAAI,GAAG,IAAIA,EAAe,GAAG,GACtF,OAAOC,EAAQ,EAAI,GAAGA,EAAM,IAAIC,IAAsBA,CACxD"}
|
package/dist/humanize.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"humanize.js","names":[],"sources":["../src/humanize.ts"],"sourcesContent":["export function humanizeNumber(value: number, { base = 1000, units = ['K', 'M', 'B', 'T', 'P'] } = {}): string {\n if (value < base) {\n return value.toString();\n }\n\n let unitIndex = -1;\n while (value >= base && unitIndex < units.length - 1) {\n value /= base;\n unitIndex++;\n }\n\n return value.toFixed(2) + (units[unitIndex] ?? '');\n}\n\n/**\n * Formats a duration in Japanese: to a tenth of a second under a minute (`59.9秒`), otherwise in whole minutes and\n * seconds (`1分0秒`).\n */\nexport function formatElapsedTimeInJapanese(milliseconds: number): string {\n // Round to the displayed precision before choosing the format, or 59.98s would be shown as `60.0秒`.\n const roundedTenths = Math.round(milliseconds / 100) / 10;\n if (roundedTenths < 60) return `${roundedTenths.toFixed(1)}秒`;\n\n // Round before splitting into minutes and seconds, or 119.8s would be shown as `1分60秒`.\n const roundedSeconds = Math.round(milliseconds / 1000);\n return `${Math.floor(roundedSeconds / 60)}分${roundedSeconds % 60}秒`;\n}\n\n/**\n * Formats a duration compactly in English: whole milliseconds under a second (`850ms`), a tenth of a second under a\n * minute (`12.3s`), and whole seconds above (`1m 5s`, `2h 0m 7s`).\n */\nexport function formatDuration(milliseconds: number): string {\n // Each branch rounds to its displayed precision before choosing the format, e.g. 59.96s is shown as `1m 0s`.\n const roundedMilliseconds = Math.round(milliseconds);\n if (roundedMilliseconds < 1000) return `${roundedMilliseconds}ms`;\n const roundedTenths = Math.round(milliseconds / 100) / 10;\n if (roundedTenths < 60) return `${roundedTenths.toFixed(1)}s`;\n const totalSeconds = Math.round(milliseconds / 1000);\n const hours = Math.floor(totalSeconds / 3600);\n const minutesAndSeconds = `${Math.floor(totalSeconds / 60) % 60}m ${totalSeconds % 60}s`;\n return hours > 0 ? `${hours}h ${minutesAndSeconds}` : minutesAndSeconds;\n}\n"],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"humanize.js","names":["humanizeNumber","value","base","unitIndex","units","formatElapsedTimeInJapanese","roundedTenths","milliseconds","roundedSeconds","formatDuration","roundedMilliseconds","totalSeconds","hours","minutesAndSeconds"],"sources":["../src/humanize.ts"],"sourcesContent":["export function humanizeNumber(value: number, { base = 1000, units = ['K', 'M', 'B', 'T', 'P'] } = {}): string {\n if (value < base) {\n return value.toString();\n }\n\n let unitIndex = -1;\n while (value >= base && unitIndex < units.length - 1) {\n value /= base;\n unitIndex++;\n }\n\n return value.toFixed(2) + (units[unitIndex] ?? '');\n}\n\n/**\n * Formats a duration in Japanese: to a tenth of a second under a minute (`59.9秒`), otherwise in whole minutes and\n * seconds (`1分0秒`).\n */\nexport function formatElapsedTimeInJapanese(milliseconds: number): string {\n // Round to the displayed precision before choosing the format, or 59.98s would be shown as `60.0秒`.\n const roundedTenths = Math.round(milliseconds / 100) / 10;\n if (roundedTenths < 60) return `${roundedTenths.toFixed(1)}秒`;\n\n // Round before splitting into minutes and seconds, or 119.8s would be shown as `1分60秒`.\n const roundedSeconds = Math.round(milliseconds / 1000);\n return `${Math.floor(roundedSeconds / 60)}分${roundedSeconds % 60}秒`;\n}\n\n/**\n * Formats a duration compactly in English: whole milliseconds under a second (`850ms`), a tenth of a second under a\n * minute (`12.3s`), and whole seconds above (`1m 5s`, `2h 0m 7s`).\n */\nexport function formatDuration(milliseconds: number): string {\n // Each branch rounds to its displayed precision before choosing the format, e.g. 59.96s is shown as `1m 0s`.\n const roundedMilliseconds = Math.round(milliseconds);\n if (roundedMilliseconds < 1000) return `${roundedMilliseconds}ms`;\n const roundedTenths = Math.round(milliseconds / 100) / 10;\n if (roundedTenths < 60) return `${roundedTenths.toFixed(1)}s`;\n const totalSeconds = Math.round(milliseconds / 1000);\n const hours = Math.floor(totalSeconds / 3600);\n const minutesAndSeconds = `${Math.floor(totalSeconds / 60) % 60}m ${totalSeconds % 60}s`;\n return hours > 0 ? `${hours}h ${minutesAndSeconds}` : minutesAndSeconds;\n}\n"],"mappings":"AAAA,SAAgBA,EAAe,EAAe,CAAE,OAAO,IAAM,QAAQ,CAAC,IAAK,IAAK,IAAK,IAAK,GAAG,GAAM,CAAC,EAAW,CAC7G,GAAIC,EAAQC,EACV,OAAOD,EAAM,SAAS,EAGxB,IAAIE,EAAY,GAChB,KAAOF,GAASC,GAAQC,EAAYC,EAAM,OAAS,GACjD,GAASF,EACT,IAGF,OAAOD,EAAM,QAAQ,CAAC,GAAKG,EAAMD,IAAc,GACjD,CAMA,SAAgBE,EAA4B,EAA8B,CAExE,IAAMC,EAAgB,KAAK,MAAMC,EAAe,GAAG,EAAI,GACvD,GAAID,EAAgB,GAAI,MAAO,GAAGA,EAAc,QAAQ,CAAC,EAAE,GAG3D,IAAME,EAAiB,KAAK,MAAMD,EAAe,GAAI,EACrD,MAAO,GAAG,KAAK,MAAMC,EAAiB,EAAE,EAAE,GAAGA,EAAiB,GAAG,EACnE,CAMA,SAAgBC,EAAe,EAA8B,CAE3D,IAAMC,EAAsB,KAAK,MAAMH,CAAY,EACnD,GAAIG,EAAsB,IAAM,MAAO,GAAGA,EAAoB,IAC9D,IAAMJ,EAAgB,KAAK,MAAMC,EAAe,GAAG,EAAI,GACvD,GAAID,EAAgB,GAAI,MAAO,GAAGA,EAAc,QAAQ,CAAC,EAAE,GAC3D,IAAMK,EAAe,KAAK,MAAMJ,EAAe,GAAI,EAC7CK,EAAQ,KAAK,MAAMD,EAAe,IAAI,EACtCE,EAAoB,GAAG,KAAK,MAAMF,EAAe,EAAE,EAAI,GAAG,IAAIA,EAAe,GAAG,GACtF,OAAOC,EAAQ,EAAI,GAAGA,EAAM,IAAIC,IAAsBA,CACxD"}
|
package/dist/mail.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mail.cjs","names":[],"sources":["../src/mail.ts"],"sourcesContent":["export interface MailData {\n from: string;\n subject: string;\n text: string;\n}\n\nclass MailTemplates {\n resetPassword(appName: string, resetUrl: string, expirationDuration?: string): MailData {\n return {\n from: 'no-reply@willbooster.dev',\n subject: 'パスワード再設定のご案内',\n text: `\n${appName} をご利用いただき誠にありがとうございます。\n\nお客様からパスワード再設定のリクエストを受け取りました。\n以下のURLをクリックして、新しいパスワードを設定してください。\n\n${resetUrl}\n${\n expirationDuration\n ? `\n上記のURLは発行から約${expirationDuration}で失効します。\n期限を過ぎてしまった場合や、URLが機能しない場合は、\n恐れ入りますが、再度パスワードを再設定してください。\n`\n : ''\n}\nなお、本メールアドレスは配信専用ですので、返信いただけません。\n\n引き続き ${appName} をよろしくお願い申し上げます。\n `.trim(),\n };\n }\n}\n\nexport const mailTemplates = new MailTemplates();\n"],"mappings":"aAmCA,
|
|
1
|
+
{"version":3,"file":"mail.cjs","names":["mailTemplates","appName","resetUrl","expirationDuration"],"sources":["../src/mail.ts"],"sourcesContent":["export interface MailData {\n from: string;\n subject: string;\n text: string;\n}\n\nclass MailTemplates {\n resetPassword(appName: string, resetUrl: string, expirationDuration?: string): MailData {\n return {\n from: 'no-reply@willbooster.dev',\n subject: 'パスワード再設定のご案内',\n text: `\n${appName} をご利用いただき誠にありがとうございます。\n\nお客様からパスワード再設定のリクエストを受け取りました。\n以下のURLをクリックして、新しいパスワードを設定してください。\n\n${resetUrl}\n${\n expirationDuration\n ? `\n上記のURLは発行から約${expirationDuration}で失効します。\n期限を過ぎてしまった場合や、URLが機能しない場合は、\n恐れ入りますが、再度パスワードを再設定してください。\n`\n : ''\n}\nなお、本メールアドレスは配信専用ですので、返信いただけません。\n\n引き続き ${appName} をよろしくお願い申し上げます。\n `.trim(),\n };\n }\n}\n\nexport const mailTemplates = new MailTemplates();\n"],"mappings":"aAmCA,MAAaA,EAAgB,IAAI,KA7Bb,CAClB,cAAc,EAAiB,EAAkB,EAAuC,CACtF,MAAO,CACL,KAAM,2BACN,QAAS,eACT,KAAM;EACVC,EAAQ;;;;;EAKRC,EAAS;EAETC,EACI;cACQA,EAAmB;;;EAI3B,GACL;;;OAGMF,EAAQ;MACT,KAAK,CACP,CACF,CACF"}
|
package/dist/mail.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mail.js","names":[],"sources":["../src/mail.ts"],"sourcesContent":["export interface MailData {\n from: string;\n subject: string;\n text: string;\n}\n\nclass MailTemplates {\n resetPassword(appName: string, resetUrl: string, expirationDuration?: string): MailData {\n return {\n from: 'no-reply@willbooster.dev',\n subject: 'パスワード再設定のご案内',\n text: `\n${appName} をご利用いただき誠にありがとうございます。\n\nお客様からパスワード再設定のリクエストを受け取りました。\n以下のURLをクリックして、新しいパスワードを設定してください。\n\n${resetUrl}\n${\n expirationDuration\n ? `\n上記のURLは発行から約${expirationDuration}で失効します。\n期限を過ぎてしまった場合や、URLが機能しない場合は、\n恐れ入りますが、再度パスワードを再設定してください。\n`\n : ''\n}\nなお、本メールアドレスは配信専用ですので、返信いただけません。\n\n引き続き ${appName} をよろしくお願い申し上げます。\n `.trim(),\n };\n }\n}\n\nexport const mailTemplates = new MailTemplates();\n"],"mappings":"AAmCA,
|
|
1
|
+
{"version":3,"file":"mail.js","names":["mailTemplates","appName","resetUrl","expirationDuration"],"sources":["../src/mail.ts"],"sourcesContent":["export interface MailData {\n from: string;\n subject: string;\n text: string;\n}\n\nclass MailTemplates {\n resetPassword(appName: string, resetUrl: string, expirationDuration?: string): MailData {\n return {\n from: 'no-reply@willbooster.dev',\n subject: 'パスワード再設定のご案内',\n text: `\n${appName} をご利用いただき誠にありがとうございます。\n\nお客様からパスワード再設定のリクエストを受け取りました。\n以下のURLをクリックして、新しいパスワードを設定してください。\n\n${resetUrl}\n${\n expirationDuration\n ? `\n上記のURLは発行から約${expirationDuration}で失効します。\n期限を過ぎてしまった場合や、URLが機能しない場合は、\n恐れ入りますが、再度パスワードを再設定してください。\n`\n : ''\n}\nなお、本メールアドレスは配信専用ですので、返信いただけません。\n\n引き続き ${appName} をよろしくお願い申し上げます。\n `.trim(),\n };\n }\n}\n\nexport const mailTemplates = new MailTemplates();\n"],"mappings":"AAmCA,MAAaA,EAAgB,IAAI,KA7Bb,CAClB,cAAc,EAAiB,EAAkB,EAAuC,CACtF,MAAO,CACL,KAAM,2BACN,QAAS,eACT,KAAM;EACVC,EAAQ;;;;;EAKRC,EAAS;EAETC,EACI;cACQA,EAAmB;;;EAI3B,GACL;;;OAGMF,EAAQ;MACT,KAAK,CACP,CACF,CACF"}
|