@hatchingpoint/point 0.0.5 → 0.0.7

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.
@@ -1,217 +1,394 @@
1
- import type {
2
- PointCoreDeclaration,
3
- PointCoreFunctionDeclaration,
4
- PointCoreProgram,
5
- PointCoreTypeDeclaration,
6
- PointCoreTypeExpression,
7
- PointCoreValueDeclaration,
8
- PointSourceSpan,
9
- } from "./ast.ts";
10
- import type {
11
- PointCoreDiagnostic,
12
- } from "./check.ts";
13
-
14
- export type PointCoreSymbolKind = "module" | "import" | "value" | "function" | "param" | "type" | "field";
15
-
16
- export interface PointCoreSymbol {
17
- ref: string;
18
- path: string;
19
- kind: PointCoreSymbolKind;
20
- name: string;
21
- module: string;
22
- type?: string;
23
- mutable?: boolean;
24
- from?: string;
25
- span: PointSourceSpan | null;
26
- }
27
-
28
- export interface PointCoreIndex {
29
- schemaVersion: "point.core.index.v1";
30
- module: string;
31
- refs: PointCoreSymbol[];
32
- }
33
-
34
- export interface PointCoreExplanation {
35
- schemaVersion: "point.core.explain.v1";
36
- ref: string;
37
- found: boolean;
38
- symbol?: PointCoreSymbol;
39
- relatedRefs: string[];
40
- summary: string;
41
- }
42
-
43
- export interface PointCoreRepairPlan {
44
- schemaVersion: "point.core.repair-plan.v1";
45
- ok: boolean;
46
- steps: PointCoreRepairStep[];
47
- }
48
-
49
- export interface PointCoreRepairStep {
50
- ref: string;
51
- code: string;
52
- message: string;
53
- repair: string;
54
- expected?: string | string[];
55
- actual?: string;
56
- relatedRefs: string[];
57
- }
58
-
59
- export function createPointCoreIndex(program: PointCoreProgram): PointCoreIndex {
60
- const moduleName = program.module ?? "anonymous";
61
- const refs: PointCoreSymbol[] = [
62
- {
63
- ref: refFor(moduleName, "module"),
64
- path: "module",
65
- kind: "module",
66
- name: moduleName,
67
- module: moduleName,
68
- span: program.span ?? null,
69
- },
70
- ];
71
- for (const declaration of program.declarations) refs.push(...symbolsForDeclaration(moduleName, declaration));
72
- return { schemaVersion: "point.core.index.v1", module: moduleName, refs };
73
- }
74
-
75
- export function explainPointCoreRef(program: PointCoreProgram, ref: string): PointCoreExplanation {
76
- const index = createPointCoreIndex(program);
77
- const symbol = index.refs.find((candidate) => candidate.ref === ref);
78
- if (!symbol) {
79
- return {
80
- schemaVersion: "point.core.explain.v1",
81
- ref,
82
- found: false,
83
- relatedRefs: [],
84
- summary: `No Point symbol found for ${ref}.`,
85
- };
86
- }
87
- const relatedRefs = relatedRefsFor(symbol, index);
88
- return {
89
- schemaVersion: "point.core.explain.v1",
90
- ref,
91
- found: true,
92
- symbol,
93
- relatedRefs,
94
- summary: summaryFor(symbol),
95
- };
96
- }
97
-
98
- export function createPointCoreRepairPlan(diagnostics: PointCoreDiagnostic[]): PointCoreRepairPlan {
99
- return {
100
- schemaVersion: "point.core.repair-plan.v1",
101
- ok: diagnostics.length === 0,
102
- steps: diagnostics.map((diagnostic) => ({
103
- ref: diagnostic.ref,
104
- code: diagnostic.code,
105
- message: diagnostic.message,
106
- repair: diagnostic.repair ?? "Inspect this ref and update the Point source.",
107
- expected: diagnostic.expected,
108
- actual: diagnostic.actual,
109
- relatedRefs: diagnostic.relatedRefs ?? [],
110
- })),
111
- };
112
- }
113
-
114
- function symbolsForDeclaration(moduleName: string, declaration: PointCoreDeclaration): PointCoreSymbol[] {
115
- if (declaration.kind === "import") {
116
- return declaration.names.map((name) => ({
117
- ref: refFor(moduleName, `import.${name}`),
118
- path: `import.${name}`,
119
- kind: "import",
120
- name,
121
- module: moduleName,
122
- from: declaration.from,
123
- span: declaration.span ?? null,
124
- }));
125
- }
126
- if (declaration.kind === "value") return [valueSymbol(moduleName, declaration, `value.${declaration.name}`)];
127
- if (declaration.kind === "type") return typeSymbols(moduleName, declaration);
128
- return functionSymbols(moduleName, declaration);
129
- }
130
-
131
- function valueSymbol(moduleName: string, declaration: PointCoreValueDeclaration, path: string): PointCoreSymbol {
132
- return {
133
- ref: refFor(moduleName, path),
134
- path,
135
- kind: "value",
136
- name: declaration.name,
137
- module: moduleName,
138
- type: formatType(declaration.type),
139
- mutable: declaration.mutable,
140
- span: declaration.span ?? null,
141
- };
142
- }
143
-
144
- function typeSymbols(moduleName: string, declaration: PointCoreTypeDeclaration): PointCoreSymbol[] {
145
- return [
146
- {
147
- ref: refFor(moduleName, `type.${declaration.name}`),
148
- path: `type.${declaration.name}`,
149
- kind: "type",
150
- name: declaration.name,
151
- module: moduleName,
152
- span: declaration.span ?? null,
153
- },
154
- ...declaration.fields.map((field) => ({
155
- ref: refFor(moduleName, `type.${declaration.name}.${field.name}`),
156
- path: `type.${declaration.name}.${field.name}`,
157
- kind: "field" as const,
158
- name: field.name,
159
- module: moduleName,
160
- type: formatType(field.type),
161
- span: field.span ?? null,
162
- })),
163
- ];
164
- }
165
-
166
- function functionSymbols(moduleName: string, declaration: PointCoreFunctionDeclaration): PointCoreSymbol[] {
167
- return [
168
- {
169
- ref: refFor(moduleName, `fn.${declaration.name}`),
170
- path: `fn.${declaration.name}`,
171
- kind: "function",
172
- name: declaration.name,
173
- module: moduleName,
174
- type: formatType(declaration.returnType),
175
- span: declaration.span ?? null,
176
- },
177
- ...declaration.params.map((param) => ({
178
- ref: refFor(moduleName, `fn.${declaration.name}.param.${param.name}`),
179
- path: `fn.${declaration.name}.param.${param.name}`,
180
- kind: "param" as const,
181
- name: param.name,
182
- module: moduleName,
183
- type: formatType(param.type),
184
- span: param.span ?? null,
185
- })),
186
- ];
187
- }
188
-
189
- function refFor(moduleName: string, path: string): string {
190
- return `point://core/${moduleName}/${path}`;
191
- }
192
-
193
- function relatedRefsFor(symbol: PointCoreSymbol, index: PointCoreIndex): string[] {
194
- if (symbol.kind === "field") {
195
- const ownerPath = symbol.path.split(".").slice(0, 2).join(".");
196
- return index.refs.filter((candidate) => candidate.path.startsWith(`${ownerPath}.`) && candidate.ref !== symbol.ref).map((candidate) => candidate.ref);
197
- }
198
- if (symbol.kind === "function") {
199
- return index.refs.filter((candidate) => candidate.path.startsWith(`${symbol.path}.param.`)).map((candidate) => candidate.ref);
200
- }
201
- return [];
202
- }
203
-
204
- function summaryFor(symbol: PointCoreSymbol): string {
205
- if (symbol.kind === "module") return `Module ${symbol.name}.`;
206
- if (symbol.kind === "import") return `Import ${symbol.name} from ${symbol.from}.`;
207
- if (symbol.kind === "value") return `${symbol.mutable ? "Mutable" : "Immutable"} value ${symbol.name}: ${symbol.type}.`;
208
- if (symbol.kind === "function") return `Function ${symbol.name} returns ${symbol.type}.`;
209
- if (symbol.kind === "param") return `Parameter ${symbol.name}: ${symbol.type}.`;
210
- if (symbol.kind === "type") return `Named type ${symbol.name}.`;
211
- return `Field ${symbol.name}: ${symbol.type}.`;
212
- }
213
-
214
- function formatType(type: PointCoreTypeExpression): string {
215
- if (type.args.length === 0) return String(type.name);
216
- return `${type.name}<${type.args.map(formatType).join(", ")}>`;
217
- }
1
+ import type {
2
+ PointCoreDeclaration,
3
+ PointCoreFunctionDeclaration,
4
+ PointCoreProgram,
5
+ PointCoreTypeDeclaration,
6
+ PointCoreTypeExpression,
7
+ PointCoreValueDeclaration,
8
+ PointSourceSpan,
9
+ } from "./ast.ts";
10
+ import type {
11
+ PointCoreDiagnostic,
12
+ } from "./check.ts";
13
+
14
+ export type PointCoreSymbolKind =
15
+ | "module"
16
+ | "import"
17
+ | "value"
18
+ | "function"
19
+ | "param"
20
+ | "type"
21
+ | "field"
22
+ | "record"
23
+ | "calculation"
24
+ | "rule"
25
+ | "label"
26
+ | "external"
27
+ | "action"
28
+ | "policy"
29
+ | "view"
30
+ | "route"
31
+ | "workflow"
32
+ | "command";
33
+
34
+ export interface PointCoreSymbol {
35
+ ref: string;
36
+ path: string;
37
+ kind: PointCoreSymbolKind;
38
+ name: string;
39
+ module: string;
40
+ type?: string;
41
+ mutable?: boolean;
42
+ from?: string;
43
+ effects?: string[];
44
+ span: PointSourceSpan | null;
45
+ }
46
+
47
+ export interface PointCoreIndex {
48
+ schemaVersion: "point.core.index.v1";
49
+ module: string;
50
+ refs: PointCoreSymbol[];
51
+ }
52
+
53
+ export interface PointCoreExplanation {
54
+ schemaVersion: "point.core.explain.v1";
55
+ ref: string;
56
+ found: boolean;
57
+ symbol?: PointCoreSymbol;
58
+ relatedRefs: string[];
59
+ summary: string;
60
+ }
61
+
62
+ export interface PointCoreRepairPlan {
63
+ schemaVersion: "point.core.repair-plan.v1";
64
+ ok: boolean;
65
+ steps: PointCoreRepairStep[];
66
+ }
67
+
68
+ export interface PointCoreRepairStep {
69
+ ref: string;
70
+ code: string;
71
+ message: string;
72
+ repair: string;
73
+ expected?: string | string[];
74
+ actual?: string;
75
+ relatedRefs: string[];
76
+ }
77
+
78
+ export function createPointCoreIndex(program: PointCoreProgram): PointCoreIndex {
79
+ const moduleName = program.module ?? "anonymous";
80
+ const refs: PointCoreSymbol[] = [
81
+ {
82
+ ref: refFor(moduleName, "module"),
83
+ path: "module",
84
+ kind: "module",
85
+ name: moduleName,
86
+ module: moduleName,
87
+ span: program.span ?? null,
88
+ },
89
+ ];
90
+ for (const declaration of program.declarations) {
91
+ refs.push(...symbolsForDeclaration(moduleName, declaration));
92
+ refs.push(...semanticSymbolsForDeclaration(moduleName, declaration));
93
+ }
94
+ return { schemaVersion: "point.core.index.v1", module: moduleName, refs };
95
+ }
96
+
97
+ export function explainPointCoreRef(program: PointCoreProgram, ref: string): PointCoreExplanation {
98
+ const index = createPointCoreIndex(program);
99
+ const symbol = index.refs.find((candidate) => candidate.ref === ref);
100
+ if (!symbol) {
101
+ return {
102
+ schemaVersion: "point.core.explain.v1",
103
+ ref,
104
+ found: false,
105
+ relatedRefs: [],
106
+ summary: `No Point symbol found for ${ref}.`,
107
+ };
108
+ }
109
+ const relatedRefs = relatedRefsFor(symbol, index);
110
+ return {
111
+ schemaVersion: "point.core.explain.v1",
112
+ ref,
113
+ found: true,
114
+ symbol,
115
+ relatedRefs,
116
+ summary: summaryFor(symbol),
117
+ };
118
+ }
119
+
120
+ export function createPointCoreRepairPlan(diagnostics: PointCoreDiagnostic[]): PointCoreRepairPlan {
121
+ return {
122
+ schemaVersion: "point.core.repair-plan.v1",
123
+ ok: diagnostics.length === 0,
124
+ steps: diagnostics.map((diagnostic) => ({
125
+ ref: diagnostic.ref,
126
+ code: diagnostic.code,
127
+ message: diagnostic.message,
128
+ repair: diagnostic.repair ?? "Inspect this ref and update the Point source.",
129
+ expected: diagnostic.expected,
130
+ actual: diagnostic.actual,
131
+ relatedRefs: diagnostic.relatedRefs ?? [],
132
+ })),
133
+ };
134
+ }
135
+
136
+ export function mapDiagnosticsToSemanticRefs(program: PointCoreProgram, diagnostics: PointCoreDiagnostic[]): PointCoreDiagnostic[] {
137
+ return diagnostics.map((diagnostic) => ({
138
+ ...diagnostic,
139
+ ref: semanticRefForDiagnosticPath(program, diagnostic.path) ?? diagnostic.ref,
140
+ relatedRefs: diagnostic.relatedRefs?.map((ref) => semanticRefForCoreRef(program, ref) ?? ref),
141
+ }));
142
+ }
143
+
144
+ function symbolsForDeclaration(moduleName: string, declaration: PointCoreDeclaration): PointCoreSymbol[] {
145
+ if (declaration.kind === "import") {
146
+ return declaration.names.map((name) => ({
147
+ ref: refFor(moduleName, `import.${name}`),
148
+ path: `import.${name}`,
149
+ kind: "import",
150
+ name,
151
+ module: moduleName,
152
+ from: declaration.from,
153
+ span: declaration.span ?? null,
154
+ }));
155
+ }
156
+ if (declaration.kind === "external") {
157
+ return [
158
+ {
159
+ ref: refFor(moduleName, `external.${declaration.name}`),
160
+ path: `external.${declaration.name}`,
161
+ kind: "external",
162
+ name: declaration.name,
163
+ module: moduleName,
164
+ type: formatType(declaration.returnType),
165
+ from: declaration.from,
166
+ span: declaration.span ?? null,
167
+ },
168
+ ];
169
+ }
170
+ if (declaration.kind === "value") return [valueSymbol(moduleName, declaration, `value.${declaration.name}`)];
171
+ if (declaration.kind === "type") return typeSymbols(moduleName, declaration);
172
+ return functionSymbols(moduleName, declaration);
173
+ }
174
+
175
+ function valueSymbol(moduleName: string, declaration: PointCoreValueDeclaration, path: string): PointCoreSymbol {
176
+ return {
177
+ ref: refFor(moduleName, path),
178
+ path,
179
+ kind: "value",
180
+ name: declaration.name,
181
+ module: moduleName,
182
+ type: formatType(declaration.type),
183
+ mutable: declaration.mutable,
184
+ span: declaration.span ?? null,
185
+ };
186
+ }
187
+
188
+ function typeSymbols(moduleName: string, declaration: PointCoreTypeDeclaration): PointCoreSymbol[] {
189
+ return [
190
+ {
191
+ ref: refFor(moduleName, `type.${declaration.name}`),
192
+ path: `type.${declaration.name}`,
193
+ kind: "type",
194
+ name: declaration.name,
195
+ module: moduleName,
196
+ span: declaration.span ?? null,
197
+ },
198
+ ...declaration.fields.map((field) => ({
199
+ ref: refFor(moduleName, `type.${declaration.name}.${field.name}`),
200
+ path: `type.${declaration.name}.${field.name}`,
201
+ kind: "field" as const,
202
+ name: field.name,
203
+ module: moduleName,
204
+ type: formatType(field.type),
205
+ span: field.span ?? null,
206
+ })),
207
+ ];
208
+ }
209
+
210
+ function functionSymbols(moduleName: string, declaration: PointCoreFunctionDeclaration): PointCoreSymbol[] {
211
+ return [
212
+ {
213
+ ref: refFor(moduleName, `fn.${declaration.name}`),
214
+ path: `fn.${declaration.name}`,
215
+ kind: "function",
216
+ name: declaration.name,
217
+ module: moduleName,
218
+ type: formatType(declaration.returnType),
219
+ effects: declaration.semantic?.effects,
220
+ span: declaration.span ?? null,
221
+ },
222
+ ...declaration.params.map((param) => ({
223
+ ref: refFor(moduleName, `fn.${declaration.name}.param.${param.name}`),
224
+ path: `fn.${declaration.name}.param.${param.name}`,
225
+ kind: "param" as const,
226
+ name: param.name,
227
+ module: moduleName,
228
+ type: formatType(param.type),
229
+ span: param.span ?? null,
230
+ })),
231
+ ];
232
+ }
233
+
234
+ function semanticSymbolsForDeclaration(moduleName: string, declaration: PointCoreDeclaration): PointCoreSymbol[] {
235
+ if (declaration.kind === "type" && declaration.semantic?.kind === "record") {
236
+ const recordPath = `record.${declaration.semantic.name}`;
237
+ return [
238
+ {
239
+ ref: semanticRefFor(moduleName, recordPath),
240
+ path: recordPath,
241
+ kind: "record",
242
+ name: declaration.semantic.name,
243
+ module: moduleName,
244
+ span: declaration.span ?? null,
245
+ },
246
+ ...declaration.fields.map((field) => {
247
+ const fieldName = field.semanticName ?? field.name;
248
+ const path = `${recordPath}.field.${fieldName}`;
249
+ return {
250
+ ref: semanticRefFor(moduleName, path),
251
+ path,
252
+ kind: "field" as const,
253
+ name: fieldName,
254
+ module: moduleName,
255
+ type: formatType(field.type),
256
+ span: field.span ?? null,
257
+ };
258
+ }),
259
+ ];
260
+ }
261
+ if (declaration.kind === "function" && declaration.semantic) {
262
+ const semanticPath = `${declaration.semantic.kind}.${declaration.semantic.name}`;
263
+ return [
264
+ {
265
+ ref: semanticRefFor(moduleName, semanticPath),
266
+ path: semanticPath,
267
+ kind: declaration.semantic.kind,
268
+ name: declaration.semantic.name,
269
+ module: moduleName,
270
+ type: formatType(declaration.returnType),
271
+ effects: declaration.semantic.effects,
272
+ span: declaration.span ?? null,
273
+ },
274
+ ...declaration.params.map((param) => {
275
+ const paramName = param.semanticName ?? param.name;
276
+ const path = `${semanticPath}.input.${paramName}`;
277
+ return {
278
+ ref: semanticRefFor(moduleName, path),
279
+ path,
280
+ kind: "param" as const,
281
+ name: paramName,
282
+ module: moduleName,
283
+ type: formatType(param.type),
284
+ span: param.span ?? null,
285
+ };
286
+ }),
287
+ ];
288
+ }
289
+ if (declaration.kind === "external" && declaration.semantic) {
290
+ const semanticPath = `external.${declaration.semantic.name}`;
291
+ return [
292
+ {
293
+ ref: semanticRefFor(moduleName, semanticPath),
294
+ path: semanticPath,
295
+ kind: "external",
296
+ name: declaration.semantic.name,
297
+ module: moduleName,
298
+ type: formatType(declaration.returnType),
299
+ from: declaration.from,
300
+ span: declaration.span ?? null,
301
+ },
302
+ ];
303
+ }
304
+ return [];
305
+ }
306
+
307
+ function refFor(moduleName: string, path: string): string {
308
+ return `point://core/${moduleName}/${path}`;
309
+ }
310
+
311
+ function semanticRefFor(moduleName: string, path: string): string {
312
+ return `point://semantic/${moduleName}/${path}`;
313
+ }
314
+
315
+ function relatedRefsFor(symbol: PointCoreSymbol, index: PointCoreIndex): string[] {
316
+ if (symbol.kind === "field") {
317
+ const ownerPath = symbol.path.split(".").slice(0, 2).join(".");
318
+ return index.refs.filter((candidate) => candidate.path.startsWith(`${ownerPath}.`) && candidate.ref !== symbol.ref).map((candidate) => candidate.ref);
319
+ }
320
+ if (
321
+ symbol.kind === "function" ||
322
+ symbol.kind === "calculation" ||
323
+ symbol.kind === "rule" ||
324
+ symbol.kind === "label" ||
325
+ symbol.kind === "action" ||
326
+ symbol.kind === "policy" ||
327
+ symbol.kind === "view" ||
328
+ symbol.kind === "route" ||
329
+ symbol.kind === "workflow" ||
330
+ symbol.kind === "command"
331
+ ) {
332
+ return index.refs
333
+ .filter((candidate) => candidate.path.startsWith(`${symbol.path}.param.`) || candidate.path.startsWith(`${symbol.path}.input.`))
334
+ .map((candidate) => candidate.ref);
335
+ }
336
+ return [];
337
+ }
338
+
339
+ function summaryFor(symbol: PointCoreSymbol): string {
340
+ if (symbol.kind === "module") return `Module ${symbol.name}.`;
341
+ if (symbol.kind === "import") return `Import ${symbol.name} from ${symbol.from}.`;
342
+ if (symbol.kind === "value") return `${symbol.mutable ? "Mutable" : "Immutable"} value ${symbol.name}: ${symbol.type}.`;
343
+ if (symbol.kind === "function") return `Function ${symbol.name} returns ${symbol.type}.`;
344
+ if (symbol.kind === "external") return `External function ${symbol.name} from ${symbol.from} returns ${symbol.type}.`;
345
+ if (symbol.kind === "param") return `Parameter ${symbol.name}: ${symbol.type}.`;
346
+ if (symbol.kind === "type") return `Named type ${symbol.name}.`;
347
+ if (symbol.kind === "record") return `Semantic record ${symbol.name}.`;
348
+ if (symbol.kind === "calculation") return `Semantic calculation ${symbol.name} returns ${symbol.type}.`;
349
+ if (symbol.kind === "rule") return `Semantic rule ${symbol.name} returns ${symbol.type}.`;
350
+ if (symbol.kind === "label") return `Semantic label ${symbol.name} returns ${symbol.type}.`;
351
+ if (symbol.kind === "action") return `Semantic action ${symbol.name} returns ${symbol.type}; effects: ${(symbol.effects ?? []).join(", ") || "none"}.`;
352
+ if (symbol.kind === "policy") return `Semantic policy ${symbol.name} returns ${symbol.type}.`;
353
+ if (symbol.kind === "view") return `Semantic view ${symbol.name} returns React JSX.`;
354
+ if (symbol.kind === "route") return `Semantic route ${symbol.name} returns ${symbol.type}.`;
355
+ if (symbol.kind === "workflow") return `Semantic workflow ${symbol.name} returns ${symbol.type}.`;
356
+ if (symbol.kind === "command") return `Semantic command ${symbol.name} returns ${symbol.type}.`;
357
+ return `Field ${symbol.name}: ${symbol.type}.`;
358
+ }
359
+
360
+ function semanticRefForCoreRef(program: PointCoreProgram, ref: string): string | null {
361
+ const prefix = `point://core/${program.module ?? "anonymous"}/`;
362
+ if (!ref.startsWith(prefix)) return null;
363
+ return semanticRefForDiagnosticPath(program, ref.slice(prefix.length));
364
+ }
365
+
366
+ function semanticRefForDiagnosticPath(program: PointCoreProgram, path: string): string | null {
367
+ const moduleName = program.module ?? "anonymous";
368
+ for (const declaration of program.declarations) {
369
+ if (declaration.kind === "type" && declaration.semantic?.kind === "record") {
370
+ const recordPath = `type.${declaration.name}`;
371
+ if (path === recordPath || path.startsWith(`${recordPath}.`)) {
372
+ const parts = path.split(".");
373
+ const field = parts.length >= 3 ? declaration.fields.find((candidate) => candidate.name === parts[2]) : undefined;
374
+ const semanticPath = field
375
+ ? `record.${declaration.semantic.name}.field.${field.semanticName ?? field.name}`
376
+ : `record.${declaration.semantic.name}`;
377
+ return semanticRefFor(moduleName, semanticPath);
378
+ }
379
+ }
380
+ if ((declaration.kind === "function" || declaration.kind === "external") && declaration.semantic) {
381
+ const fnPath = `fn.${declaration.name}`;
382
+ if (path === fnPath || path.startsWith(`${fnPath}.`)) {
383
+ const semanticPath = `${declaration.semantic.kind}.${declaration.semantic.name}`;
384
+ return semanticRefFor(moduleName, semanticPath);
385
+ }
386
+ }
387
+ }
388
+ return null;
389
+ }
390
+
391
+ function formatType(type: PointCoreTypeExpression): string {
392
+ if (type.args.length === 0) return String(type.name);
393
+ return `${type.name}<${type.args.map(formatType).join(", ")}>`;
394
+ }