typespun-codegen 0.0.4 → 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/README.md +170 -47
  2. package/package.json +2 -2
  3. package/dist/analyzer/analyze.d.ts +0 -4
  4. package/dist/analyzer/analyze.d.ts.map +0 -1
  5. package/dist/analyzer/analyze.js +0 -414
  6. package/dist/analyzer/annotations.d.ts +0 -14
  7. package/dist/analyzer/annotations.d.ts.map +0 -1
  8. package/dist/analyzer/annotations.js +0 -102
  9. package/dist/analyzer/default-expression.d.ts +0 -10
  10. package/dist/analyzer/default-expression.d.ts.map +0 -1
  11. package/dist/analyzer/default-expression.js +0 -67
  12. package/dist/analyzer/diagnostic.d.ts +0 -5
  13. package/dist/analyzer/diagnostic.d.ts.map +0 -1
  14. package/dist/analyzer/diagnostic.js +0 -16
  15. package/dist/analyzer/ir.d.ts +0 -2
  16. package/dist/analyzer/ir.d.ts.map +0 -1
  17. package/dist/analyzer/ir.js +0 -1
  18. package/dist/bin.d.ts +0 -3
  19. package/dist/bin.d.ts.map +0 -1
  20. package/dist/bin.js +0 -3
  21. package/dist/cli/diagnostics.d.ts +0 -7
  22. package/dist/cli/diagnostics.d.ts.map +0 -1
  23. package/dist/cli/diagnostics.js +0 -26
  24. package/dist/cli/init.d.ts +0 -18
  25. package/dist/cli/init.d.ts.map +0 -1
  26. package/dist/cli/init.js +0 -417
  27. package/dist/cli/main.d.ts +0 -13
  28. package/dist/cli/main.d.ts.map +0 -1
  29. package/dist/cli/main.js +0 -157
  30. package/dist/contracts.d.ts +0 -33
  31. package/dist/contracts.d.ts.map +0 -1
  32. package/dist/contracts.js +0 -1
  33. package/dist/emitter/emit.d.ts +0 -13
  34. package/dist/emitter/emit.d.ts.map +0 -1
  35. package/dist/emitter/emit.js +0 -65
  36. package/dist/emitter/fingerprint.d.ts +0 -10
  37. package/dist/emitter/fingerprint.d.ts.map +0 -1
  38. package/dist/emitter/fingerprint.js +0 -28
  39. package/dist/generate.d.ts +0 -29
  40. package/dist/generate.d.ts.map +0 -1
  41. package/dist/generate.js +0 -267
  42. package/dist/index.d.ts +0 -3
  43. package/dist/index.d.ts.map +0 -1
  44. package/dist/index.js +0 -2
  45. package/dist/project/config.d.ts +0 -20
  46. package/dist/project/config.d.ts.map +0 -1
  47. package/dist/project/config.js +0 -140
  48. package/dist/project/defaults.d.ts +0 -22
  49. package/dist/project/defaults.d.ts.map +0 -1
  50. package/dist/project/defaults.js +0 -216
  51. package/dist/project/discovery.d.ts +0 -9
  52. package/dist/project/discovery.d.ts.map +0 -1
  53. package/dist/project/discovery.js +0 -66
