@atcute/lex-cli 2.4.0 → 2.5.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 (57) hide show
  1. package/README.md +40 -8
  2. package/dist/cli.js +10 -168
  3. package/dist/cli.js.map +1 -1
  4. package/dist/codegen.d.ts.map +1 -1
  5. package/dist/codegen.js +76 -78
  6. package/dist/codegen.js.map +1 -1
  7. package/dist/commands/export.d.ts +13 -0
  8. package/dist/commands/export.d.ts.map +1 -0
  9. package/dist/commands/export.js +76 -0
  10. package/dist/commands/export.js.map +1 -0
  11. package/dist/commands/generate.d.ts +13 -0
  12. package/dist/commands/generate.d.ts.map +1 -0
  13. package/dist/commands/generate.js +136 -0
  14. package/dist/commands/generate.js.map +1 -0
  15. package/dist/commands/pull.d.ts +13 -0
  16. package/dist/commands/pull.d.ts.map +1 -0
  17. package/dist/{pull.js → commands/pull.js} +35 -81
  18. package/dist/commands/pull.js.map +1 -0
  19. package/dist/config.d.ts +68 -6
  20. package/dist/config.d.ts.map +1 -1
  21. package/dist/config.js +54 -3
  22. package/dist/config.js.map +1 -1
  23. package/dist/lexicon-loader.d.ts +17 -0
  24. package/dist/lexicon-loader.d.ts.map +1 -0
  25. package/dist/lexicon-loader.js +167 -0
  26. package/dist/lexicon-loader.js.map +1 -0
  27. package/dist/pull-sources/atproto.d.ts +9 -0
  28. package/dist/pull-sources/atproto.d.ts.map +1 -0
  29. package/dist/pull-sources/atproto.js +192 -0
  30. package/dist/pull-sources/atproto.js.map +1 -0
  31. package/dist/pull-sources/git.d.ts +11 -0
  32. package/dist/pull-sources/git.d.ts.map +1 -0
  33. package/dist/pull-sources/git.js +80 -0
  34. package/dist/pull-sources/git.js.map +1 -0
  35. package/dist/pull-sources/types.d.ts +16 -0
  36. package/dist/pull-sources/types.d.ts.map +1 -0
  37. package/dist/pull-sources/types.js +2 -0
  38. package/dist/pull-sources/types.js.map +1 -0
  39. package/dist/shared-options.d.ts +6 -0
  40. package/dist/shared-options.d.ts.map +1 -0
  41. package/dist/shared-options.js +11 -0
  42. package/dist/shared-options.js.map +1 -0
  43. package/package.json +10 -7
  44. package/src/cli.ts +9 -210
  45. package/src/codegen.ts +90 -88
  46. package/src/commands/export.ts +106 -0
  47. package/src/commands/generate.ts +170 -0
  48. package/src/{pull.ts → commands/pull.ts} +49 -116
  49. package/src/config.ts +67 -4
  50. package/src/lexicon-loader.ts +199 -0
  51. package/src/pull-sources/atproto.ts +243 -0
  52. package/src/pull-sources/git.ts +103 -0
  53. package/src/pull-sources/types.ts +18 -0
  54. package/src/shared-options.ts +13 -0
  55. package/dist/pull.d.ts +0 -7
  56. package/dist/pull.d.ts.map +0 -1
  57. package/dist/pull.js.map +0 -1
