@ic-reactor/codegen 0.11.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -4
- package/dist/chunk-VBCR5IVT.js +609 -0
- package/dist/index.cjs +616 -1
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +17 -1
- package/dist/renderer.cjs +639 -0
- package/dist/renderer.d.cts +33 -0
- package/dist/renderer.d.ts +33 -0
- package/dist/renderer.js +14 -0
- package/llms.txt +28 -0
- package/package.json +32 -13
- package/src/__snapshots__/reactor.test.ts.snap +12 -0
- package/src/bindgen.test.ts +1 -1
- package/src/generators/client.ts +0 -1
- package/src/generators/reactor.ts +12 -0
- package/src/index.ts +3 -0
- package/src/metadata-rules.json +144 -0
- package/src/metadata.ts +157 -0
- package/src/naming.test.ts +1 -1
- package/src/pipeline.test.ts +1 -1
- package/src/reactor.test.ts +4 -1
- package/src/renderer.test.ts +573 -0
- package/src/renderer.ts +515 -0
package/dist/index.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
generateCodecDeclarations
|
|
3
|
+
} from "./chunk-VBCR5IVT.js";
|
|
4
|
+
|
|
1
5
|
// src/pipeline.ts
|
|
2
6
|
import fs2 from "fs";
|
|
3
7
|
import path3 from "path";
|
|
@@ -131,6 +135,9 @@ export type ${serviceName} = _SERVICE
|
|
|
131
135
|
*
|
|
132
136
|
* Auto-generated by @ic-reactor/codegen \u2014 do not edit.
|
|
133
137
|
* This file is overwritten whenever generation runs.
|
|
138
|
+
*
|
|
139
|
+
* Keep app-specific logic in the stable \`index.ts\` wrapper (or adjacent
|
|
140
|
+
* factory modules). Avoid editing this managed file directly.
|
|
134
141
|
*/
|
|
135
142
|
export const ${reactorName} = new ${reactorClass}<${serviceName}>({
|
|
136
143
|
clientManager,
|
|
@@ -144,6 +151,15 @@ function generateReactorEntryFile() {
|
|
|
144
151
|
*
|
|
145
152
|
* Created once by @ic-reactor/codegen and safe to customize.
|
|
146
153
|
* Keep the re-export below if you want generated exports and types to stay in sync.
|
|
154
|
+
*
|
|
155
|
+
* Recommended customization points:
|
|
156
|
+
* - define reusable query/mutation factories
|
|
157
|
+
* - add app-specific hooks and cache invalidation wiring
|
|
158
|
+
* - compose generated APIs into route loaders/actions
|
|
159
|
+
*
|
|
160
|
+
* Do not edit \`index.generated.ts\`; it is regenerated on each codegen run.
|
|
161
|
+
* AI guide: https://ic-reactor.b3pay.net/llms-full.txt
|
|
162
|
+
* Skill install: npx skills add B3Pay/ic-reactor-skills --full-depth --skill ic-reactor-hooks
|
|
147
163
|
*/
|
|
148
164
|
export * from "./index.generated"
|
|
149
165
|
`;
|
|
@@ -315,7 +331,6 @@ ${queryClientImport}
|
|
|
315
331
|
*/
|
|
316
332
|
export const clientManager = new ClientManager({
|
|
317
333
|
queryClient,
|
|
318
|
-
withCanisterEnv: true,
|
|
319
334
|
})
|
|
320
335
|
`;
|
|
321
336
|
}
|
|
@@ -323,6 +338,7 @@ export {
|
|
|
323
338
|
declarationsExist,
|
|
324
339
|
extractMethods,
|
|
325
340
|
generateClientFile,
|
|
341
|
+
generateCodecDeclarations,
|
|
326
342
|
generateDeclarations,
|
|
327
343
|
generateReactorEntryFile,
|
|
328
344
|
generateReactorFile,
|
|
@@ -0,0 +1,639 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/renderer.ts
|
|
21
|
+
var renderer_exports = {};
|
|
22
|
+
__export(renderer_exports, {
|
|
23
|
+
BUILT_IN_FORMAT_HELPERS: () => BUILT_IN_FORMAT_HELPERS,
|
|
24
|
+
BUILT_IN_JSDOC_FORMAT_TYPES: () => BUILT_IN_JSDOC_FORMAT_TYPES,
|
|
25
|
+
generateCodecDeclarations: () => generateCodecDeclarations,
|
|
26
|
+
renderType: () => renderType,
|
|
27
|
+
sortDeclarations: () => sortDeclarations
|
|
28
|
+
});
|
|
29
|
+
module.exports = __toCommonJS(renderer_exports);
|
|
30
|
+
|
|
31
|
+
// src/metadata-rules.json
|
|
32
|
+
var metadata_rules_default = {
|
|
33
|
+
defaultValidationMessages: {
|
|
34
|
+
minimum: {
|
|
35
|
+
template: "Must be at least {value}"
|
|
36
|
+
},
|
|
37
|
+
maximum: {
|
|
38
|
+
template: "Must be at most {value}"
|
|
39
|
+
},
|
|
40
|
+
minLength: {
|
|
41
|
+
template: "Must be at least {value} character{plural}"
|
|
42
|
+
},
|
|
43
|
+
maxLength: {
|
|
44
|
+
template: "Must be at most {value} character{plural}"
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
email: {
|
|
48
|
+
helper: "email",
|
|
49
|
+
regex: "^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$",
|
|
50
|
+
jsonSchemaFormat: "email",
|
|
51
|
+
errorMessage: "Must be a valid email address"
|
|
52
|
+
},
|
|
53
|
+
"date-time": {
|
|
54
|
+
helper: "dateTime",
|
|
55
|
+
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))$",
|
|
56
|
+
jsonSchemaFormat: "date-time",
|
|
57
|
+
errorMessage: "Must be a valid ISO datetime"
|
|
58
|
+
},
|
|
59
|
+
datetime: {
|
|
60
|
+
helper: "datetime",
|
|
61
|
+
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))$",
|
|
62
|
+
jsonSchemaFormat: "date-time",
|
|
63
|
+
errorMessage: "Must be a valid ISO datetime"
|
|
64
|
+
},
|
|
65
|
+
date: {
|
|
66
|
+
helper: "date",
|
|
67
|
+
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])))$",
|
|
68
|
+
jsonSchemaFormat: "date",
|
|
69
|
+
errorMessage: "Must be a valid ISO date"
|
|
70
|
+
},
|
|
71
|
+
time: {
|
|
72
|
+
helper: "time",
|
|
73
|
+
regex: "^(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?$",
|
|
74
|
+
errorMessage: "Must be a valid ISO time"
|
|
75
|
+
},
|
|
76
|
+
duration: {
|
|
77
|
+
helper: "duration",
|
|
78
|
+
regex: "^P(?:(\\d+W)|(?!.*W)(?=\\d|T\\d)(\\d+Y)?(\\d+M)?(\\d+D)?(T(?=\\d)(\\d+H)?(\\d+M)?(\\d+([.,]\\d+)?S)?)?)$",
|
|
79
|
+
jsonSchemaFormat: "duration",
|
|
80
|
+
errorMessage: "Must be a valid ISO duration"
|
|
81
|
+
},
|
|
82
|
+
url: {
|
|
83
|
+
helper: "url",
|
|
84
|
+
regex: "^[a-zA-Z][a-zA-Z\\d+.-]*:.+",
|
|
85
|
+
jsonSchemaFormat: "uri",
|
|
86
|
+
errorMessage: "Must be a valid URL"
|
|
87
|
+
},
|
|
88
|
+
uri: {
|
|
89
|
+
helper: "uri",
|
|
90
|
+
regex: "^[a-zA-Z][a-zA-Z\\d+.-]*:.+",
|
|
91
|
+
jsonSchemaFormat: "uri",
|
|
92
|
+
errorMessage: "Must be a valid URI"
|
|
93
|
+
},
|
|
94
|
+
httpsUrl: {
|
|
95
|
+
helper: "httpsUrl",
|
|
96
|
+
regex: "^https://.+",
|
|
97
|
+
jsonSchemaFormat: "uri",
|
|
98
|
+
errorMessage: "Must be a valid HTTPS URL"
|
|
99
|
+
},
|
|
100
|
+
ipv4: {
|
|
101
|
+
helper: "ipv4",
|
|
102
|
+
regex: "^(?:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(?:\\.|$)){4}$",
|
|
103
|
+
jsonSchemaFormat: "ipv4",
|
|
104
|
+
errorMessage: "Must be a valid IPv4 address"
|
|
105
|
+
},
|
|
106
|
+
ipv6: {
|
|
107
|
+
helper: "ipv6",
|
|
108
|
+
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}))$",
|
|
109
|
+
jsonSchemaFormat: "ipv6",
|
|
110
|
+
errorMessage: "Must be a valid IPv6 address"
|
|
111
|
+
},
|
|
112
|
+
uuid: {
|
|
113
|
+
helper: "uuid",
|
|
114
|
+
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}$",
|
|
115
|
+
jsonSchemaFormat: "uuid",
|
|
116
|
+
errorMessage: "Must be a valid UUID"
|
|
117
|
+
},
|
|
118
|
+
guid: {
|
|
119
|
+
helper: "guid",
|
|
120
|
+
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}$",
|
|
121
|
+
jsonSchemaFormat: "uuid",
|
|
122
|
+
errorMessage: "Must be a valid GUID"
|
|
123
|
+
},
|
|
124
|
+
base64: {
|
|
125
|
+
helper: "base64",
|
|
126
|
+
regex: "^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$",
|
|
127
|
+
contentEncoding: "base64",
|
|
128
|
+
errorMessage: "Must be valid base64"
|
|
129
|
+
},
|
|
130
|
+
base64url: {
|
|
131
|
+
helper: "base64url",
|
|
132
|
+
regex: "^[A-Za-z0-9_-]+$",
|
|
133
|
+
errorMessage: "Must be valid base64url"
|
|
134
|
+
},
|
|
135
|
+
cuid: {
|
|
136
|
+
helper: "cuid",
|
|
137
|
+
regex: "^c[a-z0-9]{24}$",
|
|
138
|
+
errorMessage: "Must be a valid CUID"
|
|
139
|
+
},
|
|
140
|
+
cuid2: {
|
|
141
|
+
helper: "cuid2",
|
|
142
|
+
regex: "^[a-z][a-z0-9]*$",
|
|
143
|
+
errorMessage: "Must be a valid CUID2"
|
|
144
|
+
},
|
|
145
|
+
ulid: {
|
|
146
|
+
helper: "ulid",
|
|
147
|
+
regex: "^[0-9A-HJKMNP-TV-Z]{26}$",
|
|
148
|
+
errorMessage: "Must be a valid ULID"
|
|
149
|
+
},
|
|
150
|
+
nanoid: {
|
|
151
|
+
helper: "nanoid",
|
|
152
|
+
regex: "^[A-Za-z0-9_-]{21}$",
|
|
153
|
+
errorMessage: "Must be a valid nanoid"
|
|
154
|
+
},
|
|
155
|
+
emoji: {
|
|
156
|
+
helper: "emoji",
|
|
157
|
+
regex: "^\\p{Extended_Pictographic}+$",
|
|
158
|
+
errorMessage: "Must contain only emoji characters"
|
|
159
|
+
},
|
|
160
|
+
cidrv4: {
|
|
161
|
+
helper: "cidrv4",
|
|
162
|
+
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)$",
|
|
163
|
+
errorMessage: "Must be a valid IPv4 CIDR range"
|
|
164
|
+
},
|
|
165
|
+
cidrv6: {
|
|
166
|
+
helper: "cidrv6",
|
|
167
|
+
regex: "^[0-9A-Fa-f:]+/(?:12[0-8]|1[01]\\d|\\d?\\d)$",
|
|
168
|
+
errorMessage: "Must be a valid IPv6 CIDR range"
|
|
169
|
+
},
|
|
170
|
+
mac: {
|
|
171
|
+
helper: "mac",
|
|
172
|
+
regex: "^(?:[0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$",
|
|
173
|
+
errorMessage: "Must be a valid MAC address"
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
// src/metadata.ts
|
|
178
|
+
var metadataRulesFile = metadata_rules_default;
|
|
179
|
+
var defaultValidationMessages = metadataRulesFile.defaultValidationMessages ?? {};
|
|
180
|
+
var rawRuleEntries = Object.entries(metadata_rules_default).filter(
|
|
181
|
+
([type]) => type !== "defaultValidationMessages"
|
|
182
|
+
);
|
|
183
|
+
var BUILT_IN_JSDOC_FORMAT_TYPES = Object.fromEntries(
|
|
184
|
+
rawRuleEntries.map(([type, rule]) => [
|
|
185
|
+
type,
|
|
186
|
+
{
|
|
187
|
+
regex: rule.regex,
|
|
188
|
+
jsonSchemaFormat: rule.jsonSchemaFormat,
|
|
189
|
+
contentEncoding: rule.contentEncoding,
|
|
190
|
+
errorMessage: rule.errorMessage
|
|
191
|
+
}
|
|
192
|
+
])
|
|
193
|
+
);
|
|
194
|
+
var BUILT_IN_FORMAT_HELPERS = Object.fromEntries(
|
|
195
|
+
rawRuleEntries.filter(([, rule]) => rule.helper).map(([type, rule]) => [type, rule.helper])
|
|
196
|
+
);
|
|
197
|
+
function normalizeFormatDefinition(definition) {
|
|
198
|
+
if (!definition) return void 0;
|
|
199
|
+
return typeof definition === "string" ? { regex: definition } : definition;
|
|
200
|
+
}
|
|
201
|
+
function formatDefinitionFor(type, options) {
|
|
202
|
+
const builtIn = BUILT_IN_JSDOC_FORMAT_TYPES[type];
|
|
203
|
+
const custom = normalizeFormatDefinition(
|
|
204
|
+
options.customJSDocFormatTypes?.[type]
|
|
205
|
+
);
|
|
206
|
+
if (!custom) return builtIn;
|
|
207
|
+
return {
|
|
208
|
+
...builtIn,
|
|
209
|
+
...custom,
|
|
210
|
+
errorMessage: custom.errorMessage ?? builtIn?.errorMessage
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
function stripUndefined(value) {
|
|
214
|
+
return Object.fromEntries(
|
|
215
|
+
Object.entries(value).filter(([, entry]) => entry !== void 0)
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
function defaultBoundMessage(kind, value) {
|
|
219
|
+
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}");
|
|
220
|
+
return template.replace("{value}", value).replace("{plural}", value === "1" ? "" : "s");
|
|
221
|
+
}
|
|
222
|
+
function withDefaultBoundMessage(bound, kind) {
|
|
223
|
+
if (!bound || bound.message !== void 0) return bound;
|
|
224
|
+
return {
|
|
225
|
+
...bound,
|
|
226
|
+
message: defaultBoundMessage(kind, bound.value)
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
function normalizeValidationMetadata(validation, options) {
|
|
230
|
+
const normalized = {
|
|
231
|
+
...validation,
|
|
232
|
+
minimum: withDefaultBoundMessage(validation.minimum, "minimum"),
|
|
233
|
+
maximum: withDefaultBoundMessage(validation.maximum, "maximum"),
|
|
234
|
+
minLength: withDefaultBoundMessage(validation.minLength, "minLength"),
|
|
235
|
+
maxLength: withDefaultBoundMessage(validation.maxLength, "maxLength")
|
|
236
|
+
};
|
|
237
|
+
const format = validation.format;
|
|
238
|
+
const formatDefinition = format ? formatDefinitionFor(format.type, options) : void 0;
|
|
239
|
+
if (format && formatDefinition) {
|
|
240
|
+
normalized.format = stripUndefined({
|
|
241
|
+
...format,
|
|
242
|
+
regex: formatDefinition.regex,
|
|
243
|
+
jsonSchemaFormat: formatDefinition.jsonSchemaFormat,
|
|
244
|
+
contentEncoding: formatDefinition.contentEncoding,
|
|
245
|
+
errorMessage: format.message ?? formatDefinition.errorMessage
|
|
246
|
+
});
|
|
247
|
+
} else if (format) {
|
|
248
|
+
normalized.format = format;
|
|
249
|
+
}
|
|
250
|
+
return normalized;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// src/renderer.ts
|
|
254
|
+
var reservedWords = /* @__PURE__ */ new Set([
|
|
255
|
+
"break",
|
|
256
|
+
"case",
|
|
257
|
+
"catch",
|
|
258
|
+
"class",
|
|
259
|
+
"const",
|
|
260
|
+
"continue",
|
|
261
|
+
"debugger",
|
|
262
|
+
"default",
|
|
263
|
+
"delete",
|
|
264
|
+
"do",
|
|
265
|
+
"else",
|
|
266
|
+
"export",
|
|
267
|
+
"extends",
|
|
268
|
+
"false",
|
|
269
|
+
"finally",
|
|
270
|
+
"for",
|
|
271
|
+
"function",
|
|
272
|
+
"if",
|
|
273
|
+
"import",
|
|
274
|
+
"in",
|
|
275
|
+
"instanceof",
|
|
276
|
+
"new",
|
|
277
|
+
"null",
|
|
278
|
+
"return",
|
|
279
|
+
"super",
|
|
280
|
+
"switch",
|
|
281
|
+
"this",
|
|
282
|
+
"throw",
|
|
283
|
+
"true",
|
|
284
|
+
"try",
|
|
285
|
+
"typeof",
|
|
286
|
+
"var",
|
|
287
|
+
"void",
|
|
288
|
+
"while",
|
|
289
|
+
"with",
|
|
290
|
+
"yield",
|
|
291
|
+
"let",
|
|
292
|
+
"package",
|
|
293
|
+
"private",
|
|
294
|
+
"protected",
|
|
295
|
+
"public",
|
|
296
|
+
"static"
|
|
297
|
+
]);
|
|
298
|
+
function isValidIdentifier(name) {
|
|
299
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name) && !reservedWords.has(name);
|
|
300
|
+
}
|
|
301
|
+
function assertIdentifier(name, label) {
|
|
302
|
+
if (!isValidIdentifier(name)) {
|
|
303
|
+
throw new Error(`${label} must be a valid TypeScript identifier: ${name}`);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
function propertyName(name) {
|
|
307
|
+
return isValidIdentifier(name) ? name : JSON.stringify(name);
|
|
308
|
+
}
|
|
309
|
+
function normalizeOptions(options = {}) {
|
|
310
|
+
return typeof options === "string" ? { serviceExportName: options } : options;
|
|
311
|
+
}
|
|
312
|
+
function toServiceExportName(canisterName) {
|
|
313
|
+
return canisterName.replace(/[-\s]+/g, "_").toUpperCase();
|
|
314
|
+
}
|
|
315
|
+
function resolveServiceExportName(options, declaredTypeNames) {
|
|
316
|
+
if (options.serviceExportName) return options.serviceExportName;
|
|
317
|
+
if (options.canisterName) {
|
|
318
|
+
let name = toServiceExportName(options.canisterName);
|
|
319
|
+
if (declaredTypeNames.has(name)) {
|
|
320
|
+
name = `${name}_SERVICE`;
|
|
321
|
+
}
|
|
322
|
+
return name;
|
|
323
|
+
}
|
|
324
|
+
return "_SERVICE";
|
|
325
|
+
}
|
|
326
|
+
function hasMetadata(metadata) {
|
|
327
|
+
return metadata != null && Object.keys(metadata).length > 0;
|
|
328
|
+
}
|
|
329
|
+
function stripUndefined2(value) {
|
|
330
|
+
return Object.fromEntries(
|
|
331
|
+
Object.entries(value).filter(([, entry]) => entry !== void 0)
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
function isEmptyObject(value) {
|
|
335
|
+
return value != null && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === 0;
|
|
336
|
+
}
|
|
337
|
+
function textFormatHelperFor(expression, metadata, options) {
|
|
338
|
+
const format = metadata.validation?.format;
|
|
339
|
+
if (!format || expression !== "c.text()") return void 0;
|
|
340
|
+
if (options.customJSDocFormatTypes?.[format.type]) {
|
|
341
|
+
return void 0;
|
|
342
|
+
}
|
|
343
|
+
return BUILT_IN_FORMAT_HELPERS[format.type];
|
|
344
|
+
}
|
|
345
|
+
function hasOnlyDescriptionDocs(docs, description) {
|
|
346
|
+
return docs != null && description != null && docs.length === 1 && docs[0] === description;
|
|
347
|
+
}
|
|
348
|
+
function metadataForRender(metadata, options) {
|
|
349
|
+
if (!metadata.validation) {
|
|
350
|
+
return metadata;
|
|
351
|
+
}
|
|
352
|
+
return {
|
|
353
|
+
...metadata,
|
|
354
|
+
validation: normalizeValidationMetadata(metadata.validation, {
|
|
355
|
+
customJSDocFormatTypes: options.customJSDocFormatTypes
|
|
356
|
+
})
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
function applyMetadata(expression, metadata, options) {
|
|
360
|
+
if (!hasMetadata(metadata)) return expression;
|
|
361
|
+
const textFormatHelper = textFormatHelperFor(expression, metadata, options);
|
|
362
|
+
const textFormatMessage = metadata.validation?.format?.message;
|
|
363
|
+
const renderedMetadata = metadataForRender(metadata, options);
|
|
364
|
+
const { description, ...metadataRest } = renderedMetadata;
|
|
365
|
+
const rest = { ...metadataRest };
|
|
366
|
+
let result = textFormatHelper ? `c.${textFormatHelper}(${textFormatMessage ? JSON.stringify(textFormatMessage) : ""})` : expression;
|
|
367
|
+
if (textFormatHelper) {
|
|
368
|
+
delete rest.docs;
|
|
369
|
+
if (rest.validation) {
|
|
370
|
+
const { format: _format, ...validationRest } = rest.validation;
|
|
371
|
+
rest.validation = stripUndefined2(validationRest);
|
|
372
|
+
if (isEmptyObject(rest.validation)) {
|
|
373
|
+
delete rest.validation;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
} else if (hasOnlyDescriptionDocs(rest.docs, description)) {
|
|
377
|
+
delete rest.docs;
|
|
378
|
+
}
|
|
379
|
+
if (description) {
|
|
380
|
+
result += `.describe(${JSON.stringify(description)})`;
|
|
381
|
+
}
|
|
382
|
+
if (Object.keys(rest).length > 0) {
|
|
383
|
+
result += `.meta(${JSON.stringify(rest)})`;
|
|
384
|
+
}
|
|
385
|
+
return result;
|
|
386
|
+
}
|
|
387
|
+
function getReferencedNames(type) {
|
|
388
|
+
const refs = [];
|
|
389
|
+
function visit(node) {
|
|
390
|
+
switch (node.kind) {
|
|
391
|
+
case "reference":
|
|
392
|
+
refs.push(node.name);
|
|
393
|
+
break;
|
|
394
|
+
case "opt":
|
|
395
|
+
case "vec":
|
|
396
|
+
visit(node.type);
|
|
397
|
+
break;
|
|
398
|
+
case "record":
|
|
399
|
+
case "variant":
|
|
400
|
+
for (const field of node.fields) {
|
|
401
|
+
visit(field.type);
|
|
402
|
+
}
|
|
403
|
+
break;
|
|
404
|
+
case "tuple":
|
|
405
|
+
for (const item of node.types) {
|
|
406
|
+
visit(item);
|
|
407
|
+
}
|
|
408
|
+
break;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
visit(type);
|
|
412
|
+
return refs;
|
|
413
|
+
}
|
|
414
|
+
function sortDeclarations(declarations) {
|
|
415
|
+
const sorted = [];
|
|
416
|
+
const visited = /* @__PURE__ */ new Set();
|
|
417
|
+
const visiting = [];
|
|
418
|
+
const declarationByName = new Map(
|
|
419
|
+
declarations.map((decl) => [decl.name, decl])
|
|
420
|
+
);
|
|
421
|
+
function visit(name) {
|
|
422
|
+
if (visited.has(name)) return;
|
|
423
|
+
const cycleStart = visiting.indexOf(name);
|
|
424
|
+
if (cycleStart !== -1) {
|
|
425
|
+
const cycle = [...visiting.slice(cycleStart), name].join(" -> ");
|
|
426
|
+
throw new Error(`Recursive Candid types are not supported yet: ${cycle}`);
|
|
427
|
+
}
|
|
428
|
+
const declaration = declarationByName.get(name);
|
|
429
|
+
if (!declaration) return;
|
|
430
|
+
visiting.push(name);
|
|
431
|
+
for (const dependency of getReferencedNames(declaration.type)) {
|
|
432
|
+
visit(dependency);
|
|
433
|
+
}
|
|
434
|
+
visiting.pop();
|
|
435
|
+
visited.add(name);
|
|
436
|
+
sorted.push(declaration);
|
|
437
|
+
}
|
|
438
|
+
for (const declaration of declarations) {
|
|
439
|
+
visit(declaration.name);
|
|
440
|
+
}
|
|
441
|
+
return sorted;
|
|
442
|
+
}
|
|
443
|
+
function renderType(type, indent = "", options = {}) {
|
|
444
|
+
const nextIndent = `${indent} `;
|
|
445
|
+
let expression;
|
|
446
|
+
switch (type.kind) {
|
|
447
|
+
case "null":
|
|
448
|
+
expression = "c.null()";
|
|
449
|
+
break;
|
|
450
|
+
case "bool":
|
|
451
|
+
expression = "c.bool()";
|
|
452
|
+
break;
|
|
453
|
+
case "nat":
|
|
454
|
+
expression = "c.nat()";
|
|
455
|
+
break;
|
|
456
|
+
case "int":
|
|
457
|
+
expression = "c.int()";
|
|
458
|
+
break;
|
|
459
|
+
case "nat8":
|
|
460
|
+
expression = "c.nat8()";
|
|
461
|
+
break;
|
|
462
|
+
case "nat16":
|
|
463
|
+
expression = "c.nat16()";
|
|
464
|
+
break;
|
|
465
|
+
case "nat32":
|
|
466
|
+
expression = "c.nat32()";
|
|
467
|
+
break;
|
|
468
|
+
case "nat64":
|
|
469
|
+
expression = "c.nat64()";
|
|
470
|
+
break;
|
|
471
|
+
case "int8":
|
|
472
|
+
expression = "c.int8()";
|
|
473
|
+
break;
|
|
474
|
+
case "int16":
|
|
475
|
+
expression = "c.int16()";
|
|
476
|
+
break;
|
|
477
|
+
case "int32":
|
|
478
|
+
expression = "c.int32()";
|
|
479
|
+
break;
|
|
480
|
+
case "int64":
|
|
481
|
+
expression = "c.int64()";
|
|
482
|
+
break;
|
|
483
|
+
case "float32":
|
|
484
|
+
expression = "c.float32()";
|
|
485
|
+
break;
|
|
486
|
+
case "float64":
|
|
487
|
+
expression = "c.float64()";
|
|
488
|
+
break;
|
|
489
|
+
case "text":
|
|
490
|
+
expression = "c.text()";
|
|
491
|
+
break;
|
|
492
|
+
case "reserved":
|
|
493
|
+
expression = "c.reserved()";
|
|
494
|
+
break;
|
|
495
|
+
case "empty":
|
|
496
|
+
expression = "c.empty()";
|
|
497
|
+
break;
|
|
498
|
+
case "principal":
|
|
499
|
+
expression = "c.principal()";
|
|
500
|
+
break;
|
|
501
|
+
case "blob":
|
|
502
|
+
expression = "c.blob()";
|
|
503
|
+
break;
|
|
504
|
+
case "reference":
|
|
505
|
+
assertIdentifier(type.name, "Type reference");
|
|
506
|
+
expression = type.name;
|
|
507
|
+
break;
|
|
508
|
+
case "opt":
|
|
509
|
+
expression = `c.opt(${renderType(type.type, indent, options)})`;
|
|
510
|
+
break;
|
|
511
|
+
case "vec":
|
|
512
|
+
expression = `c.vec(${renderType(type.type, indent, options)})`;
|
|
513
|
+
break;
|
|
514
|
+
case "record": {
|
|
515
|
+
if (type.fields.length === 0) {
|
|
516
|
+
expression = "c.record({})";
|
|
517
|
+
} else {
|
|
518
|
+
const fields = type.fields.map((field) => {
|
|
519
|
+
const fieldExpression = applyMetadata(
|
|
520
|
+
renderType(field.type, nextIndent, options),
|
|
521
|
+
field.metadata,
|
|
522
|
+
options
|
|
523
|
+
);
|
|
524
|
+
return `${nextIndent}${propertyName(field.name)}: ${fieldExpression},`;
|
|
525
|
+
}).join("\n");
|
|
526
|
+
expression = `c.record({
|
|
527
|
+
${fields}
|
|
528
|
+
${indent}})`;
|
|
529
|
+
}
|
|
530
|
+
break;
|
|
531
|
+
}
|
|
532
|
+
case "variant": {
|
|
533
|
+
if (type.fields.length === 0) {
|
|
534
|
+
expression = "c.variant({})";
|
|
535
|
+
} else {
|
|
536
|
+
const fields = type.fields.map((field) => {
|
|
537
|
+
const fieldExpression = applyMetadata(
|
|
538
|
+
renderType(field.type, nextIndent, options),
|
|
539
|
+
field.metadata,
|
|
540
|
+
options
|
|
541
|
+
);
|
|
542
|
+
return `${nextIndent}${propertyName(field.name)}: ${fieldExpression},`;
|
|
543
|
+
}).join("\n");
|
|
544
|
+
expression = `c.variant({
|
|
545
|
+
${fields}
|
|
546
|
+
${indent}})`;
|
|
547
|
+
}
|
|
548
|
+
break;
|
|
549
|
+
}
|
|
550
|
+
case "tuple":
|
|
551
|
+
expression = `c.tuple([${type.types.map((item) => renderType(item, indent, options)).join(", ")}])`;
|
|
552
|
+
break;
|
|
553
|
+
case "func":
|
|
554
|
+
case "service":
|
|
555
|
+
case "class":
|
|
556
|
+
case "unknown":
|
|
557
|
+
case "knot":
|
|
558
|
+
case "future":
|
|
559
|
+
expression = `/* c.${type.kind} is not supported */ c.reserved()`;
|
|
560
|
+
break;
|
|
561
|
+
}
|
|
562
|
+
return applyMetadata(expression, type.metadata, options);
|
|
563
|
+
}
|
|
564
|
+
function renderMethodReturn(method, options) {
|
|
565
|
+
if (method.mode === "oneway") return "";
|
|
566
|
+
if (method.returns.length === 0) return "";
|
|
567
|
+
if (method.returns.length === 1) {
|
|
568
|
+
return `, ${renderType(method.returns[0], " ", options)}`;
|
|
569
|
+
}
|
|
570
|
+
return `, [${method.returns.map((returnType) => renderType(returnType, " ", options)).join(", ")}]`;
|
|
571
|
+
}
|
|
572
|
+
function generateCodecDeclarations(schema, optionsOrServiceExportName = {}) {
|
|
573
|
+
const options = normalizeOptions(optionsOrServiceExportName);
|
|
574
|
+
const lines = ['import { c } from "@ic-reactor/cod"', ""];
|
|
575
|
+
for (const declaration of sortDeclarations(schema.types)) {
|
|
576
|
+
assertIdentifier(declaration.name, "Type declaration name");
|
|
577
|
+
lines.push(
|
|
578
|
+
`export const ${declaration.name} = ${applyMetadata(
|
|
579
|
+
renderType(declaration.type, "", options),
|
|
580
|
+
declaration.metadata,
|
|
581
|
+
options
|
|
582
|
+
)}`
|
|
583
|
+
);
|
|
584
|
+
lines.push(
|
|
585
|
+
`export type ${declaration.name} = c.infer<typeof ${declaration.name}>`
|
|
586
|
+
);
|
|
587
|
+
lines.push("");
|
|
588
|
+
}
|
|
589
|
+
if (schema.service) {
|
|
590
|
+
const includeCompatibilityExports = options.includeCompatibilityExports ?? false;
|
|
591
|
+
const declaredTypeNames = new Set(schema.types.map((t) => t.name));
|
|
592
|
+
const serviceExportName = resolveServiceExportName(
|
|
593
|
+
options,
|
|
594
|
+
declaredTypeNames
|
|
595
|
+
);
|
|
596
|
+
assertIdentifier(serviceExportName, "Service export name");
|
|
597
|
+
const methods = schema.service.methods.map((method) => {
|
|
598
|
+
const args = method.args.map((arg) => renderType(arg, " ", options)).join(", ");
|
|
599
|
+
const methodExpression = `c.${method.mode}([${args}]${renderMethodReturn(
|
|
600
|
+
method,
|
|
601
|
+
options
|
|
602
|
+
)})`;
|
|
603
|
+
return ` ${propertyName(method.name)}: ${applyMetadata(
|
|
604
|
+
methodExpression,
|
|
605
|
+
method.metadata,
|
|
606
|
+
options
|
|
607
|
+
)},`;
|
|
608
|
+
}).join("\n");
|
|
609
|
+
const serviceExpression = methods.length > 0 ? `c.service({
|
|
610
|
+
${methods}
|
|
611
|
+
})` : "c.service({})";
|
|
612
|
+
lines.push(
|
|
613
|
+
`export const ${serviceExportName} = ${applyMetadata(
|
|
614
|
+
serviceExpression,
|
|
615
|
+
schema.service.metadata,
|
|
616
|
+
options
|
|
617
|
+
)}`
|
|
618
|
+
);
|
|
619
|
+
lines.push("");
|
|
620
|
+
if (includeCompatibilityExports) {
|
|
621
|
+
lines.push(`export const idlFactory = ${serviceExportName}.idlFactory`);
|
|
622
|
+
lines.push(
|
|
623
|
+
`export type _SERVICE = c.ServiceOf<typeof ${serviceExportName}>`
|
|
624
|
+
);
|
|
625
|
+
lines.push("");
|
|
626
|
+
lines.push(`export const manifest = ${serviceExportName}.manifest()`);
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
return `${lines.join("\n").trimEnd()}
|
|
630
|
+
`;
|
|
631
|
+
}
|
|
632
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
633
|
+
0 && (module.exports = {
|
|
634
|
+
BUILT_IN_FORMAT_HELPERS,
|
|
635
|
+
BUILT_IN_JSDOC_FORMAT_TYPES,
|
|
636
|
+
generateCodecDeclarations,
|
|
637
|
+
renderType,
|
|
638
|
+
sortDeclarations
|
|
639
|
+
});
|