rastack 0.0.16

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 (140) hide show
  1. package/CHANGELOG.md +43013 -0
  2. package/dist/.prettierrc +7 -0
  3. package/dist/compile/analyze.d.ts +12 -0
  4. package/dist/compile/analyze.js +233 -0
  5. package/dist/compile/index.d.ts +19 -0
  6. package/dist/compile/index.js +107 -0
  7. package/dist/compile/manifest.d.ts +4 -0
  8. package/dist/compile/manifest.js +12 -0
  9. package/dist/compile/model.d.ts +54 -0
  10. package/dist/compile/model.js +7 -0
  11. package/dist/compile/openapi.d.ts +11 -0
  12. package/dist/compile/openapi.js +233 -0
  13. package/dist/compile/program.d.ts +10 -0
  14. package/dist/compile/program.js +61 -0
  15. package/dist/csv-schema.d.ts +28 -0
  16. package/dist/csv-schema.js +162 -0
  17. package/dist/define/index.d.ts +117 -0
  18. package/dist/define/index.js +74 -0
  19. package/dist/entity-generation/delete-method.d.ts +1 -0
  20. package/dist/entity-generation/delete-method.js +31 -0
  21. package/dist/entity-generation/form.d.ts +13 -0
  22. package/dist/entity-generation/form.js +136 -0
  23. package/dist/entity-generation/get-method.d.ts +1 -0
  24. package/dist/entity-generation/get-method.js +67 -0
  25. package/dist/entity-generation/imports.d.ts +5 -0
  26. package/dist/entity-generation/imports.js +91 -0
  27. package/dist/entity-generation/sync-method.d.ts +30 -0
  28. package/dist/entity-generation/sync-method.js +158 -0
  29. package/dist/entity-generation/update-method.d.ts +1 -0
  30. package/dist/entity-generation/update-method.js +29 -0
  31. package/dist/index.d.ts +2 -0
  32. package/dist/index.js +53 -0
  33. package/dist/rad-compile.d.ts +11 -0
  34. package/dist/rad-compile.js +82 -0
  35. package/dist/rad-wasm-build.d.ts +14 -0
  36. package/dist/rad-wasm-build.js +74 -0
  37. package/dist/rad.d.ts +17 -0
  38. package/dist/rad.js +88 -0
  39. package/dist/scan.d.ts +12 -0
  40. package/dist/scan.js +79 -0
  41. package/dist/schema/camel-to-pastel.d.ts +1 -0
  42. package/dist/schema/camel-to-pastel.js +7 -0
  43. package/dist/schema/capitalise-first-letter.d.ts +1 -0
  44. package/dist/schema/capitalise-first-letter.js +7 -0
  45. package/dist/schema/extract-response.d.ts +2 -0
  46. package/dist/schema/extract-response.js +30 -0
  47. package/dist/schema/fetch-schema.d.ts +7 -0
  48. package/dist/schema/fetch-schema.js +23 -0
  49. package/dist/schema/remove-non-model-paths.d.ts +2 -0
  50. package/dist/schema/remove-non-model-paths.js +15 -0
  51. package/dist/schema/to-camel-case.d.ts +1 -0
  52. package/dist/schema/to-camel-case.js +11 -0
  53. package/dist/schema/to-pastel-case.d.ts +1 -0
  54. package/dist/schema/to-pastel-case.js +10 -0
  55. package/dist/schema-convert.d.ts +1 -0
  56. package/dist/schema-convert.js +63 -0
  57. package/dist/schema-entities.d.ts +14 -0
  58. package/dist/schema-entities.js +293 -0
  59. package/dist/schema-fetch.d.ts +1 -0
  60. package/dist/schema-fetch.js +81 -0
  61. package/dist/schema-full.d.ts +1 -0
  62. package/dist/schema-full.js +45 -0
  63. package/dist/schema-index.d.ts +1 -0
  64. package/dist/schema-index.js +34 -0
  65. package/dist/schema-params.d.ts +1 -0
  66. package/dist/schema-params.js +100 -0
  67. package/dist/seed.d.ts +17 -0
  68. package/dist/seed.js +252 -0
  69. package/hooks/form/form.ts +207 -0
  70. package/hooks/form/index.ts +8 -0
  71. package/hooks/form/interfaces.ts +217 -0
  72. package/hooks/form/structure.ts +39 -0
  73. package/hooks/form/validate-schema.ts +49 -0
  74. package/hooks/index.ts +3 -0
  75. package/hooks/query/api.ts +42 -0
  76. package/hooks/query/delete.ts +45 -0
  77. package/hooks/query/fetch.ts +48 -0
  78. package/hooks/query/index.ts +21 -0
  79. package/hooks/query/interfaces.ts +111 -0
  80. package/hooks/query/list.ts +286 -0
  81. package/hooks/query/update.ts +88 -0
  82. package/hooks/query/url.ts +31 -0
  83. package/hooks/real-time/index.ts +1 -0
  84. package/hooks/real-time/pusher.ts +43 -0
  85. package/jest.config.cjs +6 -0
  86. package/package.json +57 -0
  87. package/provider/index.ts +13 -0
  88. package/provider/provider.tsx +187 -0
  89. package/provider/types.ts +114 -0
  90. package/provider/warehouse.ts +167 -0
  91. package/provider/wasm.ts +124 -0
  92. package/runtime.ts +3 -0
  93. package/src/.prettierrc +7 -0
  94. package/src/compile/analyze.ts +224 -0
  95. package/src/compile/index.ts +86 -0
  96. package/src/compile/manifest.ts +10 -0
  97. package/src/compile/model.ts +69 -0
  98. package/src/compile/openapi.ts +266 -0
  99. package/src/compile/program.ts +40 -0
  100. package/src/csv-schema.ts +187 -0
  101. package/src/define/index.ts +162 -0
  102. package/src/entity-generation/delete-method.ts +37 -0
  103. package/src/entity-generation/form.ts +169 -0
  104. package/src/entity-generation/get-method.ts +80 -0
  105. package/src/entity-generation/imports.ts +110 -0
  106. package/src/entity-generation/sync-method.ts +222 -0
  107. package/src/entity-generation/update-method.ts +38 -0
  108. package/src/index.ts +77 -0
  109. package/src/rad-compile.ts +88 -0
  110. package/src/rad-wasm-build.ts +102 -0
  111. package/src/rad.ts +101 -0
  112. package/src/scan.ts +89 -0
  113. package/src/schema/camel-to-pastel.ts +3 -0
  114. package/src/schema/capitalise-first-letter.ts +3 -0
  115. package/src/schema/extract-response.ts +38 -0
  116. package/src/schema/fetch-schema.ts +12 -0
  117. package/src/schema/remove-non-model-paths.ts +13 -0
  118. package/src/schema/to-camel-case.ts +7 -0
  119. package/src/schema/to-pastel-case.ts +6 -0
  120. package/src/schema-convert.ts +61 -0
  121. package/src/schema-entities.ts +401 -0
  122. package/src/schema-fetch.ts +82 -0
  123. package/src/schema-full.ts +38 -0
  124. package/src/schema-index.ts +29 -0
  125. package/src/schema-params.ts +116 -0
  126. package/src/seed.ts +297 -0
  127. package/sync/engine.ts +392 -0
  128. package/sync/hooks.ts +450 -0
  129. package/sync/index.ts +8 -0
  130. package/sync/persistence.ts +237 -0
  131. package/sync/provider.tsx +91 -0
  132. package/sync/registry.ts +36 -0
  133. package/sync/store.ts +126 -0
  134. package/sync/transactions.ts +300 -0
  135. package/sync/types.ts +94 -0
  136. package/test/compile.spec.ts +192 -0
  137. package/test/csv-schema.spec.ts +143 -0
  138. package/test/schema-entities.spec.ts +688 -0
  139. package/tsconfig.json +24 -0
  140. package/types.ts +19 -0