@@ -1,216 +0,0 @@
1
- import { extname } from 'node:path';
2
- import { parseDocument } from 'yaml';
3
- import { validateTypedValue } from 'typespun/generated';
4
- const DANGEROUS_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
5
- const ALIAS_LIMIT = 20;
6
- export function compileDefaults(document, fields, policies) {
7
- const warnings = [];
8
- const errors = [];
9
- const values = {};
10
- const compiledPaths = new Set();
11
- const fieldByDefaultsPath = new Map(fields.map((field) => [toPathKey(field.defaultsPath), field]));
12
- const parsed = parseDefaultsDocument(document, errors);
13
- if (!isRecord(parsed)) {
14
- if (parsed !== undefined) {
15
- errors.push(diagnostic('invalid_defaults_document', [], document.path, 'Defaults document must contain an object at its root'));
16
- }
17
- applyInlineDefaults(fields, compiledPaths, policies, values, warnings, errors);
18
- return { values, warnings, errors };
19
- }
20
- collectUnsafeKeys(parsed, [], document.path, errors, new Set());
21
- walkDefaults(parsed, [], fieldByDefaultsPath, fields, policies, document.path, values, warnings, errors, compiledPaths);
22
- applyInlineDefaults(fields, compiledPaths, policies, values, warnings, errors);
23
- return { values, warnings, errors };
24
- }
25
- function parseDefaultsDocument(document, errors) {
26
- if (extname(document.path) === '.json') {
27
- try {
28
- return JSON.parse(document.content);
29
- }
30
- catch {
31
- errors.push(diagnostic('invalid_defaults_document', [], document.path, 'Defaults document is not valid JSON'));
32
- return undefined;
33
- }
34
- }
35
- try {
36
- const parsed = parseDocument(document.content, {
37
- customTags: [],
38
- merge: false,
39
- prettyErrors: false,
40
- resolveKnownTags: false,
41
- schema: 'core',
42
- stringKeys: true,
43
- uniqueKeys: true,
44
- });
45
- if (parsed.errors.length > 0 || parsed.warnings.length > 0) {
46
- errors.push(diagnostic('invalid_defaults_document', [], document.path, 'Defaults document is not valid YAML'));
47
- return undefined;
48
- }
49
- return parsed.toJS({ maxAliasCount: ALIAS_LIMIT });
50
- }
51
- catch {
52
- errors.push(diagnostic('invalid_defaults_document', [], document.path, 'Defaults document is not valid YAML'));
53
- return undefined;
54
- }
55
- }
56
- function walkDefaults(value, path, fieldByDefaultsPath, fields, policies, file, values, warnings, errors, compiledPaths) {
57
- for (const [key, child] of Object.entries(value)) {
58
- const childPath = [...path, key];
59
- if (DANGEROUS_KEYS.has(key)) {
60
- continue;
61
- }
62
- const field = fieldByDefaultsPath.get(toPathKey(childPath));
63
- if (field !== undefined) {
64
- compileField(field, child, childPath, policies, file, values, warnings, errors, compiledPaths);
65
- continue;
66
- }
67
- if (hasDefaultsDescendant(fields, childPath)) {
68
- if (!isRecord(child)) {
69
- errors.push(diagnostic('invalid_default_value', childPath, file, 'Expected an object for nested defaults'));
70
- continue;
71
- }
72
- walkDefaults(child, childPath, fieldByDefaultsPath, fields, policies, file, values, warnings, errors, compiledPaths);
73
- continue;
74
- }
75
- reportUnknownPath(childPath, policies.unknownKeys, file, warnings, errors);
76
- }
77
- }
78
- function collectUnsafeKeys(value, path, file, errors, ancestors) {
79
- if (!Array.isArray(value) && !isRecord(value)) {
80
- return;
81
- }
82
- if (ancestors.has(value)) {
83
- errors.push(diagnostic('invalid_defaults_document', path, file, 'Defaults document contains a recursive alias'));
84
- return;
85
- }
86
- ancestors.add(value);
87
- if (Array.isArray(value)) {
88
- for (const [index, child] of value.entries()) {
89
- collectUnsafeKeys(child, [...path, String(index)], file, errors, ancestors);
90
- }
91
- }
92
- else {
93
- for (const [key, child] of Object.entries(value)) {
94
- const childPath = [...path, key];
95
- if (DANGEROUS_KEYS.has(key)) {
96
- errors.push(diagnostic('unsafe_defaults_key', childPath, file, 'Defaults path contains an unsafe key'));
97
- }
98
- collectUnsafeKeys(child, childPath, file, errors, ancestors);
99
- }
100
- }
101
- ancestors.delete(value);
102
- }
103
- function compileField(field, value, defaultsPath, policies, file, values, warnings, errors, compiledPaths) {
104
- compiledPaths.add(toPathKey(field.propertyPath));
105
- const validationMessage = validateTypedValue(field.kind, value);
106
- if (validationMessage !== undefined) {
107
- errors.push(diagnostic('invalid_default_value', defaultsPath, file, field.secret
108
- ? 'Secret default does not match its declared field type'
109
- : validationMessage));
110
- return;
111
- }
112
- if (field.secret) {
113
- if (policies.secretDefaults === 'error') {
114
- errors.push(diagnostic('secret_default', defaultsPath, file, 'Secret fields cannot have compiled defaults'));
115
- return;
116
- }
117
- if (policies.secretDefaults === 'warn') {
118
- warnings.push(diagnostic('secret_default', defaultsPath, file, 'Secret field has a compiled default'));
119
- }
120
- }
121
- if (!setValue(values, field.propertyPath, cloneValue(value))) {
122
- errors.push(diagnostic('unsafe_defaults_key', defaultsPath, file, 'Defaults path contains an unsafe key'));
123
- }
124
- }
125
- function applyInlineDefaults(fields, compiledPaths, policies, values, warnings, errors) {
126
- for (const field of fields) {
127
- if (!field.hasDefault ||
128
- field.defaultValue === undefined ||
129
- compiledPaths.has(toPathKey(field.propertyPath))) {
130
- continue;
131
- }
132
- if (field.secret) {
133
- if (policies.secretDefaults === 'error') {
134
- errors.push(diagnostic('secret_default', field.defaultsPath, field.location.file, 'Secret fields cannot have inline defaults'));
135
- continue;
136
- }
137
- if (policies.secretDefaults === 'warn') {
138
- warnings.push(diagnostic('secret_default', field.defaultsPath, field.location.file, 'Secret field has an inline default'));
139
- }
140
- }
141
- if (!setValue(values, field.propertyPath, cloneValue(field.defaultValue))) {
142
- errors.push(diagnostic('unsafe_defaults_key', field.defaultsPath, field.location.file, 'Defaults path contains an unsafe key'));
143
- }
144
- }
145
- }
146
- function reportUnknownPath(path, policy, file, warnings, errors) {
147
- if (policy === 'ignore') {
148
- return;
149
- }
150
- const target = policy === 'warn' ? warnings : errors;
151
- target.push(diagnostic('unknown_defaults_key', path, file, 'Defaults path does not match a declared field'));
152
- }
153
- function hasDefaultsDescendant(fields, path) {
154
- return fields.some((field) => field.defaultsPath.length > path.length &&
155
- path.every((part, index) => field.defaultsPath[index] === part));
156
- }
157
- function setValue(target, path, value) {
158
- if (path.length === 0 || path.some((part) => DANGEROUS_KEYS.has(part))) {
159
- return false;
160
- }
161
- let current = target;
162
- for (const [index, part] of path.entries()) {
163
- if (index === path.length - 1) {
164
- Object.defineProperty(current, part, {
165
- configurable: true,
166
- enumerable: true,
167
- value,
168
- writable: true,
169
- });
170
- return true;
171
- }
172
- const existing = current[part];
173
- if (isRecord(existing)) {
174
- current = existing;
175
- continue;
176
- }
177
- const next = {};
178
- Object.defineProperty(current, part, {
179
- configurable: true,
180
- enumerable: true,
181
- value: next,
182
- writable: true,
183
- });
184
- current = next;
185
- }
186
- return false;
187
- }
188
- function cloneValue(value) {
189
- if (Array.isArray(value)) {
190
- return value.map((item) => cloneValue(item));
191
- }
192
- if (isRecord(value)) {
193
- const cloned = {};
194
- for (const [key, child] of Object.entries(value)) {
195
- if (!DANGEROUS_KEYS.has(key)) {
196
- Object.defineProperty(cloned, key, {
197
- configurable: true,
198
- enumerable: true,
199
- value: cloneValue(child),
200
- writable: true,
201
- });
202
- }
203
- }
204
- return cloned;
205
- }
206
- return value;
207
- }
208
- function diagnostic(code, path, file, message) {
209
- return { code, path: path.join('.'), file, message };
210
- }
211
- function toPathKey(path) {
212
- return JSON.stringify(path);
213
- }
214
- function isRecord(value) {
215
- return typeof value === 'object' && value !== null && !Array.isArray(value);
216
- }
@@ -1,9 +0,0 @@
1
- export declare const CONVENTIONAL_INPUTS: readonly ["src/config.ts", "src/config.mts", "src/config.cts"];
2
- export declare const CONVENTIONAL_DEFAULTS: readonly ["config.yaml", "config.yml", "config.json", "config/config.yaml", "config/config.yml", "config/config.json", "src/config.yaml", "src/config.yml", "src/config.json"];
3
- export declare class ProjectDiscoveryError extends Error {
4
- constructor(message: string);
5
- }
6
- export declare function discoverInputPath(projectDirectory: string): string;
7
- export declare function discoverDefaultsPath(projectDirectory: string): string | undefined;
8
- export declare function findNearestTsconfig(inputPath: string): string | undefined;
9
- //# sourceMappingURL=discovery.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../../src/project/discovery.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,mBAAmB,gEAItB,CAAC;AAEX,eAAO,MAAM,qBAAqB,gLAUxB,CAAC;AAEX,qBAAa,qBAAsB,SAAQ,KAAK;gBAClC,OAAO,EAAE,MAAM;CAI5B;AAED,wBAAgB,iBAAiB,CAAC,gBAAgB,EAAE,MAAM,GAAG,MAAM,CAalE;AAED,wBAAgB,oBAAoB,CAClC,gBAAgB,EAAE,MAAM,GACvB,MAAM,GAAG,SAAS,CAapB;AAED,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAezE"}
@@ -1,66 +0,0 @@
1
- import { existsSync, statSync } from 'node:fs';
2
- import { dirname, join, resolve } from 'node:path';
3
- export const CONVENTIONAL_INPUTS = [
4
- 'src/config.ts',
5
- 'src/config.mts',
6
- 'src/config.cts',
7
- ];
8
- export const CONVENTIONAL_DEFAULTS = [
9
- 'config.yaml',
10
- 'config.yml',
11
- 'config.json',
12
- 'config/config.yaml',
13
- 'config/config.yml',
14
- 'config/config.json',
15
- 'src/config.yaml',
16
- 'src/config.yml',
17
- 'src/config.json',
18
- ];
19
- export class ProjectDiscoveryError extends Error {
20
- constructor(message) {
21
- super(message);
22
- this.name = 'ProjectDiscoveryError';
23
- }
24
- }
25
- export function discoverInputPath(projectDirectory) {
26
- const candidates = findExistingCandidates(projectDirectory, CONVENTIONAL_INPUTS);
27
- if (candidates.length !== 1) {
28
- throw new ProjectDiscoveryError(`Expected exactly one conventional input, found ${candidates.length}.`);
29
- }
30
- return candidates[0];
31
- }
32
- export function discoverDefaultsPath(projectDirectory) {
33
- const candidates = findExistingCandidates(projectDirectory, CONVENTIONAL_DEFAULTS);
34
- if (candidates.length > 1) {
35
- throw new ProjectDiscoveryError(`Multiple conventional defaults files found: ${candidates.join(', ')}. Configure defaults.path explicitly.`);
36
- }
37
- return candidates[0];
38
- }
39
- export function findNearestTsconfig(inputPath) {
40
- let directory = dirname(resolve(inputPath));
41
- while (true) {
42
- const candidate = join(directory, 'tsconfig.json');
43
- if (isFile(candidate)) {
44
- return candidate;
45
- }
46
- const parent = dirname(directory);
47
- if (parent === directory) {
48
- return undefined;
49
- }
50
- directory = parent;
51
- }
52
- }
53
- function findExistingCandidates(projectDirectory, candidates) {
54
- const root = resolve(projectDirectory);
55
- return candidates
56
- .map((candidate) => join(root, candidate))
57
- .filter((candidate) => isFile(candidate));
58
- }
59
- function isFile(path) {
60
- try {
61
- return existsSync(path) && statSync(path).isFile();
62
- }
63
- catch {
64
- return false;
65
- }
66
- }