@procedures/client 0.3.0 → 0.4.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 (120) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/README.md +16 -6
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/adapters/http/server/transport.d.ts.map +1 -1
  5. package/dist/adapters/http/server/transport.js +2 -3
  6. package/dist/adapters/http/server/transport.js.map +1 -1
  7. package/dist/adapters/local/client/transport.d.ts.map +1 -1
  8. package/dist/adapters/local/client/transport.js +4 -2
  9. package/dist/adapters/local/client/transport.js.map +1 -1
  10. package/dist/adapters/websocket/client/node.d.ts +30 -0
  11. package/dist/adapters/websocket/client/node.d.ts.map +1 -0
  12. package/dist/adapters/websocket/client/node.js +32 -0
  13. package/dist/adapters/websocket/client/node.js.map +1 -0
  14. package/dist/adapters/websocket/client/transport.d.ts.map +1 -1
  15. package/dist/adapters/websocket/client/transport.js +17 -9
  16. package/dist/adapters/websocket/client/transport.js.map +1 -1
  17. package/dist/adapters/websocket/client/types.d.ts +3 -2
  18. package/dist/adapters/websocket/client/types.d.ts.map +1 -1
  19. package/dist/adapters/websocket/client/types.js.map +1 -1
  20. package/dist/client/errors/http-errors.d.ts.map +1 -1
  21. package/dist/client/errors/http-errors.js +6 -0
  22. package/dist/client/errors/http-errors.js.map +1 -1
  23. package/dist/client/errors/index.d.ts +1 -1
  24. package/dist/client/errors/index.js +1 -1
  25. package/dist/client/errors/registry.d.ts +2 -1
  26. package/dist/client/errors/registry.d.ts.map +1 -1
  27. package/dist/client/errors/registry.js +72 -22
  28. package/dist/client/errors/registry.js.map +1 -1
  29. package/dist/client/middleware/rate-limit.d.ts.map +1 -1
  30. package/dist/client/middleware/rate-limit.js +0 -1
  31. package/dist/client/middleware/rate-limit.js.map +1 -1
  32. package/dist/client/middleware/timeout.d.ts.map +1 -1
  33. package/dist/client/middleware/timeout.js +2 -9
  34. package/dist/client/middleware/timeout.js.map +1 -1
  35. package/dist/index.d.ts +4 -0
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +4 -0
  38. package/dist/index.js.map +1 -1
  39. package/dist/node.d.ts +11 -0
  40. package/dist/node.d.ts.map +1 -0
  41. package/dist/node.js +10 -0
  42. package/dist/node.js.map +1 -0
  43. package/dist/procedures/core/array.d.ts +1 -1
  44. package/dist/procedures/core/array.d.ts.map +1 -1
  45. package/dist/procedures/core/array.js +56 -15
  46. package/dist/procedures/core/array.js.map +1 -1
  47. package/dist/procedures/core/index.d.ts.map +1 -1
  48. package/dist/procedures/core/index.js +9 -2
  49. package/dist/procedures/core/index.js.map +1 -1
  50. package/dist/procedures/core/logic.js +1 -1
  51. package/dist/procedures/core/logic.js.map +1 -1
  52. package/dist/procedures/core/math.d.ts.map +1 -1
  53. package/dist/procedures/core/math.js +5 -1
  54. package/dist/procedures/core/math.js.map +1 -1
  55. package/dist/procedures/core/meta.d.ts.map +1 -1
  56. package/dist/procedures/core/meta.js +41 -7
  57. package/dist/procedures/core/meta.js.map +1 -1
  58. package/dist/procedures/core/type.d.ts.map +1 -1
  59. package/dist/procedures/core/type.js +4 -2
  60. package/dist/procedures/core/type.js.map +1 -1
  61. package/dist/procedures/discovery.d.ts +4 -6
  62. package/dist/procedures/discovery.d.ts.map +1 -1
  63. package/dist/procedures/discovery.js +4 -6
  64. package/dist/procedures/discovery.js.map +1 -1
  65. package/dist/procedures/ref.d.ts +4 -2
  66. package/dist/procedures/ref.d.ts.map +1 -1
  67. package/dist/procedures/ref.js +2 -1
  68. package/dist/procedures/ref.js.map +1 -1
  69. package/dist/protocol.d.ts +9 -2
  70. package/dist/protocol.d.ts.map +1 -1
  71. package/dist/protocol.js +12 -2
  72. package/dist/protocol.js.map +1 -1
  73. package/dist/server/procedure-server.d.ts +2 -1
  74. package/dist/server/procedure-server.d.ts.map +1 -1
  75. package/dist/server/procedure-server.js +4 -2
  76. package/dist/server/procedure-server.js.map +1 -1
  77. package/package.json +5 -4
  78. package/src/adapters/http/server/transport.ts +2 -3
  79. package/src/adapters/local/client/transport.ts +4 -2
  80. package/src/adapters/websocket/client/node.ts +34 -0
  81. package/src/adapters/websocket/client/transport.ts +20 -9
  82. package/src/adapters/websocket/client/types.ts +3 -2
  83. package/src/client/errors/README.md +2 -0
  84. package/src/client/errors/http-errors.ts +5 -0
  85. package/src/client/errors/index.ts +1 -1
  86. package/src/client/errors/registry.ts +79 -22
  87. package/src/client/middleware/rate-limit.ts +0 -2
  88. package/src/client/middleware/timeout.ts +2 -9
  89. package/src/index.ts +4 -0
  90. package/src/node.ts +11 -0
  91. package/src/procedures/core/array.ts +53 -16
  92. package/src/procedures/core/index.ts +9 -2
  93. package/src/procedures/core/logic.ts +1 -1
  94. package/src/procedures/core/math.ts +5 -1
  95. package/src/procedures/core/meta.ts +43 -7
  96. package/src/procedures/core/type.ts +4 -2
  97. package/src/procedures/discovery.ts +4 -6
  98. package/src/procedures/ref.ts +4 -2
  99. package/src/protocol.ts +13 -2
  100. package/src/server/procedure-server.ts +4 -2
  101. package/dist/procedures/cli/announce.d.ts +0 -67
  102. package/dist/procedures/cli/announce.d.ts.map +0 -1
  103. package/dist/procedures/cli/announce.js +0 -392
  104. package/dist/procedures/cli/announce.js.map +0 -1
  105. package/dist/procedures/cli/discover.d.ts +0 -66
  106. package/dist/procedures/cli/discover.d.ts.map +0 -1
  107. package/dist/procedures/cli/discover.js +0 -310
  108. package/dist/procedures/cli/discover.js.map +0 -1
  109. package/dist/procedures/cli/index.d.ts +0 -13
  110. package/dist/procedures/cli/index.d.ts.map +0 -1
  111. package/dist/procedures/cli/index.js +0 -203
  112. package/dist/procedures/cli/index.js.map +0 -1
  113. package/dist/procedures/cli/init.d.ts +0 -17
  114. package/dist/procedures/cli/init.d.ts.map +0 -1
  115. package/dist/procedures/cli/init.js +0 -336
  116. package/dist/procedures/cli/init.js.map +0 -1
  117. package/src/procedures/cli/announce.ts +0 -470
  118. package/src/procedures/cli/discover.ts +0 -390
  119. package/src/procedures/cli/index.ts +0 -231
  120. package/src/procedures/cli/init.ts +0 -370