@@ -0,0 +1,7 @@
1
+ {
2
+ "tabWidth": 2,
3
+ "semi": true,
4
+ "bracketSpacing": true,
5
+ "arrowParens": "always",
6
+ "printWidth": 80
7
+ }
@@ -0,0 +1,12 @@
1
+ import * as ts from "typescript";
2
+ import { ResourceModel } from "./model";
3
+ /**
4
+ * Statically analyse resource definitions and produce the canonical model.
5
+ *
6
+ * The headline behaviour is **foreign-key inference from types**: for every
7
+ * field we ask the `TypeChecker` for the field's type and, if that type is a
8
+ * `Ref<App, Model>`, we recover the relation target from the `__app`/`__model`
9
+ * string-literal type arguments. No runtime execution, no string heuristics on
10
+ * the value — the relation is read straight out of the type system.
11
+ */
12
+ export declare function analyze(program: ts.Program, fileNames: string[]): ResourceModel[];
@@ -0,0 +1,233 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.analyze = void 0;
27
+ const ts = __importStar(require("typescript"));
28
+ const program_1 = require("./program");
29
+ /**
30
+ * Statically analyse resource definitions and produce the canonical model.
31
+ *
32
+ * The headline behaviour is **foreign-key inference from types**: for every
33
+ * field we ask the `TypeChecker` for the field's type and, if that type is a
34
+ * `Ref<App, Model>`, we recover the relation target from the `__app`/`__model`
35
+ * string-literal type arguments. No runtime execution, no string heuristics on
36
+ * the value — the relation is read straight out of the type system.
37
+ */
38
+ function analyze(program, fileNames) {
39
+ const checker = program.getTypeChecker();
40
+ const resources = [];
41
+ for (const sourceFile of (0, program_1.resourceSourceFiles)(program, fileNames)) {
42
+ ts.forEachChild(sourceFile, function walk(node) {
43
+ if (isResourceCall(node)) {
44
+ const model = analyzeResourceCall(node, checker);
45
+ if (model)
46
+ resources.push(model);
47
+ }
48
+ ts.forEachChild(node, walk);
49
+ });
50
+ }
51
+ // Deterministic order: app, then model.
52
+ resources.sort((a, b) => a.app === b.app ? a.model.localeCompare(b.model) : a.app.localeCompare(b.app));
53
+ return resources;
54
+ }
55
+ exports.analyze = analyze;
56
+ function isResourceCall(node) {
57
+ return (ts.isCallExpression(node) &&
58
+ ts.isIdentifier(node.expression) &&
59
+ node.expression.text === "resource" &&
60
+ node.arguments.length >= 3);
61
+ }
62
+ function analyzeResourceCall(call, checker) {
63
+ var _a;
64
+ const app = stringLiteralArg(call.arguments[0]);
65
+ const model = stringLiteralArg(call.arguments[1]);
66
+ const fieldsArg = call.arguments[2];
67
+ if (!app || !model || !fieldsArg || !ts.isObjectLiteralExpression(fieldsArg)) {
68
+ return undefined;
69
+ }
70
+ const fields = [];
71
+ const relations = [];
72
+ for (const prop of fieldsArg.properties) {
73
+ if (!ts.isPropertyAssignment(prop))
74
+ continue;
75
+ const name = propertyName(prop.name);
76
+ if (!name)
77
+ continue;
78
+ const field = analyzeField(name, prop.initializer, checker);
79
+ if (!field)
80
+ continue;
81
+ fields.push(field);
82
+ if (field.type === "fk" && field.relation) {
83
+ relations.push({
84
+ field: name,
85
+ target: `${field.relation.app}.${field.relation.model}`,
86
+ kind: "fk",
87
+ });
88
+ }
89
+ }
90
+ const options = (_a = literalToValue(call.arguments[3])) !== null && _a !== void 0 ? _a : {};
91
+ return {
92
+ app,
93
+ model,
94
+ fields,
95
+ relations,
96
+ sync: normaliseSync(options.sync),
97
+ admin: normaliseAdmin(options.admin),
98
+ search: options.search,
99
+ ordering: options.ordering,
100
+ permission: options.permission,
101
+ };
102
+ }
103
+ /**
104
+ * Resolve a single field from its initializer. FK detection is type-driven:
105
+ * the initializer's *type* is inspected, and a `Ref` type yields a foreign key.
106
+ */
107
+ function analyzeField(name, initializer, checker) {
108
+ const fieldType = checker.getTypeAtLocation(initializer);
109
+ // Foreign key: the field type is Ref<App, Model>. Recover the target from
110
+ // the string-literal type arguments — this is the "infer FK from types" step.
111
+ if (fieldType.getProperty("__radRef")) {
112
+ const app = literalTypeProp(fieldType, "__app", checker, initializer);
113
+ const model = literalTypeProp(fieldType, "__model", checker, initializer);
114
+ if (app && model) {
115
+ return { name, type: "fk", relation: { app, model } };
116
+ }
117
+ return undefined;
118
+ }
119
+ // Scalar: the field type carries a `__radScalar` string-literal discriminant.
120
+ const scalar = literalTypeProp(fieldType, "__radScalar", checker, initializer);
121
+ if (!scalar)
122
+ return undefined;
123
+ const field = { name, type: scalar };
124
+ applyConstraints(field, initializer);
125
+ return field;
126
+ }
127
+ /** Read scalar constraints (maxLength, unique, …) from the builder call args. */
128
+ function applyConstraints(field, initializer) {
129
+ if (!ts.isCallExpression(initializer))
130
+ return;
131
+ const opts = literalToValue(initializer.arguments[0]);
132
+ if (!opts || typeof opts !== "object")
133
+ return;
134
+ if (typeof opts.maxLength === "number")
135
+ field.maxLength = opts.maxLength;
136
+ if (opts.unique === true)
137
+ field.unique = true;
138
+ if (opts.primaryKey === true)
139
+ field.primaryKey = true;
140
+ if (opts.null === true)
141
+ field.null = true;
142
+ if (opts.default !== undefined)
143
+ field.default = opts.default;
144
+ }
145
+ // -- type/AST helpers -------------------------------------------------------
146
+ /** Read a string-literal-typed property (e.g. `__app: "airports"`) off a type. */
147
+ function literalTypeProp(type, name, checker, location) {
148
+ const symbol = type.getProperty(name);
149
+ if (!symbol)
150
+ return undefined;
151
+ const propType = checker.getTypeOfSymbolAtLocation(symbol, location);
152
+ if (propType.isStringLiteral())
153
+ return propType.value;
154
+ return undefined;
155
+ }
156
+ function stringLiteralArg(node) {
157
+ if (node && ts.isStringLiteralLike(node))
158
+ return node.text;
159
+ return undefined;
160
+ }
161
+ function propertyName(name) {
162
+ if (ts.isIdentifier(name) || ts.isStringLiteralLike(name))
163
+ return name.text;
164
+ return undefined;
165
+ }
166
+ /**
167
+ * Evaluate a *static literal* AST node (string/number/bool/null/array/object)
168
+ * into a JS value. Anything non-literal (a function, an identifier) yields
169
+ * `undefined` — the options and constraint blocks are always literals.
170
+ */
171
+ function literalToValue(node) {
172
+ if (!node)
173
+ return undefined;
174
+ if (ts.isStringLiteralLike(node))
175
+ return node.text;
176
+ if (ts.isNumericLiteral(node))
177
+ return Number(node.text);
178
+ if (node.kind === ts.SyntaxKind.TrueKeyword)
179
+ return true;
180
+ if (node.kind === ts.SyntaxKind.FalseKeyword)
181
+ return false;
182
+ if (node.kind === ts.SyntaxKind.NullKeyword)
183
+ return null;
184
+ if (ts.isPrefixUnaryExpression(node) &&
185
+ node.operator === ts.SyntaxKind.MinusToken) {
186
+ const inner = literalToValue(node.operand);
187
+ return typeof inner === "number" ? -inner : undefined;
188
+ }
189
+ if (ts.isArrayLiteralExpression(node)) {
190
+ return node.elements.map((el) => literalToValue(el));
191
+ }
192
+ if (ts.isObjectLiteralExpression(node)) {
193
+ const obj = {};
194
+ for (const p of node.properties) {
195
+ if (ts.isPropertyAssignment(p)) {
196
+ const key = propertyName(p.name);
197
+ if (key)
198
+ obj[key] = literalToValue(p.initializer);
199
+ }
200
+ }
201
+ return obj;
202
+ }
203
+ return undefined;
204
+ }
205
+ function normaliseSync(sync) {
206
+ if (!sync || typeof sync !== "object")
207
+ return undefined;
208
+ const mode = sync.mode === "local" ? "local" : "server";
209
+ if (mode === "server" && !sync.confirmed) {
210
+ // A bare server resource carries no sync semantics.
211
+ return undefined;
212
+ }
213
+ const model = { mode };
214
+ if (typeof sync.scope === "string")
215
+ model.scope = sync.scope;
216
+ if (Array.isArray(sync.confirmed))
217
+ model.confirmed = sync.confirmed;
218
+ if (sync.load === "lazy")
219
+ model.load = "lazy";
220
+ if (sync.clientIds === true)
221
+ model.clientIds = true;
222
+ return model;
223
+ }
224
+ function normaliseAdmin(admin) {
225
+ if (admin === false)
226
+ return false;
227
+ if (!admin || typeof admin !== "object")
228
+ return undefined;
229
+ const model = {};
230
+ if (Array.isArray(admin.listDisplay))
231
+ model.listDisplay = admin.listDisplay;
232
+ return model;
233
+ }
@@ -0,0 +1,19 @@
1
+ import { Manifest, ResourceModel } from "./model";
2
+ export { analyze } from "./analyze";
3
+ export { buildManifest } from "./manifest";
4
+ export { buildOpenApi } from "./openapi";
5
+ export * from "./model";
6
+ export interface CompileResult {
7
+ manifest: Manifest;
8
+ openapi: any;
9
+ files: string[];
10
+ }
11
+ /** List every `.ts` resource file under a directory (or accept explicit files). */
12
+ export declare function resolveResourceFiles(input: string): string[];
13
+ /** Analyse resource definitions into the canonical model (no file output). */
14
+ export declare function analyzeResources(input: string): ResourceModel[];
15
+ /** Compile resource definitions to a manifest + OpenAPI, writing both to disk. */
16
+ export declare function compile(input: string, outDir: string): CompileResult;
17
+ /** Detect duplicate `app.model` definitions and dangling FK targets. */
18
+ export declare function collisions(resources: ResourceModel[]): string[];
19
+ export declare function urlsFor(resources: ResourceModel[]): string[];
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
26
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.urlsFor = exports.collisions = exports.compile = exports.analyzeResources = exports.resolveResourceFiles = exports.buildOpenApi = exports.buildManifest = exports.analyze = void 0;
30
+ const fs = __importStar(require("fs"));
31
+ const path = __importStar(require("path"));
32
+ const analyze_1 = require("./analyze");
33
+ const manifest_1 = require("./manifest");
34
+ const openapi_1 = require("./openapi");
35
+ const program_1 = require("./program");
36
+ var analyze_2 = require("./analyze");
37
+ Object.defineProperty(exports, "analyze", { enumerable: true, get: function () { return analyze_2.analyze; } });
38
+ var manifest_2 = require("./manifest");
39
+ Object.defineProperty(exports, "buildManifest", { enumerable: true, get: function () { return manifest_2.buildManifest; } });
40
+ var openapi_2 = require("./openapi");
41
+ Object.defineProperty(exports, "buildOpenApi", { enumerable: true, get: function () { return openapi_2.buildOpenApi; } });
42
+ __exportStar(require("./model"), exports);
43
+ /** List every `.ts` resource file under a directory (or accept explicit files). */
44
+ function resolveResourceFiles(input) {
45
+ const stat = fs.existsSync(input) ? fs.statSync(input) : undefined;
46
+ if (stat === null || stat === void 0 ? void 0 : stat.isFile())
47
+ return [path.resolve(input)];
48
+ if (stat === null || stat === void 0 ? void 0 : stat.isDirectory()) {
49
+ return fs
50
+ .readdirSync(input)
51
+ .filter((f) => f.endsWith(".ts") && !f.endsWith(".d.ts"))
52
+ .map((f) => path.resolve(input, f))
53
+ .sort();
54
+ }
55
+ throw new Error(`No such resources path: ${input}`);
56
+ }
57
+ exports.resolveResourceFiles = resolveResourceFiles;
58
+ /** Analyse resource definitions into the canonical model (no file output). */
59
+ function analyzeResources(input) {
60
+ const files = resolveResourceFiles(input);
61
+ const program = (0, program_1.createProgram)(files);
62
+ return (0, analyze_1.analyze)(program, files);
63
+ }
64
+ exports.analyzeResources = analyzeResources;
65
+ /** Compile resource definitions to a manifest + OpenAPI, writing both to disk. */
66
+ function compile(input, outDir) {
67
+ const resources = analyzeResources(input);
68
+ const manifest = (0, manifest_1.buildManifest)(resources);
69
+ const openapi = (0, openapi_1.buildOpenApi)(resources);
70
+ fs.mkdirSync(outDir, { recursive: true });
71
+ const manifestPath = path.join(outDir, "schema.rad.json");
72
+ const openapiPath = path.join(outDir, "openapi.json");
73
+ fs.writeFileSync(manifestPath, (0, manifest_1.manifestToJson)(manifest), { flag: "w" });
74
+ fs.writeFileSync(openapiPath, (0, openapi_1.openApiToJson)(openapi), { flag: "w" });
75
+ return { manifest, openapi, files: [manifestPath, openapiPath] };
76
+ }
77
+ exports.compile = compile;
78
+ // -- introspection (replaces `manage.py rastack check|list|urls`) -----------
79
+ /** Detect duplicate `app.model` definitions and dangling FK targets. */
80
+ function collisions(resources) {
81
+ var _a;
82
+ const errors = [];
83
+ const seen = new Map();
84
+ const keys = new Set(resources.map((r) => `${r.app}.${r.model}`));
85
+ for (const r of resources) {
86
+ const key = `${r.app}.${r.model}`;
87
+ seen.set(key, ((_a = seen.get(key)) !== null && _a !== void 0 ? _a : 0) + 1);
88
+ for (const rel of r.relations) {
89
+ if (!keys.has(rel.target)) {
90
+ errors.push(`${key}.${rel.field} references unknown resource ${rel.target}.`);
91
+ }
92
+ }
93
+ }
94
+ for (const [key, count] of seen) {
95
+ if (count > 1)
96
+ errors.push(`Duplicate resource ${key} defined ${count} times.`);
97
+ }
98
+ return errors;
99
+ }
100
+ exports.collisions = collisions;
101
+ function urlsFor(resources) {
102
+ return resources.flatMap((r) => [
103
+ `/api/${r.app}/v1/${r.model}/`,
104
+ `/api/${r.app}/v1/${r.model}/{id}/`,
105
+ ]);
106
+ }
107
+ exports.urlsFor = urlsFor;
@@ -0,0 +1,4 @@
1
+ import { Manifest, ResourceModel } from "./model";
2
+ /** Assemble the canonical manifest from analysed resources. */
3
+ export declare function buildManifest(resources: ResourceModel[]): Manifest;
4
+ export declare function manifestToJson(manifest: Manifest): string;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.manifestToJson = exports.buildManifest = void 0;
4
+ /** Assemble the canonical manifest from analysed resources. */
5
+ function buildManifest(resources) {
6
+ return { version: 1, resources };
7
+ }
8
+ exports.buildManifest = buildManifest;
9
+ function manifestToJson(manifest) {
10
+ return JSON.stringify(manifest, null, 2) + "\n";
11
+ }
12
+ exports.manifestToJson = manifestToJson;
@@ -0,0 +1,54 @@
1
+ /**
2
+ * The canonical, backend-agnostic data model produced by the compiler. This is
3
+ * what `schema.rad.json` serializes and what both the OpenAPI emitter and the
4
+ * Rust API consume.
5
+ */
6
+ export type ScalarType = "string" | "int" | "float" | "bool" | "datetime" | "uuid";
7
+ export type FieldType = ScalarType | "fk";
8
+ export interface RelationTarget {
9
+ app: string;
10
+ model: string;
11
+ }
12
+ export interface FieldModel {
13
+ name: string;
14
+ type: FieldType;
15
+ maxLength?: number;
16
+ unique?: boolean;
17
+ primaryKey?: boolean;
18
+ null?: boolean;
19
+ default?: unknown;
20
+ /** Present iff `type === "fk"` — the resource this field points at. */
21
+ relation?: RelationTarget;
22
+ }
23
+ export interface RelationModel {
24
+ /** The field on *this* resource that holds the foreign key. */
25
+ field: string;
26
+ /** `"app.model"` of the referenced resource. */
27
+ target: string;
28
+ kind: "fk";
29
+ }
30
+ export interface SyncModel {
31
+ mode: "server" | "local";
32
+ scope?: string;
33
+ confirmed?: string[];
34
+ load?: "instant" | "lazy";
35
+ clientIds?: boolean;
36
+ }
37
+ export interface AdminModel {
38
+ listDisplay?: string[];
39
+ }
40
+ export interface ResourceModel {
41
+ app: string;
42
+ model: string;
43
+ fields: FieldModel[];
44
+ relations: RelationModel[];
45
+ sync?: SyncModel;
46
+ admin?: AdminModel | false;
47
+ search?: string[];
48
+ ordering?: string[];
49
+ permission?: string;
50
+ }
51
+ export interface Manifest {
52
+ version: 1;
53
+ resources: ResourceModel[];
54
+ }
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ /**
3
+ * The canonical, backend-agnostic data model produced by the compiler. This is
4
+ * what `schema.rad.json` serializes and what both the OpenAPI emitter and the
5
+ * Rust API consume.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,11 @@
1
+ import { ResourceModel } from "./model";
2
+ /**
3
+ * Emit an OpenAPI 3 document from the analysed resources. The output reproduces
4
+ * the *exact* contract the existing `tools/` codegen and `stack/` runtime rely
5
+ * on — `/api/{app}/v1/{model}/` URL segments and the `x-rastack-sync*` vendor
6
+ * extensions — so those packages are unchanged. Foreign keys additionally carry
7
+ * a new `x-rastack-relation` extension so downstream tooling (and the admin) can
8
+ * follow relations.
9
+ */
10
+ export declare function buildOpenApi(resources: ResourceModel[]): any;
11
+ export declare function openApiToJson(doc: any): string;