@hraness/sponge 0.2.5 → 0.3.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.
Files changed (227) hide show
  1. package/README.md +23 -10
  2. package/bin/sponge.js +8 -7
  3. package/dist/contracts/index.js +24 -6
  4. package/dist/contracts/validate.js +106 -29
  5. package/dist/migrations/008.d.ts +3 -0
  6. package/dist/migrations/008.js +5 -0
  7. package/dist/migrations/009.d.ts +3 -0
  8. package/dist/migrations/009.js +8 -0
  9. package/dist/migrations/010.d.ts +3 -0
  10. package/dist/migrations/010.js +11 -0
  11. package/dist/migrations/index.d.ts +15 -0
  12. package/dist/migrations/index.js +39 -0
  13. package/dist/src/acquisition/capture.js +87 -28
  14. package/dist/src/acquisition/cleanup.d.ts +0 -4
  15. package/dist/src/acquisition/cleanup.js +3 -5
  16. package/dist/src/acquisition/errors.js +17 -2
  17. package/dist/src/acquisition/files.d.ts +0 -1
  18. package/dist/src/acquisition/files.js +14 -11
  19. package/dist/src/acquisition/index.d.ts +2 -3
  20. package/dist/src/acquisition/index.js +1 -2
  21. package/dist/src/acquisition/lock.js +1 -1
  22. package/dist/src/acquisition/normalize.js +57 -18
  23. package/dist/src/acquisition/pins.js +22 -4
  24. package/dist/src/acquisition/process.js +67 -29
  25. package/dist/src/acquisition/runtime.d.ts +5 -7
  26. package/dist/src/acquisition/runtime.js +129 -68
  27. package/dist/src/acquisition/verify-helper.js +10 -3
  28. package/dist/src/annotations/quote.js +40 -12
  29. package/dist/src/annotations/store.d.ts +0 -1
  30. package/dist/src/annotations/store.js +66 -14
  31. package/dist/src/app/article.d.ts +2 -1
  32. package/dist/src/app/article.js +66 -70
  33. package/dist/src/app/bundle-files.d.ts +0 -4
  34. package/dist/src/app/bundle-files.js +11 -14
  35. package/dist/src/app/markdown.js +18 -6
  36. package/dist/src/app/source.d.ts +0 -4
  37. package/dist/src/app/source.js +18 -14
  38. package/dist/src/cli/arguments.d.ts +2 -8
  39. package/dist/src/cli/arguments.js +67 -80
  40. package/dist/src/cli/commands.d.ts +42 -0
  41. package/dist/src/cli/commands.js +795 -0
  42. package/dist/src/cli/config.js +12 -9
  43. package/dist/src/cli/digest.d.ts +1 -1
  44. package/dist/src/cli/digest.js +39 -17
  45. package/dist/src/cli/import.js +25 -4
  46. package/dist/src/cli/indexing.d.ts +2 -0
  47. package/dist/src/cli/indexing.js +26 -9
  48. package/dist/src/cli/main.js +47 -386
  49. package/dist/src/cli/portability.d.ts +0 -2
  50. package/dist/src/cli/portability.js +13 -8
  51. package/dist/src/cli/queue.js +12 -8
  52. package/dist/src/cli/runtimes.d.ts +1 -3
  53. package/dist/src/cli/runtimes.js +13 -6
  54. package/dist/src/cli/terminal.js +1 -1
  55. package/dist/src/cli/worker.d.ts +7 -0
  56. package/dist/src/cli/worker.js +54 -15
  57. package/dist/src/derivations/kinds.d.ts +34 -0
  58. package/dist/src/derivations/kinds.js +14 -0
  59. package/dist/src/derivations/types.d.ts +48 -0
  60. package/dist/src/derivations/types.js +1 -0
  61. package/dist/src/digest/derivation.d.ts +25 -0
  62. package/dist/src/digest/derivation.js +124 -0
  63. package/dist/src/digest/editorial.d.ts +10 -46
  64. package/dist/src/digest/editorial.js +73 -59
  65. package/dist/src/digest/endpoint.js +63 -27
  66. package/dist/src/digest/pipeline.d.ts +53 -32
  67. package/dist/src/digest/pipeline.js +357 -125
  68. package/dist/src/digest/schema.d.ts +44 -17
  69. package/dist/src/digest/schema.js +132 -44
  70. package/dist/src/errors.d.ts +46 -0
  71. package/dist/src/errors.js +117 -0
  72. package/dist/src/identity/query-policy.d.ts +0 -5
  73. package/dist/src/identity/query-policy.js +61 -9
  74. package/dist/src/identity/uri.d.ts +6 -5
  75. package/dist/src/identity/uri.js +37 -18
  76. package/dist/src/identity/visibility.d.ts +11 -1
  77. package/dist/src/identity/visibility.js +81 -17
  78. package/dist/src/importers/apply.js +104 -36
  79. package/dist/src/importers/custody.js +27 -9
  80. package/dist/src/importers/inspect.js +125 -38
  81. package/dist/src/importers/jungle-digest.d.ts +31 -24
  82. package/dist/src/importers/jungle-digest.js +217 -115
  83. package/dist/src/importers/jungle-store.d.ts +20 -11
  84. package/dist/src/importers/jungle-store.js +136 -41
  85. package/dist/src/importers/jungle.d.ts +32 -16
  86. package/dist/src/importers/jungle.js +145 -32
  87. package/dist/src/importers/manifest.js +57 -21
  88. package/dist/src/importers/verify-helper.js +18 -4
  89. package/dist/src/indexing/image.js +29 -10
  90. package/dist/src/indexing/index.d.ts +37 -6
  91. package/dist/src/indexing/index.js +360 -124
  92. package/dist/src/indexing/jobs.js +156 -47
  93. package/dist/src/indexing/prune-run.d.ts +2 -0
  94. package/dist/src/indexing/prune-run.js +69 -26
  95. package/dist/src/indexing/prune.d.ts +12 -3
  96. package/dist/src/indexing/prune.js +85 -35
  97. package/dist/src/indexing/runner.d.ts +2 -1
  98. package/dist/src/indexing/runner.js +83 -40
  99. package/dist/src/indexing/upgrade.d.ts +9 -0
  100. package/dist/src/indexing/upgrade.js +79 -0
  101. package/dist/src/inference/asr/bridge.js +6 -2
  102. package/dist/src/inference/asr/protocol.js +109 -19
  103. package/dist/src/inference/asr/runtime.js +58 -21
  104. package/dist/src/inference/asr/windows.js +9 -5
  105. package/dist/src/inference/embedding/index.d.ts +2 -6
  106. package/dist/src/inference/embedding/index.js +89 -29
  107. package/dist/src/ingest/attempt.d.ts +29 -0
  108. package/dist/src/ingest/attempt.js +55 -0
  109. package/dist/src/media/capture-helper.js +7 -2
  110. package/dist/src/media/capture.d.ts +2 -1
  111. package/dist/src/media/capture.js +124 -43
  112. package/dist/src/media/handler.d.ts +4 -2
  113. package/dist/src/media/handler.js +88 -70
  114. package/dist/src/media/transcribe.js +63 -21
  115. package/dist/src/metadata/article.js +49 -17
  116. package/dist/src/models/artifacts.d.ts +10 -8
  117. package/dist/src/models/artifacts.js +237 -106
  118. package/dist/src/models/catalog.d.ts +2 -5
  119. package/dist/src/models/catalog.js +94 -25
  120. package/dist/src/models/download.js +55 -15
  121. package/dist/src/models/runtime.d.ts +0 -2
  122. package/dist/src/models/runtime.js +69 -33
  123. package/dist/src/pdf/capture-helper.js +3 -1
  124. package/dist/src/pdf/capture.d.ts +2 -1
  125. package/dist/src/pdf/capture.js +95 -27
  126. package/dist/src/pdf/errors.js +12 -1
  127. package/dist/src/pdf/handler.d.ts +2 -1
  128. package/dist/src/pdf/handler.js +70 -71
  129. package/dist/src/pdf/index.d.ts +1 -3
  130. package/dist/src/pdf/index.js +0 -2
  131. package/dist/src/pdf/input.js +12 -7
  132. package/dist/src/pdf/normalize.js +80 -21
  133. package/dist/src/pdf/source-identity.js +3 -2
  134. package/dist/src/pdf/source.js +12 -8
  135. package/dist/src/pdf/verify-helper.js +15 -3
  136. package/dist/src/portability/backup.d.ts +2 -9
  137. package/dist/src/portability/backup.js +108 -22
  138. package/dist/src/portability/database.js +51 -17
  139. package/dist/src/portability/export.js +35 -9
  140. package/dist/src/portability/files.d.ts +1 -4
  141. package/dist/src/portability/files.js +127 -67
  142. package/dist/src/portability/gc.js +65 -22
  143. package/dist/src/publish/component.js +1 -1
  144. package/dist/src/publish/feed.d.ts +2 -2
  145. package/dist/src/publish/feed.js +75 -29
  146. package/dist/src/publish/identity.d.ts +5 -1
  147. package/dist/src/publish/identity.js +31 -11
  148. package/dist/src/publish/index.d.ts +2 -3
  149. package/dist/src/publish/index.js +34 -21
  150. package/dist/src/publish/projection.d.ts +2 -9
  151. package/dist/src/publish/projection.js +21 -14
  152. package/dist/src/search/extension.js +3 -1
  153. package/dist/src/search/hybrid.d.ts +3 -8
  154. package/dist/src/search/hybrid.js +45 -11
  155. package/dist/src/search/lexical.js +18 -4
  156. package/dist/src/search/query.js +56 -25
  157. package/dist/src/search/vector.js +37 -10
  158. package/dist/src/service/launch-agent.d.ts +5 -3
  159. package/dist/src/service/launch-agent.js +272 -83
  160. package/dist/src/service/lifetime.js +47 -14
  161. package/dist/src/service/loop.d.ts +3 -3
  162. package/dist/src/service/loop.js +47 -19
  163. package/dist/src/store/annotations.d.ts +32 -0
  164. package/dist/src/store/annotations.js +44 -0
  165. package/dist/src/store/backups.d.ts +18 -0
  166. package/dist/src/store/backups.js +110 -0
  167. package/dist/src/store/blobs.d.ts +24 -0
  168. package/dist/src/store/blobs.js +139 -0
  169. package/dist/src/store/changes.d.ts +5 -12
  170. package/dist/src/store/changes.js +108 -56
  171. package/dist/src/store/context.d.ts +15 -0
  172. package/dist/src/store/context.js +19 -0
  173. package/dist/src/store/derivations.d.ts +42 -0
  174. package/dist/src/store/derivations.js +104 -0
  175. package/dist/src/store/files.d.ts +0 -2
  176. package/dist/src/store/files.js +19 -24
  177. package/dist/src/store/ids.d.ts +30 -0
  178. package/dist/src/store/ids.js +34 -0
  179. package/dist/src/store/imports.d.ts +26 -0
  180. package/dist/src/store/imports.js +57 -0
  181. package/dist/src/store/index.d.ts +3 -186
  182. package/dist/src/store/index.js +2 -809
  183. package/dist/src/store/lease.d.ts +20 -0
  184. package/dist/src/store/lease.js +62 -0
  185. package/dist/src/store/library.d.ts +75 -0
  186. package/dist/src/store/library.js +174 -0
  187. package/dist/src/store/open.d.ts +7 -0
  188. package/dist/src/store/open.js +183 -0
  189. package/dist/src/store/queue.d.ts +65 -0
  190. package/dist/src/store/queue.js +327 -0
  191. package/dist/src/store/revisions.d.ts +12 -0
  192. package/dist/src/store/revisions.js +189 -0
  193. package/dist/src/store/search-index.d.ts +63 -0
  194. package/dist/src/store/search-index.js +190 -0
  195. package/dist/src/store/sync-ready.d.ts +6 -0
  196. package/dist/src/store/sync-ready.js +121 -0
  197. package/dist/src/store/types.d.ts +1 -5
  198. package/dist/src/store/types.js +1 -8
  199. package/dist/src/util/canonical.d.ts +2 -0
  200. package/dist/src/util/canonical.js +17 -0
  201. package/dist/src/util/exact.d.ts +2 -0
  202. package/dist/src/util/exact.js +9 -0
  203. package/dist/src/util/fsync.d.ts +4 -0
  204. package/dist/src/util/fsync.js +22 -0
  205. package/dist/src/util/hash.d.ts +4 -0
  206. package/dist/src/util/hash.js +8 -0
  207. package/dist/src/worker/basic.d.ts +0 -1
  208. package/dist/src/worker/basic.js +23 -7
  209. package/dist/src/worker/custody.d.ts +4 -1
  210. package/dist/src/worker/custody.js +111 -38
  211. package/dist/src/worker/engine.d.ts +2 -1
  212. package/dist/src/worker/engine.js +42 -20
  213. package/dist/src/worker/identity.js +13 -5
  214. package/dist/src/worker/journal.d.ts +1 -1
  215. package/dist/src/worker/journal.js +54 -24
  216. package/dist/src/worker/lock.d.ts +18 -0
  217. package/dist/src/worker/lock.js +196 -0
  218. package/dist/src/worker/runtime-gc.d.ts +4 -3
  219. package/dist/src/worker/runtime-gc.js +68 -41
  220. package/dist/src/worker/supervisor-child.js +19 -6
  221. package/dist/src/worker/supervisor.js +41 -8
  222. package/native/runtime-manifest.json +4 -4
  223. package/package.json +10 -2
  224. package/dist/src/digest/store.d.ts +0 -57
  225. package/dist/src/digest/store.js +0 -77
  226. package/dist/src/store/foundation.d.ts +0 -7
  227. package/dist/src/store/foundation.js +0 -7