package/README.md CHANGED
@@ -20,19 +20,24 @@ export default defineLexiconConfig({
20
20
  then run the tool:
21
21
 
22
22
  ```
23
- npm exec lex-cli generate -c ./lex.config.js
23
+ npm exec lex-cli generate
24
24
  ```
25
25
 
26
26
  ## pulling lexicons
27
27
 
28
- configure sources to be pulled locally before code generation. pulled files are written using their
29
- nsids (e.g., `app.bsky.feed.post` becomes `app/bsky/feed/post.json`).
28
+ you can pull lexicon files from other sources.
29
+
30
+ ### git sources
31
+
32
+ pull lexicons from git repositories using sparse checkout:
30
33
 
31
34
  ```ts
32
35
  // file: lex.config.js
33
36
  import { defineLexiconConfig } from '@atcute/lex-cli';
34
37
 
35
38
  export default defineLexiconConfig({
39
+ files: ['lexicons/**/*.json'],
40
+ outdir: 'src/lexicons/',
36
41
  pull: {
37
42
  outdir: 'lexicons/',
38
43
  clean: true,
@@ -45,23 +50,50 @@ export default defineLexiconConfig({
45
50
  },
46
51
  ],
47
52
  },
53
+ });
54
+ ```
55
+
56
+ ### atproto sources
57
+
58
+ pull lexicons directly from the AT Protocol network.
59
+
60
+ ```ts
61
+ export default defineLexiconConfig({
48
62
  files: ['lexicons/**/*.json'],
49
63
  outdir: 'src/lexicons/',
64
+ pull: {
65
+ outdir: 'lexicons/',
66
+ sources: [
67
+ {
68
+ type: 'atproto',
69
+ mode: 'nsids',
70
+ nsids: ['app.bsky.feed.post', 'app.bsky.actor.profile'],
71
+ },
72
+ {
73
+ type: 'atproto',
74
+ mode: 'authority',
75
+ authority: 'atproto-lexicons.bsky.social',
76
+ pattern: ['com.atproto.*'],
77
+ },
78
+ ],
79
+ },
50
80
  });
51
81
  ```
52
82
 
83
+ ### running the pull command
84
+
53
85
  pull the lexicons to disk, then generate types from them:
54
86
 
55
87
  ```
56
- npm exec lex-cli pull -c ./lex.config.js
57
- npm exec lex-cli generate -c ./lex.config.js
88
+ npm exec lex-cli pull
89
+ npm exec lex-cli generate
58
90
  ```
59
91
 
60
92
  ## publishing your schemas
61
93
 
62
- if you're packaging your generated schemas as a publishable library, add the `atcute:lexicons`
63
- field to your package.json. this allows other projects to automatically discover and import your
64
- schemas without manual configuration.
94
+ if you're packaging your generated schemas as a publishable library, add the `atcute:lexicons` field
95
+ to your package.json. this allows other projects to automatically discover and import your schemas
96
+ without manual configuration.
65
97
 
66
98
  ```json
67
99
  {
package/dist/cli.js CHANGED
@@ -1,175 +1,17 @@
1
- import * as fs from 'node:fs/promises';
2
- import * as path from 'node:path';
3
- import { lexiconDoc, refineLexiconDoc } from '@atcute/lexicon-doc';
4
- import { object } from '@optique/core/constructs';
5
- import { command, constant, option } from '@optique/core/primitives';
6
1
  import { or } from '@optique/core/constructs';
7
2
  import { run } from '@optique/run';
8
- import { path as pathParser } from '@optique/run/valueparser';
9
- import pc from 'picocolors';
10
- import { generateLexiconApi } from './codegen.js';
11
- import { loadConfig } from './config.js';
12
- import { packageJsonSchema } from './lexicon-metadata.js';
13
- import { runPull } from './pull.js';
14
- /**
15
- * Resolves package imports to ImportMapping[]
16
- */
17
- const resolveImportsToMappings = async (imports, configDirname) => {
18
- const mappings = [];
19
- for (const packageName of imports) {
20
- // Walk up from config directory to find package in node_modules
21
- let packageJson;
22
- let currentDir = configDirname;
23
- let found = false;
24
- while (currentDir !== path.dirname(currentDir)) {
25
- const candidatePath = path.join(currentDir, 'node_modules', packageName, 'package.json');
26
- try {
27
- const content = await fs.readFile(candidatePath, 'utf8');
28
- packageJson = JSON.parse(content);
29
- found = true;
30
- break;
31
- }
32
- catch (err) {
33
- // Only continue to parent if file not found
34
- if (err.code !== 'ENOENT') {
35
- console.error(pc.bold(pc.red(`failed to read package.json for "${packageName}":`)));
36
- console.error(err);
37
- process.exit(1);
38
- }
39
- // Not found, try parent directory
40
- currentDir = path.dirname(currentDir);
41
- }
42
- }
43
- if (!found) {
44
- console.error(pc.bold(pc.red(`failed to resolve package "${packageName}"`)));
45
- console.error(`Could not find package in node_modules starting from ${configDirname}`);
46
- process.exit(1);
47
- }
48
- // Validate package.json
49
- const result = packageJsonSchema.try(packageJson, { mode: 'passthrough' });
50
- if (!result.ok) {
51
- console.error(pc.bold(pc.red(`invalid atcute:lexicons in "${packageName}":`)));
52
- console.error(result.message);
53
- for (const issue of result.issues) {
54
- console.log(`- ${issue.code} at .${issue.path.join('.')}`);
55
- }
56
- process.exit(1);
57
- }
58
- const lexicons = result.value['atcute:lexicons'];
59
- if (!lexicons?.mappings) {
60
- continue;
61
- }
62
- // Convert mapping to ImportMapping[]
63
- for (const [pattern, entry] of Object.entries(lexicons.mappings)) {
64
- const isWildcard = pattern.endsWith('.*');
65
- mappings.push({
66
- nsid: [pattern],
67
- imports: (nsid) => {
68
- // Check if pattern matches
69
- if (isWildcard) {
70
- if (!nsid.startsWith(pattern.slice(0, -1))) {
71
- throw new Error(`NSID ${nsid} does not match pattern ${pattern}`);
72
- }
73
- }
74
- else {
75
- if (nsid !== pattern) {
76
- throw new Error(`NSID ${nsid} does not match pattern ${pattern}`);
77
- }
78
- }
79
- const nsidPrefix = isWildcard ? pattern.slice(0, -2) : pattern;
80
- const nsidRemainder = isWildcard ? nsid.slice(nsidPrefix.length + 1) : '';
81
- let expandedPath = entry.path
82
- .replaceAll('{{nsid}}', nsid.replaceAll('.', '/'))
83
- .replaceAll('{{nsid_remainder}}', nsidRemainder.replaceAll('.', '/'))
84
- .replaceAll('{{nsid_prefix}}', nsidPrefix.replaceAll('.', '/'));
85
- if (expandedPath === '.') {
86
- expandedPath = packageName;
87
- }
88
- else if (expandedPath.startsWith('./')) {
89
- expandedPath = `${packageName}/${expandedPath.slice(2)}`;
90
- }
91
- return {
92
- type: entry.type,
93
- from: expandedPath,
94
- };
95
- },
96
- });
97
- }
98
- }
99
- return mappings;
100
- };
101
- const parser = or(command('generate', object({
102
- type: constant('generate'),
103
- config: option('-c', '--config', pathParser({ metavar: 'CONFIG' })),
104
- })), command('pull', object({
105
- type: constant('pull'),
106
- config: option('-c', '--config', pathParser({ metavar: 'CONFIG' })),
107
- })));
108
- const result = run(parser, { programName: 'lex-cli' });
3
+ import { exportCommandSchema, runExport } from './commands/export.js';
4
+ import { generateCommandSchema, runGenerate } from './commands/generate.js';
5
+ import { pullCommandSchema, runPull } from './commands/pull.js';
6
+ const parser = or(generateCommandSchema, pullCommandSchema, exportCommandSchema);
7
+ const result = run(parser, { programName: 'lex-cli', help: 'both' });
109
8
  if (result.type === 'generate') {
110
- const config = await loadConfig(result.config);
111
- // Resolve imports to mappings
112
- const importMappings = config.imports ? await resolveImportsToMappings(config.imports, config.root) : [];
113
- const allMappings = [...importMappings, ...(config.mappings ?? [])];
114
- const documents = [];
115
- for await (const filename of fs.glob(config.files, { cwd: config.root })) {
116
- let source;
117
- try {
118
- source = await fs.readFile(path.join(config.root, filename), 'utf8');
119
- }
120
- catch (err) {
121
- console.error(pc.bold(pc.red(`file read error with "${filename}"`)));
122
- console.error(err);
123
- process.exit(1);
124
- }
125
- let json;
126
- try {
127
- json = JSON.parse(source);
128
- }
129
- catch (err) {
130
- console.error(pc.bold(pc.red(`json parse error in "${filename}"`)));
131
- console.error(err);
132
- process.exit(1);
133
- }
134
- const result = lexiconDoc.try(json, { mode: 'strip' });
135
- if (!result.ok) {
136
- console.error(pc.bold(pc.red(`schema validation failed for "${filename}"`)));
137
- console.error(result.message);
138
- for (const issue of result.issues) {
139
- console.log(`- ${issue.code} at .${issue.path.join('.')}`);
140
- }
141
- process.exit(1);
142
- }
143
- const issues = refineLexiconDoc(result.value, true);
144
- if (issues.length > 0) {
145
- console.error(pc.bold(pc.red(`lint validation failed for "${filename}"`)));
146
- for (const issue of issues) {
147
- console.log(`- ${issue.message} at .${issue.path.join('.')}`);
148
- }
149
- process.exit(1);
150
- }
151
- documents.push(result.value);
152
- }
153
- const generationResult = await generateLexiconApi({
154
- documents: documents,
155
- mappings: allMappings,
156
- modules: {
157
- importSuffix: config.modules?.importSuffix ?? '.js',
158
- },
159
- prettier: {
160
- cwd: process.cwd(),
161
- },
162
- });
163
- const outdir = path.join(config.root, config.outdir);
164
- for (const file of generationResult.files) {
165
- const filename = path.join(outdir, file.filename);
166
- const dirname = path.dirname(filename);
167
- await fs.mkdir(dirname, { recursive: true });
168
- await fs.writeFile(filename, file.code);
169
- }
9
+ await runGenerate(result);
170
10
  }
171
11
  else if (result.type === 'pull') {
172
- const config = await loadConfig(result.config);
173
- await runPull(config);
12
+ await runPull(result);
13
+ }
14
+ else if (result.type === 'export') {
15
+ await runExport(result);
174
16
  }
175
17
  //# sourceMappingURL=cli.js.map
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,0BAA0B,CAAC;AAC9C,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAEnC,OAAO,EAAE,mBAAmB,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACtE,OAAO,EAAE,qBAAqB,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC5E,OAAO,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAEhE,MAAM,MAAM,GAAG,EAAE,CAAC,qBAAqB,EAAE,iBAAiB,EAAE,mBAAmB,CAAC,CAAC;AAEjF,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,EAAE,WAAW,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;AAErE,IAAI,MAAM,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;IAChC,MAAM,WAAW,CAAC,MAAM,CAAC,CAAC;AAC3B,CAAC;KAAM,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;IACnC,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;AACvB,CAAC;KAAM,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;IACrC,MAAM,SAAS,CAAC,MAAM,CAAC,CAAC;AACzB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"codegen.d.ts","sourceRoot":"","sources":["../src/codegen.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAEX,UAAU,EAWV,MAAM,qBAAqB,CAAC;AAE7B,MAAM,WAAW,UAAU;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,aAAa;IAC7B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,OAAO,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK;QAAE,IAAI,EAAE,OAAO,GAAG,WAAW,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACpF;AAED,MAAM,WAAW,iBAAiB;IACjC,SAAS,EAAE,UAAU,EAAE,CAAC;IACxB,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,OAAO,EAAE;QACR,YAAY,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,QAAQ,EAAE;QACT,GAAG,EAAE,MAAM,CAAC;KACZ,CAAC;CACF;AAED,MAAM,WAAW,gBAAgB;IAChC,KAAK,EAAE,UAAU,EAAE,CAAC;CACpB;AAuBD,eAAO,MAAM,kBAAkB,GAAU,MAAM,iBAAiB,KAAG,OAAO,CAAC,gBAAgB,CAmT1F,CAAC"}
1
+ {"version":3,"file":"codegen.d.ts","sourceRoot":"","sources":["../src/codegen.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAEX,UAAU,EAWV,MAAM,qBAAqB,CAAC;AAE7B,MAAM,WAAW,UAAU;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,aAAa;IAC7B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,OAAO,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK;QAAE,IAAI,EAAE,OAAO,GAAG,WAAW,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACpF;AAED,MAAM,WAAW,iBAAiB;IACjC,SAAS,EAAE,UAAU,EAAE,CAAC;IACxB,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,OAAO,EAAE;QACR,YAAY,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,QAAQ,EAAE;QACT,GAAG,EAAE,MAAM,CAAC;KACZ,CAAC;CACF;AAED,MAAM,WAAW,gBAAgB;IAChC,KAAK,EAAE,UAAU,EAAE,CAAC;CACpB;AAkDD,eAAO,MAAM,kBAAkB,GAAU,MAAM,iBAAiB,KAAG,OAAO,CAAC,gBAAgB,CAmT1F,CAAC"}
package/dist/codegen.js CHANGED
@@ -1,6 +1,23 @@
1
1
  import { dirname as getDirname, relative as getRelativePath } from 'node:path/posix';
2
2
  import * as prettier from 'prettier';
3
3
  const lit = JSON.stringify;
4
+ const toLexUri = (path) => {
5
+ const { nsid, defId } = path;
6
+ return defId === 'main' ? nsid : `${nsid}#${defId}`;
7
+ };
8
+ const resolvePath = (from, ref) => {
9
+ const index = ref.indexOf('#');
10
+ // nsid (no hash)
11
+ if (index === -1) {
12
+ return { nsid: ref, defId: 'main' };
13
+ }
14
+ // #defId (local ref)
15
+ if (index === 0) {
16
+ return { nsid: from.nsid, defId: ref.slice(1) };
17
+ }
18
+ // nsid#defId (full ref)
19
+ return { nsid: ref.slice(0, index), defId: ref.slice(index + 1) };
20
+ };
4
21
  const resolveExternalImport = (nsid, mappings) => {
5
22
  return mappings.find((mapping) => {
6
23
  return mapping.nsid.some((pattern) => {
@@ -51,57 +68,57 @@ export const generateLexiconApi = async (opts) => {
51
68
  });
52
69
  for (const defId of sortedDefIds) {
53
70
  const def = doc.defs[defId];
54
- const defUri = `${doc.id}#${defId}`;
71
+ const path = { nsid: doc.id, defId };
55
72
  const camelcased = toCamelCase(defId);
56
73
  const varname = `${camelcased}Schema`;
57
74
  let result;
58
75
  switch (def.type) {
59
76
  case 'query': {
60
- result = generateXrpcQuery(imports, defUri, def);
77
+ result = generateXrpcQuery(imports, path, def);
61
78
  file.imports += `import type {} from '@atcute/lexicons/ambient';\n`;
62
79
  file.ambients += `declare module '@atcute/lexicons/ambient' {\n`;
63
80
  file.ambients += ` interface XRPCQueries {\n`;
64
- file.ambients += ` ${lit(stripMainHash(defUri))}: ${camelcased}Schema;\n`;
81
+ file.ambients += ` ${lit(toLexUri(path))}: ${camelcased}Schema;\n`;
65
82
  file.ambients += ` }\n`;
66
83
  file.ambients += `}`;
67
84
  break;
68
85
  }
69
86
  case 'procedure': {
70
- result = generateXrpcProcedure(imports, defUri, def);
87
+ result = generateXrpcProcedure(imports, path, def);
71
88
  file.imports += `import type {} from '@atcute/lexicons/ambient';\n`;
72
89
  file.ambients += `declare module '@atcute/lexicons/ambient' {\n`;
73
90
  file.ambients += ` interface XRPCProcedures {\n`;
74
- file.ambients += ` ${lit(stripMainHash(defUri))}: ${camelcased}Schema;\n`;
91
+ file.ambients += ` ${lit(toLexUri(path))}: ${camelcased}Schema;\n`;
75
92
  file.ambients += ` }\n`;
76
93
  file.ambients += `}`;
77
94
  break;
78
95
  }
79
96
  case 'subscription': {
80
- result = generateXrpcSubscription(imports, defUri, def);
97
+ result = generateXrpcSubscription(imports, path, def);
81
98
  file.imports += `import type {} from '@atcute/lexicons/ambient';\n`;
82
99
  file.ambients += `declare module '@atcute/lexicons/ambient' {\n`;
83
100
  file.ambients += ` interface XRPCSubscriptions {\n`;
84
- file.ambients += ` ${lit(stripMainHash(defUri))}: ${camelcased}Schema;\n`;
101
+ file.ambients += ` ${lit(toLexUri(path))}: ${camelcased}Schema;\n`;
85
102
  file.ambients += ` }\n`;
86
103
  file.ambients += `}`;
87
104
  break;
88
105
  }
89
106
  case 'object': {
90
- result = generateObject(imports, defUri, def);
107
+ result = generateObject(imports, path, def);
91
108
  break;
92
109
  }
93
110
  case 'record': {
94
- result = generateRecord(imports, defUri, def);
111
+ result = generateRecord(imports, path, def);
95
112
  file.imports += `import type {} from '@atcute/lexicons/ambient';\n`;
96
113
  file.ambients += `declare module '@atcute/lexicons/ambient' {\n`;
97
114
  file.ambients += ` interface Records {\n`;
98
- file.ambients += ` ${lit(stripMainHash(defUri))}: ${camelcased}Schema;\n`;
115
+ file.ambients += ` ${lit(toLexUri(path))}: ${camelcased}Schema;\n`;
99
116
  file.ambients += ` }\n`;
100
117
  file.ambients += `}`;
101
118
  break;
102
119
  }
103
120
  case 'token': {
104
- result = `${PURE} v.literal(${lit(stripMainHash(defUri))})`;
121
+ result = `${PURE} v.literal(${lit(toLexUri(path))})`;
105
122
  break;
106
123
  }
107
124
  case 'permission-set': {
@@ -109,7 +126,7 @@ export const generateLexiconApi = async (opts) => {
109
126
  continue;
110
127
  }
111
128
  default: {
112
- result = generateType(imports, defUri, def);
129
+ result = generateType(imports, path, def);
113
130
  break;
114
131
  }
115
132
  }
@@ -279,32 +296,32 @@ export const generateLexiconApi = async (opts) => {
279
296
  }
280
297
  return { files };
281
298
  };
282
- const generateXrpcQuery = (imports, defUri, spec) => {
283
- const params = generateXrpcParameters(imports, defUri, spec.parameters);
284
- const output = generateXrpcBody(imports, defUri, spec.output);
285
- return `${PURE} v.query(${lit(stripMainHash(defUri))}, {\n"params": ${params}, "output": ${output} })`;
299
+ const generateXrpcQuery = (imports, path, spec) => {
300
+ const params = generateXrpcParameters(imports, path, spec.parameters);
301
+ const output = generateXrpcBody(imports, path, spec.output);
302
+ return `${PURE} v.query(${lit(toLexUri(path))}, {\n"params": ${params}, "output": ${output} })`;
286
303
  };
287
- const generateXrpcProcedure = (imports, defUri, spec) => {
288
- const params = generateXrpcParameters(imports, defUri, spec.parameters);
289
- const input = generateXrpcBody(imports, defUri, spec.input);
290
- const output = generateXrpcBody(imports, defUri, spec.output);
291
- return `${PURE} v.procedure(${lit(stripMainHash(defUri))}, {\n"params": ${params}, "input": ${input}, "output": ${output} })`;
304
+ const generateXrpcProcedure = (imports, path, spec) => {
305
+ const params = generateXrpcParameters(imports, path, spec.parameters);
306
+ const input = generateXrpcBody(imports, path, spec.input);
307
+ const output = generateXrpcBody(imports, path, spec.output);
308
+ return `${PURE} v.procedure(${lit(toLexUri(path))}, {\n"params": ${params}, "input": ${input}, "output": ${output} })`;
292
309
  };
293
- const generateXrpcSubscription = (imports, defUri, spec) => {
310
+ const generateXrpcSubscription = (imports, path, spec) => {
294
311
  const schema = spec.message?.schema;
295
- const params = generateXrpcParameters(imports, defUri, spec.parameters);
312
+ const params = generateXrpcParameters(imports, path, spec.parameters);
296
313
  let inner = ``;
297
314
  inner += `"params": ${params},`;
298
315
  if (schema) {
299
- const res = generateType(imports, defUri, schema);
316
+ const res = generateType(imports, path, schema);
300
317
  inner += `get "message" () { return ${res} },`;
301
318
  }
302
319
  else {
303
320
  inner += `"message": null,`;
304
321
  }
305
- return `${PURE} v.subscription(${lit(stripMainHash(defUri))}, {\n${inner}})`;
322
+ return `${PURE} v.subscription(${lit(toLexUri(path))}, {\n${inner}})`;
306
323
  };
307
- const generateXrpcBody = (imports, defUri, spec) => {
324
+ const generateXrpcBody = (imports, path, spec) => {
308
325
  if (spec === undefined) {
309
326
  return `null`;
310
327
  }
@@ -314,11 +331,11 @@ const generateXrpcBody = (imports, defUri, spec) => {
314
331
  let inner = ``;
315
332
  inner += `"type": "lex",`;
316
333
  if (schema.type === 'object') {
317
- const res = generateObject(imports, defUri, schema, 'none');
334
+ const res = generateObject(imports, path, schema, 'none');
318
335
  inner += `"schema": ${res},`;
319
336
  }
320
337
  else {
321
- const res = generateType(imports, defUri, schema);
338
+ const res = generateType(imports, path, schema);
322
339
  inner += `get "schema" () { return ${res} },`;
323
340
  }
324
341
  return `{\n${inner}}`;
@@ -334,7 +351,7 @@ const generateXrpcBody = (imports, defUri, spec) => {
334
351
  }
335
352
  return `null`;
336
353
  };
337
- const generateXrpcParameters = (imports, defUri, spec) => {
354
+ const generateXrpcParameters = (imports, path, spec) => {
338
355
  if (spec === undefined) {
339
356
  return `null`;
340
357
  }
@@ -363,10 +380,10 @@ const generateXrpcParameters = (imports, defUri, spec) => {
363
380
  required: spec.required,
364
381
  properties: transformedProperties ?? originalProperties,
365
382
  };
366
- return generateObject(imports, defUri, mask, 'none');
383
+ return generateObject(imports, path, mask, 'none');
367
384
  };
368
- const generateRecord = (imports, defUri, spec) => {
369
- const schema = generateObject(imports, defUri, spec.record, 'required');
385
+ const generateRecord = (imports, path, spec) => {
386
+ const schema = generateObject(imports, path, spec.record, 'required');
370
387
  let key = `${PURE} v.string()`;
371
388
  if (spec.key) {
372
389
  if (spec.key === 'tid') {
@@ -381,17 +398,17 @@ const generateRecord = (imports, defUri, spec) => {
381
398
  }
382
399
  return `${PURE} v.record(${key}, ${schema})`;
383
400
  };
384
- const generateObject = (imports, defUri, spec, writeType = 'optional') => {
401
+ const generateObject = (imports, path, spec, writeType = 'optional') => {
385
402
  const required = new Set(spec.required);
386
403
  const nullable = new Set(spec.nullable);
387
404
  let inner = ``;
388
405
  switch (writeType) {
389
406
  case 'optional': {
390
- inner += `"$type": ${PURE} v.optional(${PURE} v.literal(${lit(stripMainHash(defUri))})),`;
407
+ inner += `"$type": ${PURE} v.optional(${PURE} v.literal(${lit(toLexUri(path))})),`;
391
408
  break;
392
409
  }
393
410
  case 'required': {
394
- inner += `"$type": ${PURE} v.literal(${lit(stripMainHash(defUri))}),`;
411
+ inner += `"$type": ${PURE} v.literal(${lit(toLexUri(path))}),`;
395
412
  break;
396
413
  }
397
414
  }
@@ -406,9 +423,9 @@ const generateObject = (imports, defUri, spec, writeType = 'optional') => {
406
423
  });
407
424
  for (const [prop, propSpec] of sortedEntries) {
408
425
  const lazy = isRefVariant(propSpec.type === 'array' ? propSpec.items : propSpec);
409
- const optional = !required.has(prop) && !('default' in propSpec);
426
+ const optional = !required.has(prop) && !('default' in propSpec && propSpec.default !== undefined);
410
427
  const nulled = nullable.has(prop);
411
- let call = generateType(imports, defUri, propSpec, lazy);
428
+ let call = generateType(imports, path, propSpec, lazy);
412
429
  if (nulled) {
413
430
  call = `${PURE} v.nullable(${call})`;
414
431
  }
@@ -524,53 +541,44 @@ const generateJsdocField = (spec) => {
524
541
  }
525
542
  return res;
526
543
  };
527
- const generateType = (imports, defUri, spec, lazy = false) => {
544
+ const generateType = (imports, path, spec, lazy = false) => {
528
545
  switch (spec.type) {
529
546
  // LexRefVariant
530
547
  case 'ref': {
531
- const ref = spec.ref;
532
- if (ref.startsWith('#')) {
533
- const id = ref.slice(1);
534
- return `${toCamelCase(id)}Schema`;
535
- }
536
- else {
537
- const [ns, id = 'main'] = ref.split('#');
538
- if (ns === stripHash(defUri)) {
539
- return `${toCamelCase(id)}Schema`;
540
- }
541
- imports.add(ns);
542
- return `${toTitleCase(ns)}.${toCamelCase(id)}Schema`;
548
+ const refPath = resolvePath(path, spec.ref);
549
+ if (refPath.nsid === path.nsid) {
550
+ return `${toCamelCase(refPath.defId)}Schema`;
543
551
  }
552
+ imports.add(refPath.nsid);
553
+ return `${toTitleCase(refPath.nsid)}.${toCamelCase(refPath.defId)}Schema`;
544
554
  }
545
555
  case 'union': {
546
- const normalizedRefs = spec.refs
556
+ const refs = spec.refs
547
557
  .map((ref) => {
548
- if (ref.startsWith('#')) {
549
- return ref;
558
+ const refPath = resolvePath(path, ref);
559
+ return { path: refPath, uri: toLexUri(refPath) };
560
+ })
561
+ .sort((a, b) => {
562
+ if (a.uri < b.uri) {
563
+ return -1;
550
564
  }
551
- const [ns, id = 'main'] = ref.split('#');
552
- if (ns === stripHash(defUri)) {
553
- return `#${id}`;
565
+ if (a.uri > b.uri) {
566
+ return 1;
554
567
  }
555
- return `${ns}#${id}`;
568
+ return 0;
556
569
  })
557
- .sort();
558
- const refs = normalizedRefs.map((ref) => {
559
- if (ref.startsWith('#')) {
560
- const id = ref.slice(1);
561
- return `${toCamelCase(id)}Schema`;
562
- }
563
- else {
564
- const [ns, id = 'main'] = ref.split('#');
565
- imports.add(ns);
566
- return `${toTitleCase(ns)}.${toCamelCase(id)}Schema`;
570
+ .map(({ path: refPath }) => {
571
+ if (refPath.nsid === path.nsid) {
572
+ return `${toCamelCase(refPath.defId)}Schema`;
567
573
  }
574
+ imports.add(refPath.nsid);
575
+ return `${toTitleCase(refPath.nsid)}.${toCamelCase(refPath.defId)}Schema`;
568
576
  });
569
577
  return `${PURE} v.variant([${refs.join(', ')}]${spec.closed ? `, true` : ``})`;
570
578
  }
571
579
  // LexArray
572
580
  case 'array': {
573
- let item = generateType(imports, defUri, spec.items);
581
+ let item = generateType(imports, path, spec.items);
574
582
  if (!lazy && (spec.items.type === 'ref' || spec.items.type === 'union')) {
575
583
  item = `(() => { return ${item}; })`;
576
584
  }
@@ -740,16 +748,6 @@ const isRefVariant = (spec) => {
740
748
  const type = spec.type;
741
749
  return type === 'ref' || type === 'union';
742
750
  };
743
- const stripHash = (defUri) => {
744
- const index = defUri.indexOf('#');
745
- if (index === -1) {
746
- return defUri;
747
- }
748
- return defUri.slice(0, index);
749
- };
750
- const stripMainHash = (defUri) => {
751
- return defUri.endsWith('#main') ? defUri.slice(0, -'#main'.length) : defUri;
752
- };
753
751
  const toTitleCase = (v) => {
754
752
  v = v.replace(/^([a-z])/gi, (_, g) => g.toUpperCase());
755
753
  v = v.replace(/[.#-]([a-z])/gi, (_, g) => g.toUpperCase());