@@ -1,390 +0,0 @@
1
- /**
2
- * Procedure Discovery
3
- *
4
- * Generates a TypeScript file with static imports for all discovered procedures.
5
- *
6
- * Fast path: Reads from .client-registry.json (populated by `client-registry announce`)
7
- * Fallback: Scans node_modules for packages with `client.procedures`
8
- *
9
- * This is compile-time discovery - the generated file contains static imports
10
- * that get compiled/bundled with your application.
11
- */
12
-
13
- import * as fs from "node:fs/promises";
14
- import * as path from "node:path";
15
- import { watch } from "node:fs";
16
- import { getRegistry, REGISTRY_FILENAME } from "./announce.js";
17
-
18
- // =============================================================================
19
- // Types
20
- // =============================================================================
21
-
22
- export interface DiscoverOptions {
23
- /** Output file path */
24
- output: string;
25
- /** Root directory to scan from */
26
- root: string;
27
- /** Watch mode for development */
28
- watch: boolean;
29
- /** Check if generated file is up-to-date (CI mode) */
30
- check: boolean;
31
- /** Verbose logging */
32
- verbose: boolean;
33
- }
34
-
35
- interface PackageJson {
36
- name: string;
37
- dependencies?: Record<string, string>;
38
- devDependencies?: Record<string, string>;
39
- exports?: unknown;
40
- client?: {
41
- procedures?: string;
42
- };
43
- }
44
-
45
- /** A package that declares procedures. */
46
- export interface DiscoveredPackage {
47
- name: string;
48
- proceduresPath: string;
49
- /** The module specifier that the generated file imports */
50
- importPath: string;
51
- }
52
-
53
- /** The string targets of an exports value: a string, an array, or the leaves of conditions. */
54
- function exportTargets(value: unknown): string[] {
55
- if (typeof value === "string") return [value];
56
- if (Array.isArray(value)) return value.flatMap(exportTargets);
57
- if (value && typeof value === "object") return Object.values(value).flatMap(exportTargets);
58
- return [];
59
- }
60
-
61
- /**
62
- * The specifier that imports the procedures file of a package. With an `exports` map, a bare
63
- * `pkg/dist/register.js` import fails unless the map exports that path: the function looks for
64
- * the subpath (also a `./*` pattern) whose target is the file: for the packages of this
65
- * repository, `./register`. When the map has none, it imports the package root. (Before, the
66
- * generated import ignored the map, and failed: deep dive CORE-17.)
67
- *
68
- * @param name - Package name
69
- * @param manifest - The package.json of the package
70
- * @param proceduresPath - The `client.procedures` field
71
- * @returns The module specifier for the generated import
72
- */
73
- export function procedureImportPath(name: string, manifest: { exports?: unknown }, proceduresPath: string): string {
74
- const file = "./" + proceduresPath.replace(/^\.\//, "");
75
- if (manifest.exports === undefined) {
76
- return `${name}/${file.slice(2)}`;
77
- }
78
- const exportsMap =
79
- typeof manifest.exports === "string" || Array.isArray(manifest.exports) ||
80
- !Object.keys(manifest.exports as object).some((key) => key.startsWith("."))
81
- ? { ".": manifest.exports }
82
- : (manifest.exports as Record<string, unknown>);
83
-
84
- for (const [subpath, value] of Object.entries(exportsMap)) {
85
- for (const target of exportTargets(value)) {
86
- if (!subpath.includes("*")) {
87
- if (target === file) return subpath === "." ? name : `${name}/${subpath.slice(2)}`;
88
- continue;
89
- }
90
- // A pattern: "./*" -> "./dist/*.js"
91
- const [before, after] = target.split("*") as [string, string | undefined];
92
- if (after === undefined || !file.startsWith(before) || !file.endsWith(after)) continue;
93
- const star = file.slice(before.length, file.length - after.length);
94
- return `${name}/${subpath.replace("*", star).slice(2)}`;
95
- }
96
- }
97
- return name;
98
- }
99
-
100
- // =============================================================================
101
- // Discovery
102
- // =============================================================================
103
-
104
- /**
105
- * Discover packages with procedure declarations: the dependencies of the root package and
106
- * their dependencies, in `node_modules`.
107
- *
108
- * @param rootDir - The folder of the root package.json
109
- * @param verbose - Log each package
110
- * @returns The packages that declare procedures, sorted by name
111
- */
112
- export async function discoverPackages(
113
- rootDir: string,
114
- verbose: boolean
115
- ): Promise<DiscoveredPackage[]> {
116
- const discovered: DiscoveredPackage[] = [];
117
- const scanned = new Set<string>();
118
-
119
- // Read root package.json
120
- const rootPkgPath = path.join(rootDir, "package.json");
121
- let rootPkg: PackageJson;
122
-
123
- try {
124
- const content = await fs.readFile(rootPkgPath, "utf-8");
125
- rootPkg = JSON.parse(content);
126
- } catch {
127
- throw new Error(`Could not read package.json at ${rootPkgPath}`);
128
- }
129
-
130
- // Get all dependencies to scan
131
- const depsToScan = [
132
- ...Object.keys(rootPkg.dependencies ?? {}),
133
- ...Object.keys(rootPkg.devDependencies ?? {}),
134
- ];
135
-
136
- if (verbose) {
137
- console.log(`Scanning ${depsToScan.length} dependencies...`);
138
- }
139
-
140
- // Scan each dependency
141
- async function scanPackage(packageName: string, nodeModulesPath: string): Promise<void> {
142
- if (scanned.has(packageName)) return;
143
-
144
- const pkgDir = path.join(nodeModulesPath, packageName);
145
- const pkgJsonPath = path.join(pkgDir, "package.json");
146
-
147
- let pkg: PackageJson;
148
- try {
149
- const content = await fs.readFile(pkgJsonPath, "utf-8");
150
- pkg = JSON.parse(content);
151
- } catch {
152
- // Package doesn't exist at this path (might be hoisted differently). It is not scanned
153
- // yet: the next location can have it. (Before, the name was marked as scanned before
154
- // the read, so a hoisted transitive package was skipped: deep dive CORE-17.)
155
- return;
156
- }
157
- scanned.add(packageName);
158
-
159
- // Check if this package declares procedures
160
- if (pkg.client?.procedures) {
161
- const proceduresPath = pkg.client.procedures;
162
-
163
- // Verify the file exists
164
- const fullPath = path.join(pkgDir, proceduresPath);
165
- try {
166
- await fs.access(fullPath);
167
- discovered.push({
168
- name: packageName,
169
- proceduresPath,
170
- importPath: procedureImportPath(packageName, pkg, proceduresPath),
171
- });
172
-
173
- if (verbose) {
174
- console.log(` Found: ${packageName} -> ${proceduresPath}`);
175
- }
176
- } catch {
177
- console.warn(` Warning: ${packageName} declares procedures at ${proceduresPath} but file not found`);
178
- }
179
- }
180
-
181
- // Recursively scan this package's dependencies
182
- const subDeps = Object.keys(pkg.dependencies ?? {});
183
- for (const subDep of subDeps) {
184
- // Try local node_modules first, then parent
185
- await scanPackage(subDep, path.join(pkgDir, "node_modules"));
186
- await scanPackage(subDep, nodeModulesPath);
187
- }
188
- }
189
-
190
- // Scan all dependencies
191
- const nodeModulesPath = path.join(rootDir, "node_modules");
192
- for (const dep of depsToScan) {
193
- await scanPackage(dep, nodeModulesPath);
194
- }
195
-
196
- // Sort for deterministic output
197
- discovered.sort((a, b) => a.name.localeCompare(b.name));
198
-
199
- return discovered;
200
- }
201
-
202
- // =============================================================================
203
- // Code Generation
204
- // =============================================================================
205
-
206
- /**
207
- * Generate the TypeScript file content.
208
- *
209
- * @param packages - The discovered packages
210
- * @returns The file content: one side-effect import for each package
211
- */
212
- export function generateCode(packages: DiscoveredPackage[]): string {
213
- const timestamp = new Date().toISOString();
214
- const imports = packages
215
- .map((p) => `import "${p.importPath}";`)
216
- .join("\n");
217
-
218
- const packageList = packages.map((p) => ` "${p.name}",`).join("\n");
219
-
220
- return `/**
221
- * Auto-generated procedure imports
222
- *
223
- * DO NOT EDIT - This file is generated by \`npx client-registry discover\`
224
- * Generated at: ${timestamp}
225
- *
226
- * Import this file at your app's entry point to register all procedures
227
- * from your dependencies.
228
- */
229
-
230
- // Side-effect imports that register procedures
231
- ${imports || "// No packages with procedures found"}
232
-
233
- /**
234
- * List of packages that were discovered and imported.
235
- */
236
- export const discoveredPackages = [
237
- ${packageList || " // No packages with procedures found"}
238
- ] as const;
239
-
240
- /**
241
- * Number of packages with procedures.
242
- */
243
- export const packageCount = ${packages.length};
244
- `;
245
- }
246
-
247
- // =============================================================================
248
- // Main
249
- // =============================================================================
250
-
251
- /**
252
- * Try to load packages from registry (fast path).
253
- * Returns null if registry doesn't exist or is empty.
254
- */
255
- async function loadFromRegistry(
256
- rootDir: string,
257
- verbose: boolean
258
- ): Promise<DiscoveredPackage[] | null> {
259
- try {
260
- const registry = await getRegistry(rootDir);
261
-
262
- if (Object.keys(registry.packages).length === 0) {
263
- return null;
264
- }
265
-
266
- const packages: DiscoveredPackage[] = [];
267
-
268
- for (const [name, entry] of Object.entries(registry.packages)) {
269
- // The exports map of the installed package decides the import path
270
- let manifest: { exports?: unknown } = {};
271
- try {
272
- manifest = JSON.parse(await fs.readFile(path.join(rootDir, "node_modules", name, "package.json"), "utf-8"));
273
- } catch {
274
- // Not installed at the root: import the declared file
275
- }
276
- packages.push({
277
- name,
278
- proceduresPath: entry.proceduresPath,
279
- importPath: procedureImportPath(name, manifest, entry.proceduresPath),
280
- });
281
- }
282
-
283
- // Sort for deterministic output
284
- packages.sort((a, b) => a.name.localeCompare(b.name));
285
-
286
- if (verbose) {
287
- console.log(`Loaded ${packages.length} package(s) from ${REGISTRY_FILENAME}`);
288
- }
289
-
290
- return packages;
291
- } catch {
292
- return null;
293
- }
294
- }
295
-
296
- /**
297
- * Run procedure discovery.
298
- */
299
- export async function discover(options: DiscoverOptions): Promise<void> {
300
- const { output, root, watch: watchMode, check, verbose } = options;
301
-
302
- async function runDiscovery(): Promise<string> {
303
- if (verbose) {
304
- console.log(`Discovering procedures in ${root}...`);
305
- }
306
-
307
- // Try registry first (fast path)
308
- let packages = await loadFromRegistry(root, verbose);
309
-
310
- if (packages === null) {
311
- // Fallback to scanning
312
- if (verbose) {
313
- console.log(`No registry found, scanning node_modules...`);
314
- }
315
- packages = await discoverPackages(root, verbose);
316
- }
317
-
318
- const code = generateCode(packages);
319
-
320
- console.log(`Discovered ${packages.length} package(s) with procedures`);
321
-
322
- return code;
323
- }
324
-
325
- // Run initial discovery
326
- const code = await runDiscovery();
327
-
328
- // Resolve output path
329
- const outputPath = path.isAbsolute(output) ? output : path.join(root, output);
330
- const outputDir = path.dirname(outputPath);
331
-
332
- if (check) {
333
- // Check mode: verify the file is up-to-date
334
- try {
335
- const existing = await fs.readFile(outputPath, "utf-8");
336
- // Compare ignoring the timestamp line
337
- const normalizeForCompare = (s: string) =>
338
- s.replace(/Generated at: .+/, "Generated at: <timestamp>");
339
-
340
- if (normalizeForCompare(existing) !== normalizeForCompare(code)) {
341
- console.error("Generated file is out of date!");
342
- console.error(`Run \`npx client-registry discover\` to update ${output}`);
343
- process.exit(1);
344
- }
345
-
346
- console.log("Generated file is up-to-date");
347
- return;
348
- } catch {
349
- console.error(`Generated file does not exist: ${output}`);
350
- console.error(`Run \`npx client-registry discover\` to generate it`);
351
- process.exit(1);
352
- }
353
- }
354
-
355
- // Write the file
356
- await fs.mkdir(outputDir, { recursive: true });
357
- await fs.writeFile(outputPath, code, "utf-8");
358
- console.log(`Generated: ${output}`);
359
-
360
- if (watchMode) {
361
- // Watch mode: re-run on package.json changes
362
- console.log("\nWatching for changes...");
363
- console.log("Press Ctrl+C to stop\n");
364
-
365
- const pkgJsonPath = path.join(root, "package.json");
366
- const nodeModulesPath = path.join(root, "node_modules");
367
-
368
- // Watch package.json
369
- watch(pkgJsonPath, async () => {
370
- console.log("\npackage.json changed, re-discovering...");
371
- const newCode = await runDiscovery();
372
- await fs.writeFile(outputPath, newCode, "utf-8");
373
- console.log(`Updated: ${output}`);
374
- });
375
-
376
- // Watch node_modules (for package.json files inside)
377
- // This is a simplified watch - in production you might use chokidar
378
- watch(nodeModulesPath, { recursive: true }, async (_, filename) => {
379
- if (filename?.endsWith("package.json")) {
380
- console.log("\nDependency changed, re-discovering...");
381
- const newCode = await runDiscovery();
382
- await fs.writeFile(outputPath, newCode, "utf-8");
383
- console.log(`Updated: ${output}`);
384
- }
385
- });
386
-
387
- // Keep process alive
388
- await new Promise(() => {});
389
- }
390
- }
@@ -1,231 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Client CLI
4
- *
5
- * Command-line interface for procedure discovery and library scaffolding.
6
- *
7
- * Commands:
8
- * - discover: Generate procedure imports (reads from registry or scans)
9
- * - announce: Register this package's procedures (called from postinstall)
10
- * - init: Initialize a new library with procedure support
11
- */
12
-
13
- import { readFileSync } from "node:fs";
14
- import { parseArgs } from "node:util";
15
- import { discover } from "./discover.js";
16
- import { init } from "./init.js";
17
- import { announce, unannounce } from "./announce.js";
18
-
19
- // =============================================================================
20
- // CLI Parser
21
- // =============================================================================
22
-
23
- interface ParsedArgs {
24
- command: string;
25
- options: Record<string, string | boolean | undefined>;
26
- positionals: string[];
27
- }
28
-
29
- function parseCliArgs(): ParsedArgs {
30
- const args = process.argv.slice(2);
31
- const command = args[0] ?? "help";
32
- const rest = args.slice(1);
33
-
34
- // Parse based on command
35
- switch (command) {
36
- case "discover": {
37
- const { values } = parseArgs({
38
- args: rest,
39
- options: {
40
- output: { type: "string", short: "o", default: "./__generated__/procedures.ts" },
41
- root: { type: "string", short: "r", default: process.cwd() },
42
- watch: { type: "boolean", short: "w", default: false },
43
- check: { type: "boolean", short: "c", default: false },
44
- verbose: { type: "boolean", short: "v", default: false },
45
- },
46
- allowPositionals: false,
47
- });
48
- return { command, options: values, positionals: [] };
49
- }
50
-
51
- case "init": {
52
- const { values, positionals } = parseArgs({
53
- args: rest,
54
- options: {
55
- force: { type: "boolean", short: "f", default: false },
56
- },
57
- allowPositionals: true,
58
- });
59
- return { command, options: values, positionals };
60
- }
61
-
62
- case "announce": {
63
- const { values } = parseArgs({
64
- args: rest,
65
- options: {
66
- verbose: { type: "boolean", short: "v", default: false },
67
- propagate: { type: "boolean", default: false },
68
- },
69
- allowPositionals: false,
70
- });
71
- return { command, options: values, positionals: [] };
72
- }
73
-
74
- case "unannounce": {
75
- const { values } = parseArgs({
76
- args: rest,
77
- options: {
78
- verbose: { type: "boolean", short: "v", default: false },
79
- },
80
- allowPositionals: false,
81
- });
82
- return { command, options: values, positionals: [] };
83
- }
84
-
85
- case "help":
86
- case "--help":
87
- case "-h":
88
- return { command: "help", options: {}, positionals: [] };
89
-
90
- case "version":
91
- case "--version":
92
- case "-V":
93
- return { command: "version", options: {}, positionals: [] };
94
-
95
- default:
96
- console.error(`Unknown command: ${command}`);
97
- return { command: "help", options: {}, positionals: [] };
98
- }
99
- }
100
-
101
- // =============================================================================
102
- // Help Text
103
- // =============================================================================
104
-
105
- function printHelp(): void {
106
- console.log(`
107
- client - Procedure discovery and library scaffolding CLI
108
-
109
- Usage:
110
- client <command> [options]
111
-
112
- Commands:
113
- discover Generate procedure imports (from registry or by scanning)
114
- announce Register this package's procedures (used in postinstall)
115
- init Initialize a new library with procedure support
116
- help Show this help message
117
- version Show version
118
-
119
- Examples:
120
- # Generate procedure imports from dependencies
121
- client-registry discover
122
-
123
- # Generate to custom output path
124
- client-registry discover --output ./src/generated/procedures.ts
125
-
126
- # Watch for changes (dev mode)
127
- client-registry discover --watch
128
-
129
- # Check if generated file is up-to-date (CI)
130
- client-registry discover --check
131
-
132
- # Initialize a new library
133
- client-registry init my-new-lib
134
-
135
- # Initialize in current directory
136
- client-registry init .
137
-
138
- # Register procedures (called automatically via postinstall)
139
- client-registry announce
140
-
141
- # Also add "client-registry announce" to the postinstall script of the package that depends on this one
142
- client-registry announce --propagate
143
-
144
- How it works:
145
- 1. Libraries with procedures add to package.json:
146
- { "client": { "procedures": "./dist/register.js" } }
147
-
148
- 2. Libraries add postinstall hook:
149
- { "scripts": { "postinstall": "client-registry announce" } }
150
-
151
- 3. When installed, each library announces itself to .client-registry.json
152
-
153
- 4. App runs "client-registry discover" to generate imports from the registry
154
-
155
- For more information, visit: https://mark1russell7.github.io/client/
156
- `);
157
- }
158
-
159
- function printVersion(): void {
160
- // This file is dist/procedures/cli/index.js: the package.json is three folders up
161
- const manifest = JSON.parse(readFileSync(new URL("../../../package.json", import.meta.url), "utf8")) as { version: string };
162
- console.log(`client-registry v${manifest.version}`);
163
- }
164
-
165
- // =============================================================================
166
- // Main
167
- // =============================================================================
168
-
169
- async function main(): Promise<void> {
170
- const { command, options, positionals } = parseCliArgs();
171
-
172
- try {
173
- switch (command) {
174
- case "discover":
175
- await discover({
176
- output: options["output"] as string,
177
- root: options["root"] as string,
178
- watch: options["watch"] as boolean,
179
- check: options["check"] as boolean,
180
- verbose: options["verbose"] as boolean,
181
- });
182
- break;
183
-
184
- case "init": {
185
- const name = positionals[0];
186
- if (!name) {
187
- console.error("Error: Please provide a library name");
188
- console.error("Usage: client-registry init <name>");
189
- process.exit(1);
190
- }
191
- await init({
192
- name,
193
- force: options["force"] as boolean,
194
- });
195
- break;
196
- }
197
-
198
- case "announce":
199
- // announce is best-effort for postinstall - never fail
200
- try {
201
- await announce({
202
- verbose: options["verbose"] as boolean,
203
- propagate: options["propagate"] as boolean,
204
- });
205
- } catch {
206
- // Silently ignore errors - this runs in postinstall
207
- // and should never block package installation
208
- }
209
- break;
210
-
211
- case "unannounce":
212
- await unannounce({
213
- verbose: options["verbose"] as boolean,
214
- });
215
- break;
216
-
217
- case "help":
218
- printHelp();
219
- break;
220
-
221
- case "version":
222
- printVersion();
223
- break;
224
- }
225
- } catch (error) {
226
- console.error("Error:", error instanceof Error ? error.message : error);
227
- process.exit(1);
228
- }
229
- }
230
-
231
- main();