@@ -1,36 +1,40 @@
1
- import { createHash } from 'node:crypto';
1
+ import { sha256 } from '../util/hash.js';
2
2
  import { campaignFields, filterQuery } from '../identity/query-policy.js';
3
- export class InputError extends Error {
4
- code = 'invalid-input';
5
- constructor() { super('invalid-input'); }
6
- }
3
+ import { SpongeError } from '../errors.js';
7
4
  /** Identity normalization removes only known campaign fields; private realms remain distinct. */
8
5
  function webSource(locator, realm, kind) {
9
- if (Buffer.byteLength(locator) > 8192 || !realm || Buffer.byteLength(realm) > 256 || /[\u0000-\u001f\u007f]/u.test(locator + realm))
10
- throw new InputError();
6
+ if (Buffer.byteLength(locator) > 8192 ||
7
+ !realm ||
8
+ Buffer.byteLength(realm) > 256 ||
9
+ /[\u0000-\u001f\u007f]/u.test(locator + realm))
10
+ throw new SpongeError('invalid-input');
11
11
  const normalized = normalizeWebLocator(locator);
12
- const sourceKey = createHash('sha256').update(JSON.stringify([kind, realm, normalized])).digest('hex');
12
+ const sourceKey = sha256(JSON.stringify([kind, realm, normalized]));
13
13
  return { sourceKey, locator, realm };
14
14
  }
