@ic-reactor/codegen 0.12.0 → 0.13.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/dist/renderer.js DELETED
@@ -1,14 +0,0 @@
1
- import {
2
- BUILT_IN_FORMAT_HELPERS,
3
- BUILT_IN_JSDOC_FORMAT_TYPES,
4
- generateCodecDeclarations,
5
- renderType,
6
- sortDeclarations
7
- } from "./chunk-VBCR5IVT.js";
8
- export {
9
- BUILT_IN_FORMAT_HELPERS,
10
- BUILT_IN_JSDOC_FORMAT_TYPES,
11
- generateCodecDeclarations,
12
- renderType,
13
- sortDeclarations
14
- };
@@ -1,18 +0,0 @@
1
- // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
-
3
- exports[`Bindgen > generateDeclarations creates correct files > js-declarations 1`] = `
4
- "export const idlFactory = ({ IDL }) => {
5
- return IDL.Service({ 'greet' : IDL.Func([IDL.Text], [IDL.Text], ['query']) });
6
- };
7
- export const init = ({ IDL }) => { return []; };"
8
- `;
9
-
10
- exports[`Bindgen > generateDeclarations creates correct files > ts-declarations 1`] = `
11
- "import type { Principal } from '@icp-sdk/core/principal';
12
- import type { ActorMethod } from '@icp-sdk/core/agent';
13
- import type { IDL } from '@icp-sdk/core/candid';
14
-
15
- export interface _SERVICE { 'greet' : ActorMethod<[string], string> }
16
- export declare const idlFactory: IDL.InterfaceFactory;
17
- export declare const init: (args: { IDL: typeof IDL }) => IDL.Type[];"
18
- `;
@@ -1,127 +0,0 @@
1
- // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
-
3
- exports[`Reactor generator > keeps default behavior as DisplayReactor > display-reactor-index 1`] = `
4
- "import { createActorHooks } from "@ic-reactor/react"
5
- import { DisplayReactor } from "@ic-reactor/react"
6
- import { clientManager } from "../../clients"
7
- import { idlFactory, type _SERVICE } from "./declarations/backend"
8
-
9
- export type BackendService = _SERVICE
10
-
11
- /**
12
- * Backend Reactor
13
- *
14
- * Auto-generated by @ic-reactor/codegen — do not edit.
15
- * This file is overwritten whenever generation runs.
16
- *
17
- * Keep app-specific logic in the stable \`index.ts\` wrapper (or adjacent
18
- * factory modules). Avoid editing this managed file directly.
19
- */
20
- export const backendReactor = new DisplayReactor<BackendService>({
21
- clientManager,
22
- idlFactory,
23
- name: "backend",
24
- })
25
-
26
- export const {
27
- useActorQuery: useBackendQuery,
28
- useActorSuspenseQuery: useBackendSuspenseQuery,
29
- useActorInfiniteQuery: useBackendInfiniteQuery,
30
- useActorSuspenseInfiniteQuery: useBackendSuspenseInfiniteQuery,
31
- useActorMutation: useBackendMutation,
32
- useActorMethod: useBackendMethod,
33
- } = createActorHooks(backendReactor)
34
- "
35
- `;
36
-
37
- exports[`Reactor generator > supports Reactor mode generation > reactor-index 1`] = `
38
- "import { createActorHooks } from "@ic-reactor/react"
39
- import { Reactor } from "@ic-reactor/react"
40
- import { clientManager } from "../../clients"
41
- import { idlFactory, type _SERVICE } from "./declarations/workflow_engine"
42
-
43
- export type WorkflowEngineService = _SERVICE
44
-
45
- /**
46
- * WorkflowEngine Reactor
47
- *
48
- * Auto-generated by @ic-reactor/codegen — do not edit.
49
- * This file is overwritten whenever generation runs.
50
- *
51
- * Keep app-specific logic in the stable \`index.ts\` wrapper (or adjacent
52
- * factory modules). Avoid editing this managed file directly.
53
- */
54
- export const workflowEngineReactor = new Reactor<WorkflowEngineService>({
55
- clientManager,
56
- idlFactory,
57
- name: "workflow_engine",
58
- })
59
-
60
- export const {
61
- useActorQuery: useWorkflowEngineQuery,
62
- useActorSuspenseQuery: useWorkflowEngineSuspenseQuery,
63
- useActorInfiniteQuery: useWorkflowEngineInfiniteQuery,
64
- useActorSuspenseInfiniteQuery: useWorkflowEngineSuspenseInfiniteQuery,
65
- useActorMutation: useWorkflowEngineMutation,
66
- useActorMethod: useWorkflowEngineMethod,
67
- } = createActorHooks(workflowEngineReactor)
68
- "
69
- `;
70
-
71
- exports[`Reactor generator > supports candid reactor subclasses > metadata-display-reactor-index 1`] = `
72
- "import { createActorHooks } from "@ic-reactor/react"
73
- import { MetadataDisplayReactor } from "@ic-reactor/candid"
74
- import { clientManager } from "../../clients"
75
- import { idlFactory, type _SERVICE } from "./declarations/ledger"
76
-
77
- export type LedgerService = _SERVICE
78
-
79
- /**
80
- * Ledger Reactor
81
- *
82
- * Auto-generated by @ic-reactor/codegen — do not edit.
83
- * This file is overwritten whenever generation runs.
84
- *
85
- * Keep app-specific logic in the stable \`index.ts\` wrapper (or adjacent
86
- * factory modules). Avoid editing this managed file directly.
87
- */
88
- export const ledgerReactor = new MetadataDisplayReactor<LedgerService>({
89
- clientManager,
90
- idlFactory,
91
- name: "ledger",
92
- })
93
-
94
- export const {
95
- useActorQuery: useLedgerQuery,
96
- useActorSuspenseQuery: useLedgerSuspenseQuery,
97
- useActorInfiniteQuery: useLedgerInfiniteQuery,
98
- useActorSuspenseInfiniteQuery: useLedgerSuspenseInfiniteQuery,
99
- useActorMutation: useLedgerMutation,
100
- useActorMethod: useLedgerMethod,
101
- } = createActorHooks(ledgerReactor)
102
- "
103
- `;
104
-
105
- exports[`Reactor generator > supports core target generation without React hooks > core-display-reactor-index 1`] = `
106
- "import { DisplayReactor } from "@ic-reactor/core"
107
- import { clientManager } from "../../clients"
108
- import { idlFactory, type _SERVICE } from "./declarations/backend"
109
-
110
- export type BackendService = _SERVICE
111
-
112
- /**
113
- * Backend Reactor
114
- *
115
- * Auto-generated by @ic-reactor/codegen — do not edit.
116
- * This file is overwritten whenever generation runs.
117
- *
118
- * Keep app-specific logic in the stable \`index.ts\` wrapper (or adjacent
119
- * factory modules). Avoid editing this managed file directly.
120
- */
121
- export const backendReactor = new DisplayReactor<BackendService>({
122
- clientManager,
123
- idlFactory,
124
- name: "backend",
125
- })
126
- "
127
- `;
@@ -1,85 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"
2
- import fs from "node:fs"
3
- import path from "node:path"
4
- import { generateDeclarations } from "./generators/index.js"
5
-
6
- describe("Bindgen", () => {
7
- const mockDidFile = "mock/test.did"
8
- const mockOutDir = "mock/output"
9
- const mockCanisterName = "test_canister"
10
- const validDidContent = `service : {
11
- greet: (text) -> (text) query;
12
- }`
13
-
14
- beforeEach(() => {
15
- // Mock fs methods
16
- vi.spyOn(fs, "existsSync").mockImplementation((p) => {
17
- // Pretend the source DID file exists
18
- if (p.toString() === mockDidFile) return true
19
- return false
20
- })
21
-
22
- vi.spyOn(fs, "mkdirSync").mockImplementation(() => undefined as any)
23
- vi.spyOn(fs, "rmSync").mockImplementation(() => undefined)
24
- vi.spyOn(fs, "readFileSync").mockImplementation(() => validDidContent)
25
- vi.spyOn(fs, "writeFileSync").mockImplementation(() => undefined)
26
- })
27
-
28
- afterEach(() => {
29
- vi.restoreAllMocks()
30
- })
31
-
32
- it("generateDeclarations creates correct files", async () => {
33
- const result = await generateDeclarations({
34
- didFile: mockDidFile,
35
- outDir: mockOutDir,
36
- canisterName: mockCanisterName,
37
- })
38
-
39
- if (!result.success) {
40
- console.error(result.error)
41
- }
42
-
43
- expect(result.success).toBe(true)
44
- expect(result.declarationsDir).toBe(path.join(mockOutDir, "declarations"))
45
-
46
- // Verify file writes
47
- const declarationsDir = path.join(mockOutDir, "declarations")
48
- const jsPath = path.join(declarationsDir, "test.js")
49
- const dtsPath = path.join(declarationsDir, "test.d.ts")
50
-
51
- // Should create directory
52
- expect(fs.mkdirSync).toHaveBeenCalledWith(mockOutDir, { recursive: true })
53
- expect(fs.mkdirSync).toHaveBeenCalledWith(declarationsDir, {
54
- recursive: true,
55
- })
56
-
57
- // Validate exact content using snapshots
58
- expect(fs.writeFileSync).toHaveBeenCalledWith(jsPath, expect.any(String))
59
- expect(fs.writeFileSync).toHaveBeenCalledWith(dtsPath, expect.any(String))
60
-
61
- // Get the arguments of the calls to check content
62
- const jsCall = vi
63
- .mocked(fs.writeFileSync)
64
- .mock.calls.find((call) => call[0] === jsPath)
65
- const dtsCall = vi
66
- .mocked(fs.writeFileSync)
67
- .mock.calls.find((call) => call[0] === dtsPath)
68
-
69
- expect(jsCall?.[1]).toMatchSnapshot("js-declarations")
70
- expect(dtsCall?.[1]).toMatchSnapshot("ts-declarations")
71
- })
72
-
73
- it("returns error if DID file missing", async () => {
74
- vi.spyOn(fs, "existsSync").mockReturnValue(false)
75
-
76
- const result = await generateDeclarations({
77
- didFile: "missing.did",
78
- outDir: mockOutDir,
79
- canisterName: mockCanisterName,
80
- })
81
-
82
- expect(result.success).toBe(false)
83
- expect(result.error).toContain("DID file not found")
84
- })
85
- })
@@ -1,144 +0,0 @@
1
- {
2
- "defaultValidationMessages": {
3
- "minimum": {
4
- "template": "Must be at least {value}"
5
- },
6
- "maximum": {
7
- "template": "Must be at most {value}"
8
- },
9
- "minLength": {
10
- "template": "Must be at least {value} character{plural}"
11
- },
12
- "maxLength": {
13
- "template": "Must be at most {value} character{plural}"
14
- }
15
- },
16
- "email": {
17
- "helper": "email",
18
- "regex": "^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$",
19
- "jsonSchemaFormat": "email",
20
- "errorMessage": "Must be a valid email address"
21
- },
22
- "date-time": {
23
- "helper": "dateTime",
24
- "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))$",
25
- "jsonSchemaFormat": "date-time",
26
- "errorMessage": "Must be a valid ISO datetime"
27
- },
28
- "datetime": {
29
- "helper": "datetime",
30
- "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))$",
31
- "jsonSchemaFormat": "date-time",
32
- "errorMessage": "Must be a valid ISO datetime"
33
- },
34
- "date": {
35
- "helper": "date",
36
- "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])))$",
37
- "jsonSchemaFormat": "date",
38
- "errorMessage": "Must be a valid ISO date"
39
- },
40
- "time": {
41
- "helper": "time",
42
- "regex": "^(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?$",
43
- "errorMessage": "Must be a valid ISO time"
44
- },
45
- "duration": {
46
- "helper": "duration",
47
- "regex": "^P(?:(\\d+W)|(?!.*W)(?=\\d|T\\d)(\\d+Y)?(\\d+M)?(\\d+D)?(T(?=\\d)(\\d+H)?(\\d+M)?(\\d+([.,]\\d+)?S)?)?)$",
48
- "jsonSchemaFormat": "duration",
49
- "errorMessage": "Must be a valid ISO duration"
50
- },
51
- "url": {
52
- "helper": "url",
53
- "regex": "^[a-zA-Z][a-zA-Z\\d+.-]*:.+",
54
- "jsonSchemaFormat": "uri",
55
- "errorMessage": "Must be a valid URL"
56
- },
57
- "uri": {
58
- "helper": "uri",
59
- "regex": "^[a-zA-Z][a-zA-Z\\d+.-]*:.+",
60
- "jsonSchemaFormat": "uri",
61
- "errorMessage": "Must be a valid URI"
62
- },
63
- "httpsUrl": {
64
- "helper": "httpsUrl",
65
- "regex": "^https://.+",
66
- "jsonSchemaFormat": "uri",
67
- "errorMessage": "Must be a valid HTTPS URL"
68
- },
69
- "ipv4": {
70
- "helper": "ipv4",
71
- "regex": "^(?:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(?:\\.|$)){4}$",
72
- "jsonSchemaFormat": "ipv4",
73
- "errorMessage": "Must be a valid IPv4 address"
74
- },
75
- "ipv6": {
76
- "helper": "ipv6",
77
- "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}))$",
78
- "jsonSchemaFormat": "ipv6",
79
- "errorMessage": "Must be a valid IPv6 address"
80
- },
81
- "uuid": {
82
- "helper": "uuid",
83
- "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}$",
84
- "jsonSchemaFormat": "uuid",
85
- "errorMessage": "Must be a valid UUID"
86
- },
87
- "guid": {
88
- "helper": "guid",
89
- "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}$",
90
- "jsonSchemaFormat": "uuid",
91
- "errorMessage": "Must be a valid GUID"
92
- },
93
- "base64": {
94
- "helper": "base64",
95
- "regex": "^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$",
96
- "contentEncoding": "base64",
97
- "errorMessage": "Must be valid base64"
98
- },
99
- "base64url": {
100
- "helper": "base64url",
101
- "regex": "^[A-Za-z0-9_-]+$",
102
- "errorMessage": "Must be valid base64url"
103
- },
104
- "cuid": {
105
- "helper": "cuid",
106
- "regex": "^c[a-z0-9]{24}$",
107
- "errorMessage": "Must be a valid CUID"
108
- },
109
- "cuid2": {
110
- "helper": "cuid2",
111
- "regex": "^[a-z][a-z0-9]*$",
112
- "errorMessage": "Must be a valid CUID2"
113
- },
114
- "ulid": {
115
- "helper": "ulid",
116
- "regex": "^[0-9A-HJKMNP-TV-Z]{26}$",
117
- "errorMessage": "Must be a valid ULID"
118
- },
119
- "nanoid": {
120
- "helper": "nanoid",
121
- "regex": "^[A-Za-z0-9_-]{21}$",
122
- "errorMessage": "Must be a valid nanoid"
123
- },
124
- "emoji": {
125
- "helper": "emoji",
126
- "regex": "^\\p{Extended_Pictographic}+$",
127
- "errorMessage": "Must contain only emoji characters"
128
- },
129
- "cidrv4": {
130
- "helper": "cidrv4",
131
- "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)$",
132
- "errorMessage": "Must be a valid IPv4 CIDR range"
133
- },
134
- "cidrv6": {
135
- "helper": "cidrv6",
136
- "regex": "^[0-9A-Fa-f:]+/(?:12[0-8]|1[01]\\d|\\d?\\d)$",
137
- "errorMessage": "Must be a valid IPv6 CIDR range"
138
- },
139
- "mac": {
140
- "helper": "mac",
141
- "regex": "^(?:[0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$",
142
- "errorMessage": "Must be a valid MAC address"
143
- }
144
- }
package/src/metadata.ts DELETED
@@ -1,157 +0,0 @@
1
- import type { CandidMetadata } from "@ic-reactor/parser"
2
- import metadataRules from "./metadata-rules.json"
3
-
4
- export type JSDocFormatDefinition = {
5
- regex?: string
6
- errorMessage?: string
7
- jsonSchemaFormat?: string
8
- contentEncoding?: string
9
- }
10
-
11
- export type CustomJSDocFormatTypes = Record<
12
- string,
13
- string | JSDocFormatDefinition
14
- >
15
-
16
- type ValidationBoundRule = {
17
- template?: string
18
- }
19
-
20
- type MetadataRulesFile = {
21
- defaultValidationMessages?: Record<string, ValidationBoundRule>
22
- }
23
-
24
- const metadataRulesFile = metadataRules as MetadataRulesFile
25
- const defaultValidationMessages =
26
- metadataRulesFile.defaultValidationMessages ?? {}
27
-
28
- const rawRuleEntries = Object.entries(metadataRules).filter(
29
- ([type]) => type !== "defaultValidationMessages"
30
- ) as Array<
31
- [
32
- string,
33
- {
34
- helper?: string
35
- regex?: string
36
- jsonSchemaFormat?: string
37
- contentEncoding?: string
38
- errorMessage?: string
39
- },
40
- ]
41
- >
42
-
43
- export const BUILT_IN_JSDOC_FORMAT_TYPES: Readonly<
44
- Record<string, JSDocFormatDefinition>
45
- > = Object.fromEntries(
46
- rawRuleEntries.map(([type, rule]) => [
47
- type,
48
- {
49
- regex: rule.regex,
50
- jsonSchemaFormat: rule.jsonSchemaFormat,
51
- contentEncoding: rule.contentEncoding,
52
- errorMessage: rule.errorMessage,
53
- },
54
- ])
55
- ) as Record<string, JSDocFormatDefinition>
56
-
57
- export const BUILT_IN_FORMAT_HELPERS: Readonly<Record<string, string>> =
58
- Object.fromEntries(
59
- rawRuleEntries
60
- .filter(([, rule]) => rule.helper)
61
- .map(([type, rule]) => [type, rule.helper as string])
62
- )
63
-
64
- export interface FormatResolutionOptions {
65
- customJSDocFormatTypes?: CustomJSDocFormatTypes
66
- }
67
-
68
- export function normalizeFormatDefinition(
69
- definition: CustomJSDocFormatTypes[string] | undefined
70
- ): JSDocFormatDefinition | undefined {
71
- if (!definition) return undefined
72
- return typeof definition === "string" ? { regex: definition } : definition
73
- }
74
-
75
- export function formatDefinitionFor(
76
- type: string,
77
- options: FormatResolutionOptions
78
- ): JSDocFormatDefinition | undefined {
79
- const builtIn = BUILT_IN_JSDOC_FORMAT_TYPES[type]
80
- const custom = normalizeFormatDefinition(
81
- options.customJSDocFormatTypes?.[type]
82
- )
83
-
84
- if (!custom) return builtIn
85
- return {
86
- ...builtIn,
87
- ...custom,
88
- errorMessage: custom.errorMessage ?? builtIn?.errorMessage,
89
- }
90
- }
91
-
92
- function stripUndefined<T extends Record<string, unknown>>(value: T): T {
93
- return Object.fromEntries(
94
- Object.entries(value).filter(([, entry]) => entry !== undefined)
95
- ) as T
96
- }
97
-
98
- function defaultBoundMessage(
99
- kind: "minimum" | "maximum" | "minLength" | "maxLength",
100
- value: string
101
- ): string {
102
- const template =
103
- defaultValidationMessages[kind]?.template ??
104
- (kind === "minimum"
105
- ? "Must be at least {value}"
106
- : kind === "maximum"
107
- ? "Must be at most {value}"
108
- : kind === "minLength"
109
- ? "Must be at least {value} character{plural}"
110
- : "Must be at most {value} character{plural}")
111
-
112
- return template
113
- .replace("{value}", value)
114
- .replace("{plural}", value === "1" ? "" : "s")
115
- }
116
-
117
- function withDefaultBoundMessage<
118
- T extends { value: string; message?: string } | undefined,
119
- >(bound: T, kind: "minimum" | "maximum" | "minLength" | "maxLength"): T {
120
- if (!bound || bound.message !== undefined) return bound
121
- return {
122
- ...bound,
123
- message: defaultBoundMessage(kind, bound.value),
124
- } as T
125
- }
126
-
127
- export function normalizeValidationMetadata(
128
- validation: NonNullable<CandidMetadata["validation"]>,
129
- options: FormatResolutionOptions
130
- ): NonNullable<CandidMetadata["validation"]> {
131
- const normalized = {
132
- ...validation,
133
- minimum: withDefaultBoundMessage(validation.minimum, "minimum"),
134
- maximum: withDefaultBoundMessage(validation.maximum, "maximum"),
135
- minLength: withDefaultBoundMessage(validation.minLength, "minLength"),
136
- maxLength: withDefaultBoundMessage(validation.maxLength, "maxLength"),
137
- }
138
-
139
- const format = validation.format
140
- const formatDefinition = format
141
- ? formatDefinitionFor(format.type, options)
142
- : undefined
143
-
144
- if (format && formatDefinition) {
145
- normalized.format = stripUndefined({
146
- ...format,
147
- regex: formatDefinition.regex,
148
- jsonSchemaFormat: formatDefinition.jsonSchemaFormat,
149
- contentEncoding: formatDefinition.contentEncoding,
150
- errorMessage: format.message ?? formatDefinition.errorMessage,
151
- })
152
- } else if (format) {
153
- normalized.format = format
154
- }
155
-
156
- return normalized
157
- }
@@ -1,45 +0,0 @@
1
- import { describe, it, expect } from "vitest"
2
- import {
3
- toPascalCase,
4
- toCamelCase,
5
- getReactorName,
6
- getServiceTypeName,
7
- } from "./naming.js"
8
-
9
- describe("Naming Utilities", () => {
10
- describe("Base Conversions", () => {
11
- it("converts to PascalCase", () => {
12
- expect(toPascalCase("get_user")).toBe("GetUser")
13
- expect(toPascalCase("my-canister")).toBe("MyCanister")
14
- expect(toPascalCase("list_items_v2")).toBe("ListItemsV2")
15
- })
16
-
17
- it("converts to camelCase", () => {
18
- expect(toCamelCase("get_user")).toBe("getUser")
19
- expect(toCamelCase("my-canister")).toBe("myCanister")
20
- expect(toCamelCase("ListItems")).toBe("listItems")
21
- })
22
- })
23
-
24
- describe("Domain-Specific Naming", () => {
25
- it("generates reactor names", () => {
26
- expect(getReactorName("backend")).toBe("backendReactor")
27
- expect(getReactorName("my-canister")).toBe("myCanisterReactor")
28
- })
29
-
30
- it("generates service type names", () => {
31
- expect(getServiceTypeName("backend")).toBe("BackendService")
32
- expect(getServiceTypeName("update_item")).toBe("UpdateItemService")
33
- })
34
-
35
- it("generates reactor names", () => {
36
- expect(getReactorName("backend")).toBe("backendReactor")
37
- expect(getReactorName("my-canister")).toBe("myCanisterReactor")
38
- })
39
-
40
- it("generates service type names", () => {
41
- expect(getServiceTypeName("backend")).toBe("BackendService")
42
- expect(getServiceTypeName("my-canister")).toBe("MyCanisterService")
43
- })
44
- })
45
- })
package/src/parser.ts DELETED
@@ -1,79 +0,0 @@
1
- /**
2
- * Candid Parser Utilities
3
- *
4
- * Parses Candid .did files to extract service method signatures.
5
- * Used by the CLI for listing methods and by advanced generators.
6
- */
7
-
8
- import { didToJs } from "@ic-reactor/parser"
9
- import fs from "node:fs"
10
-
11
- // ─────────────────────────────────────────────────────────────────────────────
12
- // TYPES
13
- // ─────────────────────────────────────────────────────────────────────────────
14
-
15
- export type MethodType = "query" | "mutation"
16
-
17
- export interface MethodInfo {
18
- /** Method name as it appears in the Candid service */
19
- name: string
20
- /** "query" for read-only calls, "mutation" for update calls */
21
- type: MethodType
22
- /** True if the method takes at least one argument */
23
- hasArgs: boolean
24
- }
25
-
26
- // ─────────────────────────────────────────────────────────────────────────────
27
- // PARSERS
28
- // ─────────────────────────────────────────────────────────────────────────────
29
-
30
- /**
31
- * Extract method information from raw Candid source text.
32
- *
33
- * Compiles the Candid to JS and inspects the IDL.Service definition.
34
- */
35
- export function extractMethods(didContent: string): MethodInfo[] {
36
- try {
37
- const jsContent = didToJs(didContent)
38
-
39
- const methods: MethodInfo[] = []
40
-
41
- // Find the IDL.Service({...}) body
42
- const serviceMatch = /IDL\.Service\(\{([\s\S]*?)\}\)/.exec(jsContent)
43
- if (!serviceMatch) return methods
44
-
45
- const serviceBody = serviceMatch[1]
46
-
47
- // Match each method: 'methodName': IDL.Func([args], [rets], [annotations])
48
- const methodRegex =
49
- /['"]([\w]+)['"]\s*:\s*IDL\.Func\(\s*\[(.*?)\],\s*\[.*?\],\s*\[(.*?)\]\)/g
50
-
51
- let match: RegExpExecArray | null
52
- while ((match = methodRegex.exec(serviceBody)) !== null) {
53
- const [, name, args, annotations] = match
54
- methods.push({
55
- name,
56
- type: annotations.includes("'query'") ? "query" : "mutation",
57
- hasArgs: args.trim().length > 0,
58
- })
59
- }
60
-
61
- return methods
62
- } catch (error) {
63
- const msg = error instanceof Error ? error.message : String(error)
64
- throw new Error(`Failed to parse Candid: ${msg}`)
65
- }
66
- }
67
-
68
- /**
69
- * Parse a .did file from disk and return its methods.
70
- *
71
- * @throws if the file does not exist or fails to parse
72
- */
73
- export function parseDIDFile(didFilePath: string): MethodInfo[] {
74
- if (!fs.existsSync(didFilePath)) {
75
- throw new Error(`DID file not found: ${didFilePath}`)
76
- }
77
- const content = fs.readFileSync(didFilePath, "utf-8")
78
- return extractMethods(content)
79
- }