@ic-reactor/codegen 0.12.0 → 0.12.1
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/README.md +0 -1
- package/dist/index.cjs +226 -625
- package/dist/index.d.cts +111 -4
- package/dist/index.d.ts +111 -4
- package/dist/index.js +216 -26
- package/package.json +3 -13
- package/src/generators/reactor.ts +15 -4
- package/src/index.ts +19 -3
- package/src/pipeline.ts +36 -9
- package/src/validate.test.ts +393 -0
- package/src/validate.ts +369 -0
- package/dist/chunk-VBCR5IVT.js +0 -609
- package/dist/renderer.cjs +0 -639
- package/dist/renderer.d.cts +0 -33
- package/dist/renderer.d.ts +0 -33
- package/dist/renderer.js +0 -14
- package/src/metadata-rules.json +0 -144
- package/src/metadata.ts +0 -157
- package/src/renderer.test.ts +0 -573
- package/src/renderer.ts +0 -515
package/dist/index.cjs
CHANGED
|
@@ -30,24 +30,33 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
CANISTER_NAME_PATTERN: () => CANISTER_NAME_PATTERN,
|
|
34
|
+
CODEGEN_TARGETS: () => CODEGEN_TARGETS,
|
|
35
|
+
CodegenConfigError: () => CodegenConfigError,
|
|
36
|
+
REACTOR_CLASS_NAMES: () => REACTOR_CLASS_NAMES,
|
|
37
|
+
assertContainedPath: () => assertContainedPath,
|
|
38
|
+
assertOneOf: () => assertOneOf,
|
|
39
|
+
assertSafeCanisterConfig: () => assertSafeCanisterConfig,
|
|
40
|
+
assertSafeCanisterName: () => assertSafeCanisterName,
|
|
41
|
+
assertSafeModuleSpecifier: () => assertSafeModuleSpecifier,
|
|
33
42
|
declarationsExist: () => declarationsExist,
|
|
34
43
|
extractMethods: () => extractMethods,
|
|
35
44
|
generateClientFile: () => generateClientFile,
|
|
36
|
-
generateCodecDeclarations: () => generateCodecDeclarations,
|
|
37
45
|
generateDeclarations: () => generateDeclarations,
|
|
38
46
|
generateReactorEntryFile: () => generateReactorEntryFile,
|
|
39
47
|
generateReactorFile: () => generateReactorFile,
|
|
40
48
|
getReactorName: () => getReactorName,
|
|
41
49
|
getServiceTypeName: () => getServiceTypeName,
|
|
42
50
|
parseDIDFile: () => parseDIDFile,
|
|
51
|
+
resolveContainedOutDir: () => resolveContainedOutDir,
|
|
43
52
|
runCanisterPipeline: () => runCanisterPipeline,
|
|
44
53
|
toPascalCase: () => toPascalCase
|
|
45
54
|
});
|
|
46
55
|
module.exports = __toCommonJS(index_exports);
|
|
47
56
|
|
|
48
57
|
// src/pipeline.ts
|
|
49
|
-
var
|
|
50
|
-
var
|
|
58
|
+
var import_node_fs3 = __toESM(require("fs"), 1);
|
|
59
|
+
var import_node_path4 = __toESM(require("path"), 1);
|
|
51
60
|
|
|
52
61
|
// src/generators/declarations.ts
|
|
53
62
|
var import_parser = require("@ic-reactor/parser");
|
|
@@ -123,6 +132,9 @@ function getReactorName(canisterName) {
|
|
|
123
132
|
function getServiceTypeName(canisterName) {
|
|
124
133
|
return `${toPascalCase(canisterName)}Service`;
|
|
125
134
|
}
|
|
135
|
+
function getHookPrefix(canisterName) {
|
|
136
|
+
return toPascalCase(canisterName);
|
|
137
|
+
}
|
|
126
138
|
|
|
127
139
|
// src/generators/reactor.ts
|
|
128
140
|
function getReactorClassImportSource(reactorClass, runtimeTarget) {
|
|
@@ -134,6 +146,10 @@ function getReactorClassImportSource(reactorClass, runtimeTarget) {
|
|
|
134
146
|
case "CandidDisplayReactor":
|
|
135
147
|
case "MetadataDisplayReactor":
|
|
136
148
|
return "@ic-reactor/candid";
|
|
149
|
+
default:
|
|
150
|
+
throw new Error(
|
|
151
|
+
`Unknown reactor class ${JSON.stringify(reactorClass)}. Expected one of: Reactor, DisplayReactor, CandidReactor, CandidDisplayReactor, MetadataDisplayReactor.`
|
|
152
|
+
);
|
|
137
153
|
}
|
|
138
154
|
}
|
|
139
155
|
function generateReactorFile(options) {
|
|
@@ -167,9 +183,9 @@ export const {
|
|
|
167
183
|
useActorMethod: use${pascalName}Method,
|
|
168
184
|
} = createActorHooks(${reactorName})
|
|
169
185
|
` : "";
|
|
170
|
-
return `${runtimeTarget === "react" ? 'import { createActorHooks } from "@ic-reactor/react"\n' : ""}import { ${reactorClass} } from
|
|
171
|
-
import { clientManager } from
|
|
172
|
-
import { idlFactory, type _SERVICE } from
|
|
186
|
+
return `${runtimeTarget === "react" ? 'import { createActorHooks } from "@ic-reactor/react"\n' : ""}import { ${reactorClass} } from ${JSON.stringify(reactorImportSource)}
|
|
187
|
+
import { clientManager } from ${JSON.stringify(clientManagerPath)}
|
|
188
|
+
import { idlFactory, type _SERVICE } from ${JSON.stringify(declarationsPath)}
|
|
173
189
|
|
|
174
190
|
export type ${serviceName} = _SERVICE
|
|
175
191
|
|
|
@@ -185,7 +201,7 @@ export type ${serviceName} = _SERVICE
|
|
|
185
201
|
export const ${reactorName} = new ${reactorClass}<${serviceName}>({
|
|
186
202
|
clientManager,
|
|
187
203
|
idlFactory,
|
|
188
|
-
${canisterIdLine} name:
|
|
204
|
+
${canisterIdLine} name: ${JSON.stringify(canisterName)},
|
|
189
205
|
})${hookExports || "\n"}`;
|
|
190
206
|
}
|
|
191
207
|
function generateReactorEntryFile() {
|
|
@@ -208,6 +224,162 @@ export * from "./index.generated"
|
|
|
208
224
|
`;
|
|
209
225
|
}
|
|
210
226
|
|
|
227
|
+
// src/validate.ts
|
|
228
|
+
var import_node_fs2 = __toESM(require("fs"), 1);
|
|
229
|
+
var import_node_path3 = __toESM(require("path"), 1);
|
|
230
|
+
var CodegenConfigError = class extends Error {
|
|
231
|
+
name = "CodegenConfigError";
|
|
232
|
+
constructor(message) {
|
|
233
|
+
super(message);
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
var CANISTER_NAME_PATTERN = /^[A-Za-z0-9_.-]+$/;
|
|
237
|
+
var IDENTIFIER_PATTERN = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
238
|
+
var MAX_CANISTER_NAME_LENGTH = 64;
|
|
239
|
+
var HAS_URI_SCHEME = /^(?:[A-Za-z][A-Za-z0-9+.-]*:|\/\/)/;
|
|
240
|
+
var BREAKS_OUT_OF_LITERAL = /["'`\\\u0000-\u001f\u2028\u2029]/;
|
|
241
|
+
function assertSafeCanisterName(name) {
|
|
242
|
+
if (typeof name !== "string" || name.length === 0) {
|
|
243
|
+
throw new CodegenConfigError(
|
|
244
|
+
`Invalid canister name: expected a non-empty string, received ${name === void 0 ? "undefined" : JSON.stringify(name)}. Check the "canisters" entries in your ic-reactor.json (or the plugin's "canisters" option).`
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
if (name.length > MAX_CANISTER_NAME_LENGTH) {
|
|
248
|
+
throw new CodegenConfigError(
|
|
249
|
+
`Invalid canister name ${JSON.stringify(name)}: must be at most ${MAX_CANISTER_NAME_LENGTH} characters.`
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
if (!CANISTER_NAME_PATTERN.test(name)) {
|
|
253
|
+
throw new CodegenConfigError(
|
|
254
|
+
`Invalid canister name ${JSON.stringify(name)}: may contain only letters, digits, "_", "." and "-" (${CANISTER_NAME_PATTERN.source}). Canister names become directory names, so path separators, quotes and whitespace are not allowed.`
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
if (name === "." || name === "..") {
|
|
258
|
+
throw new CodegenConfigError(
|
|
259
|
+
`Invalid canister name ${JSON.stringify(name)}: refers to a directory, not a canister.`
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
if (toPascalCase(name).length === 0) {
|
|
263
|
+
throw new CodegenConfigError(
|
|
264
|
+
`Invalid canister name ${JSON.stringify(name)}: contains no letters or digits, so it collapses to an empty identifier in generated code.`
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
const derived = [
|
|
268
|
+
["reactor constant", getReactorName(name)],
|
|
269
|
+
["service type", getServiceTypeName(name)],
|
|
270
|
+
["hook name", `use${getHookPrefix(name)}Query`]
|
|
271
|
+
];
|
|
272
|
+
for (const [what, identifier] of derived) {
|
|
273
|
+
if (!IDENTIFIER_PATTERN.test(identifier)) {
|
|
274
|
+
throw new CodegenConfigError(
|
|
275
|
+
`Invalid canister name ${JSON.stringify(name)}: it derives the ${what} ${JSON.stringify(identifier)}, which is not a valid TypeScript identifier. Rename the canister so it does not begin with a digit.`
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
function assertSafeModuleSpecifier(label, specifier) {
|
|
281
|
+
if (typeof specifier !== "string" || specifier.length === 0) {
|
|
282
|
+
throw new CodegenConfigError(
|
|
283
|
+
`Invalid ${label}: expected a non-empty string, received ${specifier === void 0 ? "undefined" : JSON.stringify(specifier)}.`
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
if (BREAKS_OUT_OF_LITERAL.test(specifier)) {
|
|
287
|
+
throw new CodegenConfigError(
|
|
288
|
+
`Invalid ${label} ${JSON.stringify(specifier)}: must not contain quotes, backslashes or control characters.`
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
if (/^\s|\s$/.test(specifier)) {
|
|
292
|
+
throw new CodegenConfigError(
|
|
293
|
+
`Invalid ${label} ${JSON.stringify(specifier)}: must not begin or end with whitespace.`
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
if (HAS_URI_SCHEME.test(specifier.trim())) {
|
|
297
|
+
throw new CodegenConfigError(
|
|
298
|
+
`Invalid ${label} ${JSON.stringify(specifier)}: must be a relative path ("./\u2026", "../\u2026") or a bare package name. URLs are not allowed \u2014 the generated file imports from this specifier.`
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
var REACTOR_CLASS_NAMES = [
|
|
303
|
+
"Reactor",
|
|
304
|
+
"DisplayReactor",
|
|
305
|
+
"CandidReactor",
|
|
306
|
+
"CandidDisplayReactor",
|
|
307
|
+
"MetadataDisplayReactor"
|
|
308
|
+
];
|
|
309
|
+
var CODEGEN_TARGETS = ["react", "core"];
|
|
310
|
+
function assertOneOf(label, value, allowed) {
|
|
311
|
+
if (typeof value !== "string" || !allowed.includes(value)) {
|
|
312
|
+
throw new CodegenConfigError(
|
|
313
|
+
`Invalid ${label} ${JSON.stringify(value)}: must be one of ${allowed.map((a) => JSON.stringify(a)).join(", ")}.`
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
function realpathAllowingMissing(target) {
|
|
318
|
+
let current = import_node_path3.default.resolve(target);
|
|
319
|
+
const missing = [];
|
|
320
|
+
for (; ; ) {
|
|
321
|
+
try {
|
|
322
|
+
return import_node_path3.default.join(import_node_fs2.default.realpathSync(current), ...missing);
|
|
323
|
+
} catch {
|
|
324
|
+
const parent = import_node_path3.default.dirname(current);
|
|
325
|
+
if (parent === current) return import_node_path3.default.resolve(target);
|
|
326
|
+
missing.unshift(import_node_path3.default.basename(current));
|
|
327
|
+
current = parent;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
function assertContainedPath(label, resolved, projectRoot, original = resolved) {
|
|
332
|
+
const relative = import_node_path3.default.relative(
|
|
333
|
+
realpathAllowingMissing(projectRoot),
|
|
334
|
+
realpathAllowingMissing(resolved)
|
|
335
|
+
);
|
|
336
|
+
const [firstSegment] = relative.split(/[\\/]/);
|
|
337
|
+
if (firstSegment === ".." || import_node_path3.default.isAbsolute(relative)) {
|
|
338
|
+
throw new CodegenConfigError(
|
|
339
|
+
`Invalid ${label} ${JSON.stringify(original)}: resolves to ${JSON.stringify(resolved)}, which is outside the project root ${JSON.stringify(import_node_path3.default.resolve(projectRoot))}. Generated output must stay inside the project \u2014 generated directories are deleted and rewritten on every run.`
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
function resolveContainedOutDir(label, outDir, projectRoot) {
|
|
344
|
+
if (typeof outDir !== "string" || outDir.length === 0) {
|
|
345
|
+
throw new CodegenConfigError(
|
|
346
|
+
`Invalid ${label}: expected a non-empty string, received ${outDir === void 0 ? "undefined" : JSON.stringify(outDir)}.`
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
const resolved = import_node_path3.default.isAbsolute(outDir) ? import_node_path3.default.resolve(outDir) : import_node_path3.default.resolve(projectRoot, outDir);
|
|
350
|
+
assertContainedPath(label, resolved, projectRoot, outDir);
|
|
351
|
+
return resolved;
|
|
352
|
+
}
|
|
353
|
+
function assertSafeCanisterConfig(options) {
|
|
354
|
+
const {
|
|
355
|
+
name,
|
|
356
|
+
canisterOutDir,
|
|
357
|
+
globalOutDir,
|
|
358
|
+
clientManagerPath,
|
|
359
|
+
projectRoot,
|
|
360
|
+
mode,
|
|
361
|
+
target
|
|
362
|
+
} = options;
|
|
363
|
+
assertSafeCanisterName(name);
|
|
364
|
+
assertSafeModuleSpecifier("clientManagerPath", clientManagerPath);
|
|
365
|
+
if (mode != null) assertOneOf("mode", mode, REACTOR_CLASS_NAMES);
|
|
366
|
+
if (target != null) assertOneOf("target", target, CODEGEN_TARGETS);
|
|
367
|
+
const outDir = canisterOutDir != null ? resolveContainedOutDir(
|
|
368
|
+
`outDir for canister ${JSON.stringify(name)}`,
|
|
369
|
+
canisterOutDir,
|
|
370
|
+
projectRoot
|
|
371
|
+
) : import_node_path3.default.join(
|
|
372
|
+
resolveContainedOutDir("outDir", globalOutDir, projectRoot),
|
|
373
|
+
name
|
|
374
|
+
);
|
|
375
|
+
assertContainedPath(
|
|
376
|
+
`output directory for canister ${JSON.stringify(name)}`,
|
|
377
|
+
outDir,
|
|
378
|
+
projectRoot
|
|
379
|
+
);
|
|
380
|
+
return { name, outDir, clientManagerPath };
|
|
381
|
+
}
|
|
382
|
+
|
|
211
383
|
// src/pipeline.ts
|
|
212
384
|
function resolveReactorClass(canisterConfig) {
|
|
213
385
|
return canisterConfig.mode ?? "DisplayReactor";
|
|
@@ -233,8 +405,30 @@ async function runCanisterPipeline(options) {
|
|
|
233
405
|
} = options;
|
|
234
406
|
const { name, didFile, clientManagerPath } = canisterConfig;
|
|
235
407
|
const files = [];
|
|
236
|
-
|
|
237
|
-
|
|
408
|
+
let validated;
|
|
409
|
+
try {
|
|
410
|
+
validated = assertSafeCanisterConfig({
|
|
411
|
+
name,
|
|
412
|
+
canisterOutDir: canisterConfig.outDir,
|
|
413
|
+
globalOutDir: globalConfig.outDir,
|
|
414
|
+
clientManagerPath: clientManagerPath ?? globalConfig.clientManagerPath ?? "../../clients",
|
|
415
|
+
projectRoot,
|
|
416
|
+
mode: canisterConfig.mode,
|
|
417
|
+
target: canisterConfig.target ?? globalConfig.target
|
|
418
|
+
});
|
|
419
|
+
} catch (err) {
|
|
420
|
+
if (err instanceof CodegenConfigError) {
|
|
421
|
+
return {
|
|
422
|
+
canisterName: typeof name === "string" ? name : String(name),
|
|
423
|
+
success: false,
|
|
424
|
+
files,
|
|
425
|
+
error: err.message
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
throw err;
|
|
429
|
+
}
|
|
430
|
+
const resolvedDidFile = import_node_path4.default.isAbsolute(didFile) ? didFile : import_node_path4.default.resolve(projectRoot, didFile);
|
|
431
|
+
if (!import_node_fs3.default.existsSync(resolvedDidFile)) {
|
|
238
432
|
return {
|
|
239
433
|
canisterName: name,
|
|
240
434
|
success: false,
|
|
@@ -242,8 +436,8 @@ async function runCanisterPipeline(options) {
|
|
|
242
436
|
error: `DID file not found: ${resolvedDidFile}`
|
|
243
437
|
};
|
|
244
438
|
}
|
|
245
|
-
const canisterOutDir =
|
|
246
|
-
const resolvedClientManagerPath =
|
|
439
|
+
const canisterOutDir = validated.outDir;
|
|
440
|
+
const resolvedClientManagerPath = validated.clientManagerPath;
|
|
247
441
|
try {
|
|
248
442
|
const declResult = await generateDeclarations({
|
|
249
443
|
didFile: resolvedDidFile,
|
|
@@ -274,8 +468,8 @@ async function runCanisterPipeline(options) {
|
|
|
274
468
|
files
|
|
275
469
|
};
|
|
276
470
|
}
|
|
277
|
-
const reactorPath =
|
|
278
|
-
const entryPath =
|
|
471
|
+
const reactorPath = import_node_path4.default.join(canisterOutDir, "index.generated.ts");
|
|
472
|
+
const entryPath = import_node_path4.default.join(canisterOutDir, "index.ts");
|
|
279
473
|
const reactorClass = resolveReactorClass(canisterConfig);
|
|
280
474
|
const runtimeTarget = resolveRuntimeTarget(canisterConfig, globalConfig);
|
|
281
475
|
try {
|
|
@@ -288,16 +482,16 @@ async function runCanisterPipeline(options) {
|
|
|
288
482
|
reactorClass
|
|
289
483
|
});
|
|
290
484
|
const entryContent = generateReactorEntryFile();
|
|
291
|
-
|
|
292
|
-
|
|
485
|
+
import_node_fs3.default.mkdirSync(canisterOutDir, { recursive: true });
|
|
486
|
+
import_node_fs3.default.writeFileSync(reactorPath, reactorContent);
|
|
293
487
|
files.push({ success: true, filePath: reactorPath });
|
|
294
|
-
if (!
|
|
295
|
-
|
|
488
|
+
if (!import_node_fs3.default.existsSync(entryPath)) {
|
|
489
|
+
import_node_fs3.default.writeFileSync(entryPath, entryContent);
|
|
296
490
|
files.push({ success: true, filePath: entryPath });
|
|
297
491
|
} else {
|
|
298
|
-
const existingEntryContent =
|
|
492
|
+
const existingEntryContent = import_node_fs3.default.readFileSync(entryPath, "utf-8");
|
|
299
493
|
if (isLegacyGeneratedIndexFile(existingEntryContent) || isManagedEntryWrapper(existingEntryContent, entryContent)) {
|
|
300
|
-
|
|
494
|
+
import_node_fs3.default.writeFileSync(entryPath, entryContent);
|
|
301
495
|
files.push({ success: true, filePath: entryPath });
|
|
302
496
|
} else {
|
|
303
497
|
files.push({ success: true, filePath: entryPath, skipped: true });
|
|
@@ -325,7 +519,7 @@ async function runCanisterPipeline(options) {
|
|
|
325
519
|
|
|
326
520
|
// src/parser.ts
|
|
327
521
|
var import_parser2 = require("@ic-reactor/parser");
|
|
328
|
-
var
|
|
522
|
+
var import_node_fs4 = __toESM(require("fs"), 1);
|
|
329
523
|
function extractMethods(didContent) {
|
|
330
524
|
try {
|
|
331
525
|
const jsContent = (0, import_parser2.didToJs)(didContent);
|
|
@@ -350,10 +544,10 @@ function extractMethods(didContent) {
|
|
|
350
544
|
}
|
|
351
545
|
}
|
|
352
546
|
function parseDIDFile(didFilePath) {
|
|
353
|
-
if (!
|
|
547
|
+
if (!import_node_fs4.default.existsSync(didFilePath)) {
|
|
354
548
|
throw new Error(`DID file not found: ${didFilePath}`);
|
|
355
549
|
}
|
|
356
|
-
const content =
|
|
550
|
+
const content = import_node_fs4.default.readFileSync(didFilePath, "utf-8");
|
|
357
551
|
return extractMethods(content);
|
|
358
552
|
}
|
|
359
553
|
|
|
@@ -377,620 +571,27 @@ export const clientManager = new ClientManager({
|
|
|
377
571
|
})
|
|
378
572
|
`;
|
|
379
573
|
}
|
|
380
|
-
|
|
381
|
-
// src/metadata-rules.json
|
|
382
|
-
var metadata_rules_default = {
|
|
383
|
-
defaultValidationMessages: {
|
|
384
|
-
minimum: {
|
|
385
|
-
template: "Must be at least {value}"
|
|
386
|
-
},
|
|
387
|
-
maximum: {
|
|
388
|
-
template: "Must be at most {value}"
|
|
389
|
-
},
|
|
390
|
-
minLength: {
|
|
391
|
-
template: "Must be at least {value} character{plural}"
|
|
392
|
-
},
|
|
393
|
-
maxLength: {
|
|
394
|
-
template: "Must be at most {value} character{plural}"
|
|
395
|
-
}
|
|
396
|
-
},
|
|
397
|
-
email: {
|
|
398
|
-
helper: "email",
|
|
399
|
-
regex: "^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$",
|
|
400
|
-
jsonSchemaFormat: "email",
|
|
401
|
-
errorMessage: "Must be a valid email address"
|
|
402
|
-
},
|
|
403
|
-
"date-time": {
|
|
404
|
-
helper: "dateTime",
|
|
405
|
-
regex: "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|[+-](?:[01]\\d|2[0-3]):[0-5]\\d))$",
|
|
406
|
-
jsonSchemaFormat: "date-time",
|
|
407
|
-
errorMessage: "Must be a valid ISO datetime"
|
|
408
|
-
},
|
|
409
|
-
datetime: {
|
|
410
|
-
helper: "datetime",
|
|
411
|
-
regex: "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|[+-](?:[01]\\d|2[0-3]):[0-5]\\d))$",
|
|
412
|
-
jsonSchemaFormat: "date-time",
|
|
413
|
-
errorMessage: "Must be a valid ISO datetime"
|
|
414
|
-
},
|
|
415
|
-
date: {
|
|
416
|
-
helper: "date",
|
|
417
|
-
regex: "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$",
|
|
418
|
-
jsonSchemaFormat: "date",
|
|
419
|
-
errorMessage: "Must be a valid ISO date"
|
|
420
|
-
},
|
|
421
|
-
time: {
|
|
422
|
-
helper: "time",
|
|
423
|
-
regex: "^(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?$",
|
|
424
|
-
errorMessage: "Must be a valid ISO time"
|
|
425
|
-
},
|
|
426
|
-
duration: {
|
|
427
|
-
helper: "duration",
|
|
428
|
-
regex: "^P(?:(\\d+W)|(?!.*W)(?=\\d|T\\d)(\\d+Y)?(\\d+M)?(\\d+D)?(T(?=\\d)(\\d+H)?(\\d+M)?(\\d+([.,]\\d+)?S)?)?)$",
|
|
429
|
-
jsonSchemaFormat: "duration",
|
|
430
|
-
errorMessage: "Must be a valid ISO duration"
|
|
431
|
-
},
|
|
432
|
-
url: {
|
|
433
|
-
helper: "url",
|
|
434
|
-
regex: "^[a-zA-Z][a-zA-Z\\d+.-]*:.+",
|
|
435
|
-
jsonSchemaFormat: "uri",
|
|
436
|
-
errorMessage: "Must be a valid URL"
|
|
437
|
-
},
|
|
438
|
-
uri: {
|
|
439
|
-
helper: "uri",
|
|
440
|
-
regex: "^[a-zA-Z][a-zA-Z\\d+.-]*:.+",
|
|
441
|
-
jsonSchemaFormat: "uri",
|
|
442
|
-
errorMessage: "Must be a valid URI"
|
|
443
|
-
},
|
|
444
|
-
httpsUrl: {
|
|
445
|
-
helper: "httpsUrl",
|
|
446
|
-
regex: "^https://.+",
|
|
447
|
-
jsonSchemaFormat: "uri",
|
|
448
|
-
errorMessage: "Must be a valid HTTPS URL"
|
|
449
|
-
},
|
|
450
|
-
ipv4: {
|
|
451
|
-
helper: "ipv4",
|
|
452
|
-
regex: "^(?:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(?:\\.|$)){4}$",
|
|
453
|
-
jsonSchemaFormat: "ipv4",
|
|
454
|
-
errorMessage: "Must be a valid IPv4 address"
|
|
455
|
-
},
|
|
456
|
-
ipv6: {
|
|
457
|
-
helper: "ipv6",
|
|
458
|
-
regex: "^((?:[0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}|::1|::|(?:[0-9A-Fa-f]{1,4}:){1,7}:|(?:[0-9A-Fa-f]{1,4}:){1,6}:[0-9A-Fa-f]{1,4}|(?:[0-9A-Fa-f]{1,4}:){1,5}(?::[0-9A-Fa-f]{1,4}){1,2}|(?:[0-9A-Fa-f]{1,4}:){1,4}(?::[0-9A-Fa-f]{1,4}){1,3}|(?:[0-9A-Fa-f]{1,4}:){1,3}(?::[0-9A-Fa-f]{1,4}){1,4}|(?:[0-9A-Fa-f]{1,4}:){1,2}(?::[0-9A-Fa-f]{1,4}){1,5}|[0-9A-Fa-f]{1,4}:(?:(?::[0-9A-Fa-f]{1,4}){1,6}))$",
|
|
459
|
-
jsonSchemaFormat: "ipv6",
|
|
460
|
-
errorMessage: "Must be a valid IPv6 address"
|
|
461
|
-
},
|
|
462
|
-
uuid: {
|
|
463
|
-
helper: "uuid",
|
|
464
|
-
regex: "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$",
|
|
465
|
-
jsonSchemaFormat: "uuid",
|
|
466
|
-
errorMessage: "Must be a valid UUID"
|
|
467
|
-
},
|
|
468
|
-
guid: {
|
|
469
|
-
helper: "guid",
|
|
470
|
-
regex: "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$",
|
|
471
|
-
jsonSchemaFormat: "uuid",
|
|
472
|
-
errorMessage: "Must be a valid GUID"
|
|
473
|
-
},
|
|
474
|
-
base64: {
|
|
475
|
-
helper: "base64",
|
|
476
|
-
regex: "^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$",
|
|
477
|
-
contentEncoding: "base64",
|
|
478
|
-
errorMessage: "Must be valid base64"
|
|
479
|
-
},
|
|
480
|
-
base64url: {
|
|
481
|
-
helper: "base64url",
|
|
482
|
-
regex: "^[A-Za-z0-9_-]+$",
|
|
483
|
-
errorMessage: "Must be valid base64url"
|
|
484
|
-
},
|
|
485
|
-
cuid: {
|
|
486
|
-
helper: "cuid",
|
|
487
|
-
regex: "^c[a-z0-9]{24}$",
|
|
488
|
-
errorMessage: "Must be a valid CUID"
|
|
489
|
-
},
|
|
490
|
-
cuid2: {
|
|
491
|
-
helper: "cuid2",
|
|
492
|
-
regex: "^[a-z][a-z0-9]*$",
|
|
493
|
-
errorMessage: "Must be a valid CUID2"
|
|
494
|
-
},
|
|
495
|
-
ulid: {
|
|
496
|
-
helper: "ulid",
|
|
497
|
-
regex: "^[0-9A-HJKMNP-TV-Z]{26}$",
|
|
498
|
-
errorMessage: "Must be a valid ULID"
|
|
499
|
-
},
|
|
500
|
-
nanoid: {
|
|
501
|
-
helper: "nanoid",
|
|
502
|
-
regex: "^[A-Za-z0-9_-]{21}$",
|
|
503
|
-
errorMessage: "Must be a valid nanoid"
|
|
504
|
-
},
|
|
505
|
-
emoji: {
|
|
506
|
-
helper: "emoji",
|
|
507
|
-
regex: "^\\p{Extended_Pictographic}+$",
|
|
508
|
-
errorMessage: "Must contain only emoji characters"
|
|
509
|
-
},
|
|
510
|
-
cidrv4: {
|
|
511
|
-
helper: "cidrv4",
|
|
512
|
-
regex: "^(?:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)\\.){3}(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)/(?:3[0-2]|[12]?\\d)$",
|
|
513
|
-
errorMessage: "Must be a valid IPv4 CIDR range"
|
|
514
|
-
},
|
|
515
|
-
cidrv6: {
|
|
516
|
-
helper: "cidrv6",
|
|
517
|
-
regex: "^[0-9A-Fa-f:]+/(?:12[0-8]|1[01]\\d|\\d?\\d)$",
|
|
518
|
-
errorMessage: "Must be a valid IPv6 CIDR range"
|
|
519
|
-
},
|
|
520
|
-
mac: {
|
|
521
|
-
helper: "mac",
|
|
522
|
-
regex: "^(?:[0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$",
|
|
523
|
-
errorMessage: "Must be a valid MAC address"
|
|
524
|
-
}
|
|
525
|
-
};
|
|
526
|
-
|
|
527
|
-
// src/metadata.ts
|
|
528
|
-
var metadataRulesFile = metadata_rules_default;
|
|
529
|
-
var defaultValidationMessages = metadataRulesFile.defaultValidationMessages ?? {};
|
|
530
|
-
var rawRuleEntries = Object.entries(metadata_rules_default).filter(
|
|
531
|
-
([type]) => type !== "defaultValidationMessages"
|
|
532
|
-
);
|
|
533
|
-
var BUILT_IN_JSDOC_FORMAT_TYPES = Object.fromEntries(
|
|
534
|
-
rawRuleEntries.map(([type, rule]) => [
|
|
535
|
-
type,
|
|
536
|
-
{
|
|
537
|
-
regex: rule.regex,
|
|
538
|
-
jsonSchemaFormat: rule.jsonSchemaFormat,
|
|
539
|
-
contentEncoding: rule.contentEncoding,
|
|
540
|
-
errorMessage: rule.errorMessage
|
|
541
|
-
}
|
|
542
|
-
])
|
|
543
|
-
);
|
|
544
|
-
var BUILT_IN_FORMAT_HELPERS = Object.fromEntries(
|
|
545
|
-
rawRuleEntries.filter(([, rule]) => rule.helper).map(([type, rule]) => [type, rule.helper])
|
|
546
|
-
);
|
|
547
|
-
function normalizeFormatDefinition(definition) {
|
|
548
|
-
if (!definition) return void 0;
|
|
549
|
-
return typeof definition === "string" ? { regex: definition } : definition;
|
|
550
|
-
}
|
|
551
|
-
function formatDefinitionFor(type, options) {
|
|
552
|
-
const builtIn = BUILT_IN_JSDOC_FORMAT_TYPES[type];
|
|
553
|
-
const custom = normalizeFormatDefinition(
|
|
554
|
-
options.customJSDocFormatTypes?.[type]
|
|
555
|
-
);
|
|
556
|
-
if (!custom) return builtIn;
|
|
557
|
-
return {
|
|
558
|
-
...builtIn,
|
|
559
|
-
...custom,
|
|
560
|
-
errorMessage: custom.errorMessage ?? builtIn?.errorMessage
|
|
561
|
-
};
|
|
562
|
-
}
|
|
563
|
-
function stripUndefined(value) {
|
|
564
|
-
return Object.fromEntries(
|
|
565
|
-
Object.entries(value).filter(([, entry]) => entry !== void 0)
|
|
566
|
-
);
|
|
567
|
-
}
|
|
568
|
-
function defaultBoundMessage(kind, value) {
|
|
569
|
-
const template = defaultValidationMessages[kind]?.template ?? (kind === "minimum" ? "Must be at least {value}" : kind === "maximum" ? "Must be at most {value}" : kind === "minLength" ? "Must be at least {value} character{plural}" : "Must be at most {value} character{plural}");
|
|
570
|
-
return template.replace("{value}", value).replace("{plural}", value === "1" ? "" : "s");
|
|
571
|
-
}
|
|
572
|
-
function withDefaultBoundMessage(bound, kind) {
|
|
573
|
-
if (!bound || bound.message !== void 0) return bound;
|
|
574
|
-
return {
|
|
575
|
-
...bound,
|
|
576
|
-
message: defaultBoundMessage(kind, bound.value)
|
|
577
|
-
};
|
|
578
|
-
}
|
|
579
|
-
function normalizeValidationMetadata(validation, options) {
|
|
580
|
-
const normalized = {
|
|
581
|
-
...validation,
|
|
582
|
-
minimum: withDefaultBoundMessage(validation.minimum, "minimum"),
|
|
583
|
-
maximum: withDefaultBoundMessage(validation.maximum, "maximum"),
|
|
584
|
-
minLength: withDefaultBoundMessage(validation.minLength, "minLength"),
|
|
585
|
-
maxLength: withDefaultBoundMessage(validation.maxLength, "maxLength")
|
|
586
|
-
};
|
|
587
|
-
const format = validation.format;
|
|
588
|
-
const formatDefinition = format ? formatDefinitionFor(format.type, options) : void 0;
|
|
589
|
-
if (format && formatDefinition) {
|
|
590
|
-
normalized.format = stripUndefined({
|
|
591
|
-
...format,
|
|
592
|
-
regex: formatDefinition.regex,
|
|
593
|
-
jsonSchemaFormat: formatDefinition.jsonSchemaFormat,
|
|
594
|
-
contentEncoding: formatDefinition.contentEncoding,
|
|
595
|
-
errorMessage: format.message ?? formatDefinition.errorMessage
|
|
596
|
-
});
|
|
597
|
-
} else if (format) {
|
|
598
|
-
normalized.format = format;
|
|
599
|
-
}
|
|
600
|
-
return normalized;
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
// src/renderer.ts
|
|
604
|
-
var reservedWords = /* @__PURE__ */ new Set([
|
|
605
|
-
"break",
|
|
606
|
-
"case",
|
|
607
|
-
"catch",
|
|
608
|
-
"class",
|
|
609
|
-
"const",
|
|
610
|
-
"continue",
|
|
611
|
-
"debugger",
|
|
612
|
-
"default",
|
|
613
|
-
"delete",
|
|
614
|
-
"do",
|
|
615
|
-
"else",
|
|
616
|
-
"export",
|
|
617
|
-
"extends",
|
|
618
|
-
"false",
|
|
619
|
-
"finally",
|
|
620
|
-
"for",
|
|
621
|
-
"function",
|
|
622
|
-
"if",
|
|
623
|
-
"import",
|
|
624
|
-
"in",
|
|
625
|
-
"instanceof",
|
|
626
|
-
"new",
|
|
627
|
-
"null",
|
|
628
|
-
"return",
|
|
629
|
-
"super",
|
|
630
|
-
"switch",
|
|
631
|
-
"this",
|
|
632
|
-
"throw",
|
|
633
|
-
"true",
|
|
634
|
-
"try",
|
|
635
|
-
"typeof",
|
|
636
|
-
"var",
|
|
637
|
-
"void",
|
|
638
|
-
"while",
|
|
639
|
-
"with",
|
|
640
|
-
"yield",
|
|
641
|
-
"let",
|
|
642
|
-
"package",
|
|
643
|
-
"private",
|
|
644
|
-
"protected",
|
|
645
|
-
"public",
|
|
646
|
-
"static"
|
|
647
|
-
]);
|
|
648
|
-
function isValidIdentifier(name) {
|
|
649
|
-
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name) && !reservedWords.has(name);
|
|
650
|
-
}
|
|
651
|
-
function assertIdentifier(name, label) {
|
|
652
|
-
if (!isValidIdentifier(name)) {
|
|
653
|
-
throw new Error(`${label} must be a valid TypeScript identifier: ${name}`);
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
function propertyName(name) {
|
|
657
|
-
return isValidIdentifier(name) ? name : JSON.stringify(name);
|
|
658
|
-
}
|
|
659
|
-
function normalizeOptions(options = {}) {
|
|
660
|
-
return typeof options === "string" ? { serviceExportName: options } : options;
|
|
661
|
-
}
|
|
662
|
-
function toServiceExportName(canisterName) {
|
|
663
|
-
return canisterName.replace(/[-\s]+/g, "_").toUpperCase();
|
|
664
|
-
}
|
|
665
|
-
function resolveServiceExportName(options, declaredTypeNames) {
|
|
666
|
-
if (options.serviceExportName) return options.serviceExportName;
|
|
667
|
-
if (options.canisterName) {
|
|
668
|
-
let name = toServiceExportName(options.canisterName);
|
|
669
|
-
if (declaredTypeNames.has(name)) {
|
|
670
|
-
name = `${name}_SERVICE`;
|
|
671
|
-
}
|
|
672
|
-
return name;
|
|
673
|
-
}
|
|
674
|
-
return "_SERVICE";
|
|
675
|
-
}
|
|
676
|
-
function hasMetadata(metadata) {
|
|
677
|
-
return metadata != null && Object.keys(metadata).length > 0;
|
|
678
|
-
}
|
|
679
|
-
function stripUndefined2(value) {
|
|
680
|
-
return Object.fromEntries(
|
|
681
|
-
Object.entries(value).filter(([, entry]) => entry !== void 0)
|
|
682
|
-
);
|
|
683
|
-
}
|
|
684
|
-
function isEmptyObject(value) {
|
|
685
|
-
return value != null && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === 0;
|
|
686
|
-
}
|
|
687
|
-
function textFormatHelperFor(expression, metadata, options) {
|
|
688
|
-
const format = metadata.validation?.format;
|
|
689
|
-
if (!format || expression !== "c.text()") return void 0;
|
|
690
|
-
if (options.customJSDocFormatTypes?.[format.type]) {
|
|
691
|
-
return void 0;
|
|
692
|
-
}
|
|
693
|
-
return BUILT_IN_FORMAT_HELPERS[format.type];
|
|
694
|
-
}
|
|
695
|
-
function hasOnlyDescriptionDocs(docs, description) {
|
|
696
|
-
return docs != null && description != null && docs.length === 1 && docs[0] === description;
|
|
697
|
-
}
|
|
698
|
-
function metadataForRender(metadata, options) {
|
|
699
|
-
if (!metadata.validation) {
|
|
700
|
-
return metadata;
|
|
701
|
-
}
|
|
702
|
-
return {
|
|
703
|
-
...metadata,
|
|
704
|
-
validation: normalizeValidationMetadata(metadata.validation, {
|
|
705
|
-
customJSDocFormatTypes: options.customJSDocFormatTypes
|
|
706
|
-
})
|
|
707
|
-
};
|
|
708
|
-
}
|
|
709
|
-
function applyMetadata(expression, metadata, options) {
|
|
710
|
-
if (!hasMetadata(metadata)) return expression;
|
|
711
|
-
const textFormatHelper = textFormatHelperFor(expression, metadata, options);
|
|
712
|
-
const textFormatMessage = metadata.validation?.format?.message;
|
|
713
|
-
const renderedMetadata = metadataForRender(metadata, options);
|
|
714
|
-
const { description, ...metadataRest } = renderedMetadata;
|
|
715
|
-
const rest = { ...metadataRest };
|
|
716
|
-
let result = textFormatHelper ? `c.${textFormatHelper}(${textFormatMessage ? JSON.stringify(textFormatMessage) : ""})` : expression;
|
|
717
|
-
if (textFormatHelper) {
|
|
718
|
-
delete rest.docs;
|
|
719
|
-
if (rest.validation) {
|
|
720
|
-
const { format: _format, ...validationRest } = rest.validation;
|
|
721
|
-
rest.validation = stripUndefined2(validationRest);
|
|
722
|
-
if (isEmptyObject(rest.validation)) {
|
|
723
|
-
delete rest.validation;
|
|
724
|
-
}
|
|
725
|
-
}
|
|
726
|
-
} else if (hasOnlyDescriptionDocs(rest.docs, description)) {
|
|
727
|
-
delete rest.docs;
|
|
728
|
-
}
|
|
729
|
-
if (description) {
|
|
730
|
-
result += `.describe(${JSON.stringify(description)})`;
|
|
731
|
-
}
|
|
732
|
-
if (Object.keys(rest).length > 0) {
|
|
733
|
-
result += `.meta(${JSON.stringify(rest)})`;
|
|
734
|
-
}
|
|
735
|
-
return result;
|
|
736
|
-
}
|
|
737
|
-
function getReferencedNames(type) {
|
|
738
|
-
const refs = [];
|
|
739
|
-
function visit(node) {
|
|
740
|
-
switch (node.kind) {
|
|
741
|
-
case "reference":
|
|
742
|
-
refs.push(node.name);
|
|
743
|
-
break;
|
|
744
|
-
case "opt":
|
|
745
|
-
case "vec":
|
|
746
|
-
visit(node.type);
|
|
747
|
-
break;
|
|
748
|
-
case "record":
|
|
749
|
-
case "variant":
|
|
750
|
-
for (const field of node.fields) {
|
|
751
|
-
visit(field.type);
|
|
752
|
-
}
|
|
753
|
-
break;
|
|
754
|
-
case "tuple":
|
|
755
|
-
for (const item of node.types) {
|
|
756
|
-
visit(item);
|
|
757
|
-
}
|
|
758
|
-
break;
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
visit(type);
|
|
762
|
-
return refs;
|
|
763
|
-
}
|
|
764
|
-
function sortDeclarations(declarations) {
|
|
765
|
-
const sorted = [];
|
|
766
|
-
const visited = /* @__PURE__ */ new Set();
|
|
767
|
-
const visiting = [];
|
|
768
|
-
const declarationByName = new Map(
|
|
769
|
-
declarations.map((decl) => [decl.name, decl])
|
|
770
|
-
);
|
|
771
|
-
function visit(name) {
|
|
772
|
-
if (visited.has(name)) return;
|
|
773
|
-
const cycleStart = visiting.indexOf(name);
|
|
774
|
-
if (cycleStart !== -1) {
|
|
775
|
-
const cycle = [...visiting.slice(cycleStart), name].join(" -> ");
|
|
776
|
-
throw new Error(`Recursive Candid types are not supported yet: ${cycle}`);
|
|
777
|
-
}
|
|
778
|
-
const declaration = declarationByName.get(name);
|
|
779
|
-
if (!declaration) return;
|
|
780
|
-
visiting.push(name);
|
|
781
|
-
for (const dependency of getReferencedNames(declaration.type)) {
|
|
782
|
-
visit(dependency);
|
|
783
|
-
}
|
|
784
|
-
visiting.pop();
|
|
785
|
-
visited.add(name);
|
|
786
|
-
sorted.push(declaration);
|
|
787
|
-
}
|
|
788
|
-
for (const declaration of declarations) {
|
|
789
|
-
visit(declaration.name);
|
|
790
|
-
}
|
|
791
|
-
return sorted;
|
|
792
|
-
}
|
|
793
|
-
function renderType(type, indent = "", options = {}) {
|
|
794
|
-
const nextIndent = `${indent} `;
|
|
795
|
-
let expression;
|
|
796
|
-
switch (type.kind) {
|
|
797
|
-
case "null":
|
|
798
|
-
expression = "c.null()";
|
|
799
|
-
break;
|
|
800
|
-
case "bool":
|
|
801
|
-
expression = "c.bool()";
|
|
802
|
-
break;
|
|
803
|
-
case "nat":
|
|
804
|
-
expression = "c.nat()";
|
|
805
|
-
break;
|
|
806
|
-
case "int":
|
|
807
|
-
expression = "c.int()";
|
|
808
|
-
break;
|
|
809
|
-
case "nat8":
|
|
810
|
-
expression = "c.nat8()";
|
|
811
|
-
break;
|
|
812
|
-
case "nat16":
|
|
813
|
-
expression = "c.nat16()";
|
|
814
|
-
break;
|
|
815
|
-
case "nat32":
|
|
816
|
-
expression = "c.nat32()";
|
|
817
|
-
break;
|
|
818
|
-
case "nat64":
|
|
819
|
-
expression = "c.nat64()";
|
|
820
|
-
break;
|
|
821
|
-
case "int8":
|
|
822
|
-
expression = "c.int8()";
|
|
823
|
-
break;
|
|
824
|
-
case "int16":
|
|
825
|
-
expression = "c.int16()";
|
|
826
|
-
break;
|
|
827
|
-
case "int32":
|
|
828
|
-
expression = "c.int32()";
|
|
829
|
-
break;
|
|
830
|
-
case "int64":
|
|
831
|
-
expression = "c.int64()";
|
|
832
|
-
break;
|
|
833
|
-
case "float32":
|
|
834
|
-
expression = "c.float32()";
|
|
835
|
-
break;
|
|
836
|
-
case "float64":
|
|
837
|
-
expression = "c.float64()";
|
|
838
|
-
break;
|
|
839
|
-
case "text":
|
|
840
|
-
expression = "c.text()";
|
|
841
|
-
break;
|
|
842
|
-
case "reserved":
|
|
843
|
-
expression = "c.reserved()";
|
|
844
|
-
break;
|
|
845
|
-
case "empty":
|
|
846
|
-
expression = "c.empty()";
|
|
847
|
-
break;
|
|
848
|
-
case "principal":
|
|
849
|
-
expression = "c.principal()";
|
|
850
|
-
break;
|
|
851
|
-
case "blob":
|
|
852
|
-
expression = "c.blob()";
|
|
853
|
-
break;
|
|
854
|
-
case "reference":
|
|
855
|
-
assertIdentifier(type.name, "Type reference");
|
|
856
|
-
expression = type.name;
|
|
857
|
-
break;
|
|
858
|
-
case "opt":
|
|
859
|
-
expression = `c.opt(${renderType(type.type, indent, options)})`;
|
|
860
|
-
break;
|
|
861
|
-
case "vec":
|
|
862
|
-
expression = `c.vec(${renderType(type.type, indent, options)})`;
|
|
863
|
-
break;
|
|
864
|
-
case "record": {
|
|
865
|
-
if (type.fields.length === 0) {
|
|
866
|
-
expression = "c.record({})";
|
|
867
|
-
} else {
|
|
868
|
-
const fields = type.fields.map((field) => {
|
|
869
|
-
const fieldExpression = applyMetadata(
|
|
870
|
-
renderType(field.type, nextIndent, options),
|
|
871
|
-
field.metadata,
|
|
872
|
-
options
|
|
873
|
-
);
|
|
874
|
-
return `${nextIndent}${propertyName(field.name)}: ${fieldExpression},`;
|
|
875
|
-
}).join("\n");
|
|
876
|
-
expression = `c.record({
|
|
877
|
-
${fields}
|
|
878
|
-
${indent}})`;
|
|
879
|
-
}
|
|
880
|
-
break;
|
|
881
|
-
}
|
|
882
|
-
case "variant": {
|
|
883
|
-
if (type.fields.length === 0) {
|
|
884
|
-
expression = "c.variant({})";
|
|
885
|
-
} else {
|
|
886
|
-
const fields = type.fields.map((field) => {
|
|
887
|
-
const fieldExpression = applyMetadata(
|
|
888
|
-
renderType(field.type, nextIndent, options),
|
|
889
|
-
field.metadata,
|
|
890
|
-
options
|
|
891
|
-
);
|
|
892
|
-
return `${nextIndent}${propertyName(field.name)}: ${fieldExpression},`;
|
|
893
|
-
}).join("\n");
|
|
894
|
-
expression = `c.variant({
|
|
895
|
-
${fields}
|
|
896
|
-
${indent}})`;
|
|
897
|
-
}
|
|
898
|
-
break;
|
|
899
|
-
}
|
|
900
|
-
case "tuple":
|
|
901
|
-
expression = `c.tuple([${type.types.map((item) => renderType(item, indent, options)).join(", ")}])`;
|
|
902
|
-
break;
|
|
903
|
-
case "func":
|
|
904
|
-
case "service":
|
|
905
|
-
case "class":
|
|
906
|
-
case "unknown":
|
|
907
|
-
case "knot":
|
|
908
|
-
case "future":
|
|
909
|
-
expression = `/* c.${type.kind} is not supported */ c.reserved()`;
|
|
910
|
-
break;
|
|
911
|
-
}
|
|
912
|
-
return applyMetadata(expression, type.metadata, options);
|
|
913
|
-
}
|
|
914
|
-
function renderMethodReturn(method, options) {
|
|
915
|
-
if (method.mode === "oneway") return "";
|
|
916
|
-
if (method.returns.length === 0) return "";
|
|
917
|
-
if (method.returns.length === 1) {
|
|
918
|
-
return `, ${renderType(method.returns[0], " ", options)}`;
|
|
919
|
-
}
|
|
920
|
-
return `, [${method.returns.map((returnType) => renderType(returnType, " ", options)).join(", ")}]`;
|
|
921
|
-
}
|
|
922
|
-
function generateCodecDeclarations(schema, optionsOrServiceExportName = {}) {
|
|
923
|
-
const options = normalizeOptions(optionsOrServiceExportName);
|
|
924
|
-
const lines = ['import { c } from "@ic-reactor/cod"', ""];
|
|
925
|
-
for (const declaration of sortDeclarations(schema.types)) {
|
|
926
|
-
assertIdentifier(declaration.name, "Type declaration name");
|
|
927
|
-
lines.push(
|
|
928
|
-
`export const ${declaration.name} = ${applyMetadata(
|
|
929
|
-
renderType(declaration.type, "", options),
|
|
930
|
-
declaration.metadata,
|
|
931
|
-
options
|
|
932
|
-
)}`
|
|
933
|
-
);
|
|
934
|
-
lines.push(
|
|
935
|
-
`export type ${declaration.name} = c.infer<typeof ${declaration.name}>`
|
|
936
|
-
);
|
|
937
|
-
lines.push("");
|
|
938
|
-
}
|
|
939
|
-
if (schema.service) {
|
|
940
|
-
const includeCompatibilityExports = options.includeCompatibilityExports ?? false;
|
|
941
|
-
const declaredTypeNames = new Set(schema.types.map((t) => t.name));
|
|
942
|
-
const serviceExportName = resolveServiceExportName(
|
|
943
|
-
options,
|
|
944
|
-
declaredTypeNames
|
|
945
|
-
);
|
|
946
|
-
assertIdentifier(serviceExportName, "Service export name");
|
|
947
|
-
const methods = schema.service.methods.map((method) => {
|
|
948
|
-
const args = method.args.map((arg) => renderType(arg, " ", options)).join(", ");
|
|
949
|
-
const methodExpression = `c.${method.mode}([${args}]${renderMethodReturn(
|
|
950
|
-
method,
|
|
951
|
-
options
|
|
952
|
-
)})`;
|
|
953
|
-
return ` ${propertyName(method.name)}: ${applyMetadata(
|
|
954
|
-
methodExpression,
|
|
955
|
-
method.metadata,
|
|
956
|
-
options
|
|
957
|
-
)},`;
|
|
958
|
-
}).join("\n");
|
|
959
|
-
const serviceExpression = methods.length > 0 ? `c.service({
|
|
960
|
-
${methods}
|
|
961
|
-
})` : "c.service({})";
|
|
962
|
-
lines.push(
|
|
963
|
-
`export const ${serviceExportName} = ${applyMetadata(
|
|
964
|
-
serviceExpression,
|
|
965
|
-
schema.service.metadata,
|
|
966
|
-
options
|
|
967
|
-
)}`
|
|
968
|
-
);
|
|
969
|
-
lines.push("");
|
|
970
|
-
if (includeCompatibilityExports) {
|
|
971
|
-
lines.push(`export const idlFactory = ${serviceExportName}.idlFactory`);
|
|
972
|
-
lines.push(
|
|
973
|
-
`export type _SERVICE = c.ServiceOf<typeof ${serviceExportName}>`
|
|
974
|
-
);
|
|
975
|
-
lines.push("");
|
|
976
|
-
lines.push(`export const manifest = ${serviceExportName}.manifest()`);
|
|
977
|
-
}
|
|
978
|
-
}
|
|
979
|
-
return `${lines.join("\n").trimEnd()}
|
|
980
|
-
`;
|
|
981
|
-
}
|
|
982
574
|
// Annotate the CommonJS export names for ESM import in node:
|
|
983
575
|
0 && (module.exports = {
|
|
576
|
+
CANISTER_NAME_PATTERN,
|
|
577
|
+
CODEGEN_TARGETS,
|
|
578
|
+
CodegenConfigError,
|
|
579
|
+
REACTOR_CLASS_NAMES,
|
|
580
|
+
assertContainedPath,
|
|
581
|
+
assertOneOf,
|
|
582
|
+
assertSafeCanisterConfig,
|
|
583
|
+
assertSafeCanisterName,
|
|
584
|
+
assertSafeModuleSpecifier,
|
|
984
585
|
declarationsExist,
|
|
985
586
|
extractMethods,
|
|
986
587
|
generateClientFile,
|
|
987
|
-
generateCodecDeclarations,
|
|
988
588
|
generateDeclarations,
|
|
989
589
|
generateReactorEntryFile,
|
|
990
590
|
generateReactorFile,
|
|
991
591
|
getReactorName,
|
|
992
592
|
getServiceTypeName,
|
|
993
593
|
parseDIDFile,
|
|
594
|
+
resolveContainedOutDir,
|
|
994
595
|
runCanisterPipeline,
|
|
995
596
|
toPascalCase
|
|
996
597
|
});
|