15
15
  /** The identity form of an http(s) locator; source keys and shareable URIs both use it. Its output pins every existing source key. */
16
16
  export function normalizeWebLocator(locator) {
17
17
  if (Buffer.byteLength(locator) > 8192 || /[\u0000-\u001f\u007f]/u.test(locator))
18
- throw new InputError();
18
+ throw new SpongeError('invalid-input');
19
19
  let url;
20
20
  try {
21
21
  url = new URL(locator);
22
22
  }
23
23
  catch {
24
- throw new InputError();
24
+ throw new SpongeError('invalid-input');
25
25
  }
26
26
  if (!['http:', 'https:'].includes(url.protocol) || url.username || url.password || !url.hostname)
27
- throw new InputError();
27
+ throw new SpongeError('invalid-input');
28
28
  // Undecodable keys are kept: they cannot be campaign fields.
29
29
  if (url.search)
30
- url.search = filterQuery(url.search, key => campaignFields.has(key));
30
+ url.search = filterQuery(url.search, (key) => campaignFields.has(key));
31
31
  // URL normalizes host casing/default ports. Preserve query order, unknown fields and fragments.
32
32
  return url.href;
33
33
  }
34
- export function articleSource(locator, realm = 'public') { return webSource(locator, realm, 'article'); }
34
+ export function articleSource(locator, realm = 'public') {
35
+ return webSource(locator, realm, 'article');
36
+ }
35
37
  /** A page and its media can be saved independently without changing existing article identities. */
36
- export function mediaSource(locator, realm = 'public') { return webSource(locator, realm, 'media'); }
38
+ export function mediaSource(locator, realm = 'public') {
39
+ return webSource(locator, realm, 'media');
40
+ }
@@ -1,9 +1,3 @@
1
- export interface Arguments {
2
- command: string;
3
- positional: string[];
4
- flags: Record<string, string | true>;
5
- library?: string;
6
- json: boolean;
7
- }
8
- /** No shell grammar, implicit option coercion, duplicate flags, or unbounded argv. */
1
+ import { type Arguments } from './commands.js';
2
+ /** No shell grammar, implicit option coercion, duplicate flags, or unbounded argv. Commands and their rules live in the registry. */
9
3
  export declare function parseArguments(argv: string[]): Arguments;
@@ -1,47 +1,30 @@
1
1
  import { isAbsolute } from 'node:path';
2
- import { InputError } from '../app/source.js';
3
- import { reviewSpec } from './digest.js';
4
- const specifications = {
5
- init: { count: 0, flags: {} },
6
- add: { count: 1, flags: { connection: 'value', kind: 'value', wait: 'boolean', language: 'value', 'force-local': 'boolean', 'allow-private-network': 'boolean' } },
7
- list: { count: 0, flags: {} },
8
- asset: { count: 2, flags: {} },
9
- show: { count: 1, flags: { format: 'value' } },
10
- delete: { count: 1, flags: {} },
11
- refresh: { count: 1, flags: { wait: 'boolean', language: 'value', 'force-local': 'boolean', 'allow-private-network': 'boolean' } },
12
- search: { count: 1, flags: { mode: 'value', limit: 'value', profile: 'value' } },
13
- run: { count: 0, flags: { drain: 'boolean', language: 'value', 'force-local': 'boolean', 'allow-private-network': 'boolean' } },
14
- setup: { count: 0, flags: { capture: 'boolean', asr: 'boolean', embedding: 'boolean' } },
15
- doctor: { count: 0, flags: {} },
16
- queue: { count: -1, flags: {} },
17
- export: { count: 1, flags: { output: 'value' } },
18
- backup: { count: 0, flags: { output: 'value' } },
19
- restore: { count: 1, flags: { into: 'value' } },
20
- gc: { count: 0, flags: { 'dry-run': 'boolean', apply: 'value', runtimes: 'boolean' } },
21
- worker: { count: 1, flags: { language: 'value', 'force-local': 'boolean' } },
22
- index: { count: -1, flags: { profile: 'value', keep: 'value', 'dry-run': 'boolean', vacuum: 'boolean' } },
23
- visibility: { count: 2, flags: {} },
24
- highlight: { count: 2, flags: { note: 'value', occurrence: 'value' } },
25
- comment: { count: 2, flags: { on: 'value' } },
26
- publish: { count: 0, flags: { 'dry-run': 'boolean', output: 'value', 'min-age-days': 'value', title: 'value', 'base-url': 'value' } },
27
- digest: { count: 1, flags: { endpoint: 'value', model: 'value', 'check-support': 'boolean' } },
28
- review: { count: 1, flags: { accept: 'value', reject: 'value' } },
29
- import: { count: -1, flags: { apply: 'boolean', 'dry-run': 'boolean', limit: 'value', offset: 'value' } },
2
+ import { SpongeError } from '../errors.js';
3
+ import { command as lookup, commands } from './commands.js';
4
+ const globals = {
5
+ library: 'value',
6
+ json: 'boolean',
7
+ debug: 'boolean',
8
+ help: 'boolean',
30
9
  };
31
- /** No shell grammar, implicit option coercion, duplicate flags, or unbounded argv. */
10
+ /** A usage error names its command so the CLI can print that command's synopsis. */
11
+ const usage = (command) => {
12
+ throw new SpongeError('invalid-input', { detail: command ? { command } : {} });
13
+ };
14
+ /** No shell grammar, implicit option coercion, duplicate flags, or unbounded argv. Commands and their rules live in the registry. */
32
15
  export function parseArguments(argv) {
33
- if (argv.length > 32 || argv.some(arg => Buffer.byteLength(arg) > 8192 || /[\u0000-\u001f\u007f]/u.test(arg)))
34
- throw new InputError();
35
- if (argv.length === 0 || argv.length === 1 && ['--help', '-h'].includes(argv[0]))
36
- return { command: 'help', positional: [], flags: {}, json: false };
16
+ if (argv.length > 32 || argv.some((arg) => Buffer.byteLength(arg) > 8192 || /[\u0000-\u001f\u007f]/u.test(arg)))
17
+ usage();
18
+ if (argv.length === 0 || (argv.length === 1 && ['--help', '-h'].includes(argv[0])))
19
+ return { command: 'help', positional: [], flags: {}, json: false, debug: false };
37
20
  if (argv.length === 1 && argv[0] === '--version')
38
- return { command: 'version', positional: [], flags: {}, json: false };
21
+ return { command: 'version', positional: [], flags: {}, json: false, debug: false };
39
22
  // Global flags are valid before or after the command.
40
23
  let command;
41
24
  const flags = Object.create(null);
42
25
  const positional = [];
43
- const allFlags = { library: 'value', json: 'boolean' };
44
- for (const spec of Object.values(specifications))
26
+ const allFlags = { ...globals };
27
+ for (const spec of commands)
45
28
  Object.assign(allFlags, spec.flags);
46
29
  let literal = false;
47
30
  for (let i = 0; i < argv.length; i++) {
@@ -52,15 +35,15 @@ export function parseArguments(argv) {
52
35
  }
53
36
  if (arg.startsWith('--') && !literal) {
54
37
  const key = arg.slice(2);
55
- const kind = (command && specifications[command]?.flags[key]) ?? allFlags[key];
38
+ const kind = lookup(command)?.flags[key] ?? allFlags[key];
56
39
  if (!kind || Object.hasOwn(flags, key))
57
- throw new InputError();
40
+ usage(command);
58
41
  if (kind === 'boolean')
59
42
  flags[key] = true;
60
43
  else {
61
44
  const value = argv[++i];
62
45
  if (!value || value.startsWith('--'))
63
- throw new InputError();
46
+ usage(command);
64
47
  flags[key] = value;
65
48
  }
66
49
  }
@@ -69,55 +52,59 @@ export function parseArguments(argv) {
69
52
  else
70
53
  positional.push(arg);
71
54
  }
72
- const spec = command && specifications[command];
73
- if (!command || !spec || (command === 'queue' ? !(['status', 'recover'].includes(positional[0]) && positional.length === 1 || ['cancel', 'retry'].includes(positional[0]) && positional.length === 2) : command === 'import' ? !(positional.length === 1 || positional[0] === 'jungle' && positional.length === 2) : command === 'index' ? !(['create', 'run', 'prune'].includes(positional[0]) && positional.length === 1 || ['status', 'rollback'].includes(positional[0]) && positional.length === 2) : positional.length !== spec.count))
74
- throw new InputError();
75
- if (Object.keys(flags).some(key => !['library', 'json'].includes(key) && !Object.hasOwn(spec.flags, key)))
76
- throw new InputError();
55
+ const spec = lookup(command);
56
+ if (!command && flags.help)
57
+ return { command: 'help', positional: [], flags: {}, json: false, debug: flags.debug === true };
58
+ if (!command || !spec)
59
+ return usage();
60
+ if (flags.help)
61
+ return { command: 'help', positional: [spec.name], flags: {}, json: false, debug: flags.debug === true };
62
+ if (typeof spec.positional === 'number' ? positional.length !== spec.positional : !spec.positional(positional))
63
+ usage(command);
64
+ if (Object.keys(flags).some((key) => !Object.hasOwn(globals, key) && !Object.hasOwn(spec.flags, key)))
65
+ usage(command);
66
+ if (spec.check && !spec.check(positional, flags))
67
+ usage(command);
77
68
  const library = flags.library;
78
69
  if (library !== undefined && (typeof library !== 'string' || !isAbsolute(library)))
79
- throw new InputError();
70
+ usage(command);
80
71
  if (flags.kind && !['article', 'pdf', 'media'].includes(String(flags.kind)))
81
- throw new InputError();
72
+ usage(command);
82
73
  if (flags.format && !['markdown', 'json'].includes(String(flags.format)))
83
- throw new InputError();
74
+ usage(command);
84
75
  if (flags.mode && !['lexical', 'hybrid'].includes(String(flags.mode)))
85
- throw new InputError();
86
- if (flags.limit && (!/^[1-9]\d{0,3}$/u.test(String(flags.limit)) || Number(flags.limit) > (command === 'import' && positional[0] === 'jungle' ? 1000 : 100)))
87
- throw new InputError();
88
- if (command === 'run' && !flags.drain || command === 'setup' && [flags.capture, flags.asr, flags.embedding].filter(Boolean).length !== 1)
89
- throw new InputError();
76
+ usage(command);
77
+ if (flags.limit &&
78
+ (!/^[1-9]\d{0,3}$/u.test(String(flags.limit)) ||
79
+ Number(flags.limit) > (command === 'import' && positional[0] === 'jungle' ? 1000 : 100)))
80
+ usage(command);
90
81
  if (flags['allow-private-network'] && command !== 'run' && !flags.wait)
91
- throw new InputError();
92
- if (['export', 'backup'].includes(command) && (typeof flags.output !== 'string' || !isAbsolute(flags.output)))
93
- throw new InputError();
94
- if (command === 'restore' && (typeof flags.into !== 'string' || !isAbsolute(flags.into) || flags.library !== undefined || !isAbsolute(positional[0])))
95
- throw new InputError();
96
- if (command === 'gc' && (flags.runtimes ? flags.apply !== undefined : Boolean(flags['dry-run']) === Boolean(flags.apply) || flags.apply !== undefined && !isAbsolute(String(flags.apply))))
97
- throw new InputError();
98
- if (command === 'worker' && !['install', 'status', 'uninstall', 'serve'].includes(positional[0]))
99
- throw new InputError();
100
- if (command === 'import' && (!isAbsolute(positional.at(-1)) || flags.apply && flags['dry-run'] || flags.offset !== undefined && !/^(0|[1-9][0-9]{0,5})$/.test(String(flags.offset))))
101
- throw new InputError();
102
- if (command === 'visibility' && !['public', 'private', 'default'].includes(positional[1]) || command === 'publish' && (Boolean(flags['dry-run']) === Boolean(flags.output) || flags.output !== undefined && (typeof flags.output !== 'string' || !isAbsolute(flags.output)) || (flags.title !== undefined || flags['base-url'] !== undefined) && !flags.output || flags['min-age-days'] !== undefined && !/^(0|[1-9][0-9]{0,3})$/.test(String(flags['min-age-days'])) || Number(flags['min-age-days'] ?? 0) > 3650 || flags.title !== undefined && String(flags.title).length > 200 || flags['base-url'] !== undefined && !/^https:\/\//.test(String(flags['base-url']))))
103
- throw new InputError();
104
- if (command === 'digest' && (Boolean(flags.endpoint) !== Boolean(flags.model) || flags['check-support'] && !flags.endpoint || flags.model !== undefined && !/^[\w.:/@+-]{1,256}$/.test(String(flags.model))))
105
- throw new InputError();
106
- if (command === 'review' && (flags.accept === undefined && flags.reject === undefined || [flags.accept, flags.reject].some(v => v !== undefined && !reviewSpec.test(String(v)))))
107
- throw new InputError();
82
+ usage(command);
108
83
  if (flags.occurrence !== undefined && !/^(0|[1-9][0-9]{0,4})$/.test(String(flags.occurrence)))
109
- throw new InputError();
84
+ usage(command);
110
85
  if (flags.language !== undefined && flags.language !== 'en')
111
- throw new InputError();
86
+ usage(command);
112
87
  if (flags['force-local'] && command === 'worker' && positional[0] === 'install')
113
- throw new InputError();
88
+ usage(command);
114
89
  if (flags['force-local'] && !flags.language)
115
- throw new InputError();
116
- if ((flags.language || flags['force-local']) && (['add', 'refresh'].includes(command) && !flags.wait || command === 'worker' && !['serve', 'install'].includes(positional[0])))
117
- throw new InputError();
118
- if (command === 'index' && positional[0] !== 'prune' && (flags.keep !== undefined || flags['dry-run'] || flags.vacuum) || flags.keep !== undefined && !/^(0|[1-9]|1[0-6])$/.test(String(flags.keep)))
119
- throw new InputError();
120
- if (flags.profile && (!(command === 'search' || command === 'index' && positional[0] === 'create') || !/^[a-zA-Z0-9_-]{1,128}$/.test(String(flags.profile))))
121
- throw new InputError();
122
- return { command, positional, flags, library: library, json: flags.json === true || flags.format === 'json' };
90
+ usage(command);
91
+ if ((flags.language || flags['force-local']) &&
92
+ ((['add', 'refresh'].includes(command) && !flags.wait) ||
93
+ (command === 'worker' && !['serve', 'install'].includes(positional[0]))))
94
+ usage(command);
95
+ if (flags.keep !== undefined && !/^(0|[1-9]|1[0-6])$/.test(String(flags.keep)))
96
+ usage(command);
97
+ if (flags.profile &&
98
+ (!(command === 'search' ||
99
+ (command === 'index' && (positional[0] === 'create' || (positional[0] === 'status' && positional.length === 1)))) ||
100
+ !/^[a-zA-Z0-9_-]{1,128}$/.test(String(flags.profile))))
101
+ usage(command);
102
+ return {
103
+ command,
104
+ positional,
105
+ flags,
106
+ library: library,
107
+ json: flags.json === true || flags.format === 'json',
108
+ debug: flags.debug === true,
109
+ };
123
110
  }
@@ -0,0 +1,42 @@
1
+ import type { LibraryStore } from '../store/index.js';
2
+ /** A parsed command line (see `arguments.ts`). */
3
+ export interface Arguments {
4
+ command: string;
5
+ positional: string[];
6
+ flags: Record<string, string | true>;
7
+ library?: string;
8
+ json: boolean;
9
+ debug: boolean;
10
+ }
11
+ type Flags = Arguments['flags'];
12
+ export interface Context {
13
+ args: Arguments;
14
+ store: LibraryStore;
15
+ version: string;
16
+ signal: AbortSignal;
17
+ emit: (data: unknown, text?: string) => void;
18
+ write: (text: string | Uint8Array) => void;
19
+ captureRoot: string;
20
+ nativeRoot: string;
21
+ }
22
+ export interface Command {
23
+ name: string;
24
+ summary: string;
25
+ /** Help lines: [synopsis without `sponge`, description]. */
26
+ usage: readonly (readonly [string, string])[];
27
+ /** Positional count, or a predicate over the positionals. */
28
+ positional: number | ((p: string[]) => boolean);
29
+ flags: Readonly<Record<string, 'value' | 'boolean'>>;
30
+ /** Command-specific usage rules beyond arity and flag names; false is a usage error. */
31
+ check?: (p: string[], f: Flags) => boolean;
32
+ /** none: no library; create: init may create it; open: an existing library. */
33
+ library: 'none' | 'create' | 'open';
34
+ handler: (c: Context) => Promise<number> | number;
35
+ }
36
+ export declare const commands: readonly Command[];
37
+ export declare const command: (name: string | undefined) => Command | undefined;
38
+ export declare function help(): string;
39
+ /** One command's synopsis lines; also the hint printed for its usage errors. */
40
+ export declare const usageOf: (name: string) => string;
41
+ export declare function commandHelp(name: string): string;
42
+ export {};