bonescript-compiler 0.5.5 → 0.5.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.
Files changed (45) hide show
  1. package/dist/cli.js +17 -1
  2. package/dist/cli.js.map +1 -1
  3. package/dist/emit_admin.d.ts +7 -0
  4. package/dist/emit_admin.js +130 -0
  5. package/dist/emit_admin.js.map +1 -0
  6. package/dist/emit_audit.d.ts +7 -0
  7. package/dist/emit_audit.js +89 -0
  8. package/dist/emit_audit.js.map +1 -0
  9. package/dist/emit_full.js +22 -0
  10. package/dist/emit_full.js.map +1 -1
  11. package/dist/emit_openapi.d.ts +7 -0
  12. package/dist/emit_openapi.js +333 -0
  13. package/dist/emit_openapi.js.map +1 -0
  14. package/dist/emit_postman.d.ts +6 -0
  15. package/dist/emit_postman.js +126 -0
  16. package/dist/emit_postman.js.map +1 -0
  17. package/dist/emit_runtime.js +30 -6
  18. package/dist/emit_runtime.js.map +1 -1
  19. package/dist/emit_sdk.d.ts +7 -0
  20. package/dist/emit_sdk.js +162 -0
  21. package/dist/emit_sdk.js.map +1 -0
  22. package/dist/emit_seed.d.ts +6 -0
  23. package/dist/emit_seed.js +88 -0
  24. package/dist/emit_seed.js.map +1 -0
  25. package/dist/emit_zod.d.ts +7 -0
  26. package/dist/emit_zod.js +115 -0
  27. package/dist/emit_zod.js.map +1 -0
  28. package/dist/index.d.ts +7 -0
  29. package/dist/index.js +19 -1
  30. package/dist/index.js.map +1 -1
  31. package/dist/lowering.js +5 -2
  32. package/dist/lowering.js.map +1 -1
  33. package/package.json +1 -1
  34. package/src/cli.ts +14 -1
  35. package/src/emit_admin.ts +131 -0
  36. package/src/emit_audit.ts +112 -0
  37. package/src/emit_full.ts +29 -0
  38. package/src/emit_openapi.ts +344 -0
  39. package/src/emit_postman.ts +145 -0
  40. package/src/emit_runtime.ts +31 -6
  41. package/src/emit_sdk.ts +195 -0
  42. package/src/emit_seed.ts +91 -0
  43. package/src/emit_zod.ts +111 -0
  44. package/src/index.ts +9 -0
  45. package/src/lowering.ts +5 -2
@@ -0,0 +1,333 @@
1
+ "use strict";
2
+ /**
3
+ * BoneScript OpenAPI Emitter
4
+ * Generates OpenAPI 3.0.3 YAML and JSON specs from an IRSystem.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.emitOpenApiJson = exports.emitOpenApiSpec = void 0;
8
+ // ─── Helpers ──────────────────────────────────────────────────────────────────
9
+ function toSnakeCase(s) {
10
+ return s.replace(/([a-z])([A-Z])/g, "$1_$2").toLowerCase();
11
+ }
12
+ function toDashCase(s) {
13
+ return toSnakeCase(s).replace(/_/g, "-");
14
+ }
15
+ function toPascalCase(s) {
16
+ return s.replace(/(^|_)([a-z])/g, (_, _p, c) => c.toUpperCase());
17
+ }
18
+ function irTypeToOpenApi(irType) {
19
+ if (irType === "string")
20
+ return { type: "string" };
21
+ if (irType === "uint" || irType === "int")
22
+ return { type: "integer" };
23
+ if (irType === "float")
24
+ return { type: "number" };
25
+ if (irType === "bool")
26
+ return { type: "boolean" };
27
+ if (irType === "timestamp")
28
+ return { type: "string", format: "date-time" };
29
+ if (irType === "uuid")
30
+ return { type: "string", format: "uuid" };
31
+ if (irType === "bytes")
32
+ return { type: "string", format: "byte" };
33
+ if (irType === "json")
34
+ return { type: "object" };
35
+ const listMatch = irType.match(/^list<(.+)>$/);
36
+ if (listMatch)
37
+ return { type: "array", items: irTypeToOpenApi(listMatch[1]) };
38
+ const setMatch = irType.match(/^set<(.+)>$/);
39
+ if (setMatch)
40
+ return { type: "array", items: irTypeToOpenApi(setMatch[1]) };
41
+ const optMatch = irType.match(/^optional<(.+)>$/);
42
+ if (optMatch)
43
+ return { ...irTypeToOpenApi(optMatch[1]), nullable: true };
44
+ return { type: "string" };
45
+ }
46
+ function ind(n) {
47
+ return " ".repeat(n);
48
+ }
49
+ function yamlValue(v, depth) {
50
+ if (v === null || v === undefined)
51
+ return "null";
52
+ if (typeof v === "boolean")
53
+ return String(v);
54
+ if (typeof v === "number")
55
+ return String(v);
56
+ if (typeof v === "string") {
57
+ if (v.includes(":") ||
58
+ v.includes("#") ||
59
+ v.includes("'") ||
60
+ v.startsWith("{") ||
61
+ v.startsWith("[")) {
62
+ return JSON.stringify(v);
63
+ }
64
+ return v;
65
+ }
66
+ if (Array.isArray(v)) {
67
+ if (v.length === 0)
68
+ return "[]";
69
+ return ("\n" +
70
+ v
71
+ .map((item) => ind(depth) + "- " + yamlValue(item, depth + 1))
72
+ .join("\n"));
73
+ }
74
+ if (typeof v === "object") {
75
+ const entries = Object.entries(v);
76
+ if (entries.length === 0)
77
+ return "{}";
78
+ return ("\n" +
79
+ entries
80
+ .map(([k, val]) => {
81
+ const valStr = yamlValue(val, depth + 1);
82
+ if (valStr.startsWith("\n")) {
83
+ return ind(depth) + k + ":" + valStr;
84
+ }
85
+ return ind(depth) + k + ": " + valStr;
86
+ })
87
+ .join("\n"));
88
+ }
89
+ return String(v);
90
+ }
91
+ function objToYaml(obj, depth = 0) {
92
+ const lines = [];
93
+ for (const [k, v] of Object.entries(obj)) {
94
+ const valStr = yamlValue(v, depth + 1);
95
+ if (valStr.startsWith("\n")) {
96
+ lines.push(ind(depth) + k + ":" + valStr);
97
+ }
98
+ else {
99
+ lines.push(ind(depth) + k + ": " + valStr);
100
+ }
101
+ }
102
+ return lines.join("\n");
103
+ }
104
+ // ─── Spec builder ─────────────────────────────────────────────────────────────
105
+ function buildSpec(system) {
106
+ const paths = {};
107
+ const schemas = {};
108
+ for (const mod of system.modules) {
109
+ if (mod.kind !== "api_service" || mod.models.length === 0)
110
+ continue;
111
+ const model = mod.models[0];
112
+ const tableName = toSnakeCase(model.name);
113
+ const modelName = toPascalCase(model.name);
114
+ const collectionPath = "/" + tableName + "s";
115
+ const itemPath = "/" + tableName + "s/{id}";
116
+ const allMethods = mod.interfaces.flatMap((i) => i.methods);
117
+ const crudNames = new Set(["create", "read", "update", "delete", "list"]);
118
+ const capabilityMethods = allMethods.filter((m) => !crudNames.has(m.name.toLowerCase()));
119
+ const securityRef = [{ BearerAuth: [] }];
120
+ const listOp = {
121
+ summary: "List " + modelName,
122
+ operationId: "list" + modelName,
123
+ tags: [modelName],
124
+ parameters: [
125
+ { name: "page", in: "query", schema: { type: "integer", default: 1 } },
126
+ {
127
+ name: "page_size",
128
+ in: "query",
129
+ schema: { type: "integer", default: 50 },
130
+ },
131
+ ],
132
+ responses: {
133
+ "200": {
134
+ description: "List of " + modelName,
135
+ content: {
136
+ "application/json": {
137
+ schema: {
138
+ type: "object",
139
+ properties: {
140
+ items: {
141
+ type: "array",
142
+ items: { $ref: "#/components/schemas/" + modelName },
143
+ },
144
+ total: { type: "integer" },
145
+ page: { type: "integer" },
146
+ page_size: { type: "integer" },
147
+ },
148
+ },
149
+ },
150
+ },
151
+ },
152
+ "401": { description: "Unauthorized" },
153
+ },
154
+ };
155
+ const createOp = {
156
+ summary: "Create " + modelName,
157
+ operationId: "create" + modelName,
158
+ tags: [modelName],
159
+ security: securityRef,
160
+ requestBody: {
161
+ required: true,
162
+ content: {
163
+ "application/json": {
164
+ schema: { $ref: "#/components/schemas/" + modelName },
165
+ },
166
+ },
167
+ },
168
+ responses: {
169
+ "200": {
170
+ description: "Created",
171
+ content: {
172
+ "application/json": {
173
+ schema: { $ref: "#/components/schemas/" + modelName },
174
+ },
175
+ },
176
+ },
177
+ "401": { description: "Unauthorized" },
178
+ "422": { description: "Precondition failed" },
179
+ "400": { description: "Bad request" },
180
+ },
181
+ };
182
+ paths[collectionPath] = { get: listOp, post: createOp };
183
+ const idParam = [
184
+ {
185
+ name: "id",
186
+ in: "path",
187
+ required: true,
188
+ schema: { type: "string", format: "uuid" },
189
+ },
190
+ ];
191
+ paths[itemPath] = {
192
+ get: {
193
+ summary: "Get " + modelName,
194
+ operationId: "get" + modelName,
195
+ tags: [modelName],
196
+ parameters: idParam,
197
+ security: securityRef,
198
+ responses: {
199
+ "200": {
200
+ description: "Found",
201
+ content: {
202
+ "application/json": {
203
+ schema: { $ref: "#/components/schemas/" + modelName },
204
+ },
205
+ },
206
+ },
207
+ "401": { description: "Unauthorized" },
208
+ "400": { description: "Not found" },
209
+ },
210
+ },
211
+ put: {
212
+ summary: "Update " + modelName,
213
+ operationId: "update" + modelName,
214
+ tags: [modelName],
215
+ parameters: idParam,
216
+ security: securityRef,
217
+ requestBody: {
218
+ required: true,
219
+ content: {
220
+ "application/json": {
221
+ schema: { $ref: "#/components/schemas/" + modelName },
222
+ },
223
+ },
224
+ },
225
+ responses: {
226
+ "200": {
227
+ description: "Updated",
228
+ content: {
229
+ "application/json": {
230
+ schema: { $ref: "#/components/schemas/" + modelName },
231
+ },
232
+ },
233
+ },
234
+ "401": { description: "Unauthorized" },
235
+ "422": { description: "Precondition failed" },
236
+ "400": { description: "Bad request" },
237
+ },
238
+ },
239
+ delete: {
240
+ summary: "Delete " + modelName,
241
+ operationId: "delete" + modelName,
242
+ tags: [modelName],
243
+ parameters: idParam,
244
+ security: securityRef,
245
+ responses: {
246
+ "200": { description: "Deleted" },
247
+ "401": { description: "Unauthorized" },
248
+ "400": { description: "Not found" },
249
+ },
250
+ },
251
+ };
252
+ for (const method of capabilityMethods) {
253
+ const capPath = collectionPath + "/" + toDashCase(method.name);
254
+ const capOp = {
255
+ summary: method.name + " on " + modelName,
256
+ operationId: method.name + modelName,
257
+ tags: [modelName],
258
+ requestBody: {
259
+ required: true,
260
+ content: {
261
+ "application/json": {
262
+ schema: { $ref: "#/components/schemas/" + modelName },
263
+ },
264
+ },
265
+ },
266
+ responses: {
267
+ "200": {
268
+ description: "Success",
269
+ content: {
270
+ "application/json": {
271
+ schema: {
272
+ type: "object",
273
+ properties: {
274
+ ok: { type: "boolean" },
275
+ action: { type: "string" },
276
+ },
277
+ },
278
+ },
279
+ },
280
+ },
281
+ "401": { description: "Unauthorized" },
282
+ "422": { description: "Precondition failed" },
283
+ "400": { description: "Bad request" },
284
+ },
285
+ };
286
+ if (method.authenticated) {
287
+ capOp.security = securityRef;
288
+ }
289
+ paths[capPath] = { post: capOp };
290
+ }
291
+ const properties = {};
292
+ for (const field of model.fields) {
293
+ properties[field.name] = irTypeToOpenApi(field.type);
294
+ }
295
+ schemas[modelName] = {
296
+ type: "object",
297
+ properties,
298
+ };
299
+ }
300
+ return {
301
+ openapi: "3.0.3",
302
+ info: {
303
+ title: system.name,
304
+ version: system.version,
305
+ description: "Generated by BoneScript compiler",
306
+ },
307
+ servers: [{ url: "http://localhost:3000" }],
308
+ paths,
309
+ components: {
310
+ securitySchemes: {
311
+ BearerAuth: {
312
+ type: "http",
313
+ scheme: "bearer",
314
+ bearerFormat: "JWT",
315
+ },
316
+ },
317
+ schemas,
318
+ },
319
+ };
320
+ }
321
+ // ─── Public API ───────────────────────────────────────────────────────────────
322
+ function emitOpenApiSpec(system) {
323
+ const spec = buildSpec(system);
324
+ const lines = ["# Generated by BoneScript compiler"];
325
+ lines.push(objToYaml(spec));
326
+ return lines.join("\n") + "\n";
327
+ }
328
+ exports.emitOpenApiSpec = emitOpenApiSpec;
329
+ function emitOpenApiJson(system) {
330
+ return JSON.stringify(buildSpec(system), null, 2);
331
+ }
332
+ exports.emitOpenApiJson = emitOpenApiJson;
333
+ //# sourceMappingURL=emit_openapi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emit_openapi.js","sourceRoot":"","sources":["../src/emit_openapi.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAIH,iFAAiF;AAEjF,SAAS,WAAW,CAAC,CAAS;IAC5B,OAAO,CAAC,CAAC,OAAO,CAAC,iBAAiB,EAAE,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC;AAC7D,CAAC;AAED,SAAS,UAAU,CAAC,CAAS;IAC3B,OAAO,WAAW,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;AAC3C,CAAC;AAED,SAAS,YAAY,CAAC,CAAS;IAC7B,OAAO,CAAC,CAAC,OAAO,CAAC,eAAe,EAAE,CAAC,CAAS,EAAE,EAAU,EAAE,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;AAC3F,CAAC;AAED,SAAS,eAAe,CAAC,MAAc;IACrC,IAAI,MAAM,KAAK,QAAQ;QAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;IACnD,IAAI,MAAM,KAAK,MAAM,IAAI,MAAM,KAAK,KAAK;QAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;IACtE,IAAI,MAAM,KAAK,OAAO;QAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;IAClD,IAAI,MAAM,KAAK,MAAM;QAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;IAClD,IAAI,MAAM,KAAK,WAAW;QAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;IAC3E,IAAI,MAAM,KAAK,MAAM;QAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IACjE,IAAI,MAAM,KAAK,OAAO;QAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAClE,IAAI,MAAM,KAAK,MAAM;QAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;IACjD,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;IAC/C,IAAI,SAAS;QAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,eAAe,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9E,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;IAC7C,IAAI,QAAQ;QAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5E,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;IAClD,IAAI,QAAQ;QAAE,OAAO,EAAE,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IACzE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;AAC5B,CAAC;AAED,SAAS,GAAG,CAAC,CAAS;IACpB,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;AACxB,CAAC;AAED,SAAS,SAAS,CAAC,CAAU,EAAE,KAAa;IAC1C,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC;IACjD,IAAI,OAAO,CAAC,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;IAC7C,IAAI,OAAO,CAAC,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;IAC5C,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;QAC1B,IACE,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC;YACf,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC;YACf,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC;YACf,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC;YACjB,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,EACjB,CAAC;YACD,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;QAC3B,CAAC;QACD,OAAO,CAAC,CAAC;IACX,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;QACrB,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QAChC,OAAO,CACL,IAAI;YACJ,CAAC;iBACE,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;iBAC7D,IAAI,CAAC,IAAI,CAAC,CACd,CAAC;IACJ,CAAC;IACD,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;QAC1B,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,CAA4B,CAAC,CAAC;QAC7D,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QACtC,OAAO,CACL,IAAI;YACJ,OAAO;iBACJ,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE;gBAChB,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;gBACzC,IAAI,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;oBAC5B,OAAO,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,MAAM,CAAC;gBACvC,CAAC;gBACD,OAAO,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,MAAM,CAAC;YACxC,CAAC,CAAC;iBACD,IAAI,CAAC,IAAI,CAAC,CACd,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;AACnB,CAAC;AAED,SAAS,SAAS,CAAC,GAA4B,EAAE,KAAK,GAAG,CAAC;IACxD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACzC,MAAM,MAAM,GAAG,SAAS,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;QACvC,IAAI,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YAC5B,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,MAAM,CAAC,CAAC;QAC5C,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,MAAM,CAAC,CAAC;QAC7C,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,iFAAiF;AAEjF,SAAS,SAAS,CAAC,MAAmB;IACpC,MAAM,KAAK,GAA4B,EAAE,CAAC;IAC1C,MAAM,OAAO,GAA4B,EAAE,CAAC;IAE5C,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACjC,IAAI,GAAG,CAAC,IAAI,KAAK,aAAa,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QAEpE,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAC5B,MAAM,SAAS,GAAG,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC1C,MAAM,SAAS,GAAG,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC3C,MAAM,cAAc,GAAG,GAAG,GAAG,SAAS,GAAG,GAAG,CAAC;QAC7C,MAAM,QAAQ,GAAG,GAAG,GAAG,SAAS,GAAG,QAAQ,CAAC;QAE5C,MAAM,UAAU,GAAkB,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QAC3E,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;QAC1E,MAAM,iBAAiB,GAAG,UAAU,CAAC,MAAM,CACzC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAC5C,CAAC;QAEF,MAAM,WAAW,GAAG,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC,CAAC;QAEzC,MAAM,MAAM,GAA4B;YACtC,OAAO,EAAE,OAAO,GAAG,SAAS;YAC5B,WAAW,EAAE,MAAM,GAAG,SAAS;YAC/B,IAAI,EAAE,CAAC,SAAS,CAAC;YACjB,UAAU,EAAE;gBACV,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE,EAAE;gBACtE;oBACE,IAAI,EAAE,WAAW;oBACjB,EAAE,EAAE,OAAO;oBACX,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE,EAAE;iBACzC;aACF;YACD,SAAS,EAAE;gBACT,KAAK,EAAE;oBACL,WAAW,EAAE,UAAU,GAAG,SAAS;oBACnC,OAAO,EAAE;wBACP,kBAAkB,EAAE;4BAClB,MAAM,EAAE;gCACN,IAAI,EAAE,QAAQ;gCACd,UAAU,EAAE;oCACV,KAAK,EAAE;wCACL,IAAI,EAAE,OAAO;wCACb,KAAK,EAAE,EAAE,IAAI,EAAE,uBAAuB,GAAG,SAAS,EAAE;qCACrD;oCACD,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;oCAC1B,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;oCACzB,SAAS,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;iCAC/B;6BACF;yBACF;qBACF;iBACF;gBACD,KAAK,EAAE,EAAE,WAAW,EAAE,cAAc,EAAE;aACvC;SACF,CAAC;QAEF,MAAM,QAAQ,GAA4B;YACxC,OAAO,EAAE,SAAS,GAAG,SAAS;YAC9B,WAAW,EAAE,QAAQ,GAAG,SAAS;YACjC,IAAI,EAAE,CAAC,SAAS,CAAC;YACjB,QAAQ,EAAE,WAAW;YACrB,WAAW,EAAE;gBACX,QAAQ,EAAE,IAAI;gBACd,OAAO,EAAE;oBACP,kBAAkB,EAAE;wBAClB,MAAM,EAAE,EAAE,IAAI,EAAE,uBAAuB,GAAG,SAAS,EAAE;qBACtD;iBACF;aACF;YACD,SAAS,EAAE;gBACT,KAAK,EAAE;oBACL,WAAW,EAAE,SAAS;oBACtB,OAAO,EAAE;wBACP,kBAAkB,EAAE;4BAClB,MAAM,EAAE,EAAE,IAAI,EAAE,uBAAuB,GAAG,SAAS,EAAE;yBACtD;qBACF;iBACF;gBACD,KAAK,EAAE,EAAE,WAAW,EAAE,cAAc,EAAE;gBACtC,KAAK,EAAE,EAAE,WAAW,EAAE,qBAAqB,EAAE;gBAC7C,KAAK,EAAE,EAAE,WAAW,EAAE,aAAa,EAAE;aACtC;SACF,CAAC;QAEF,KAAK,CAAC,cAAc,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;QAExD,MAAM,OAAO,GAAG;YACd;gBACE,IAAI,EAAE,IAAI;gBACV,EAAE,EAAE,MAAM;gBACV,QAAQ,EAAE,IAAI;gBACd,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE;aAC3C;SACF,CAAC;QAEF,KAAK,CAAC,QAAQ,CAAC,GAAG;YAChB,GAAG,EAAE;gBACH,OAAO,EAAE,MAAM,GAAG,SAAS;gBAC3B,WAAW,EAAE,KAAK,GAAG,SAAS;gBAC9B,IAAI,EAAE,CAAC,SAAS,CAAC;gBACjB,UAAU,EAAE,OAAO;gBACnB,QAAQ,EAAE,WAAW;gBACrB,SAAS,EAAE;oBACT,KAAK,EAAE;wBACL,WAAW,EAAE,OAAO;wBACpB,OAAO,EAAE;4BACP,kBAAkB,EAAE;gCAClB,MAAM,EAAE,EAAE,IAAI,EAAE,uBAAuB,GAAG,SAAS,EAAE;6BACtD;yBACF;qBACF;oBACD,KAAK,EAAE,EAAE,WAAW,EAAE,cAAc,EAAE;oBACtC,KAAK,EAAE,EAAE,WAAW,EAAE,WAAW,EAAE;iBACpC;aACF;YACD,GAAG,EAAE;gBACH,OAAO,EAAE,SAAS,GAAG,SAAS;gBAC9B,WAAW,EAAE,QAAQ,GAAG,SAAS;gBACjC,IAAI,EAAE,CAAC,SAAS,CAAC;gBACjB,UAAU,EAAE,OAAO;gBACnB,QAAQ,EAAE,WAAW;gBACrB,WAAW,EAAE;oBACX,QAAQ,EAAE,IAAI;oBACd,OAAO,EAAE;wBACP,kBAAkB,EAAE;4BAClB,MAAM,EAAE,EAAE,IAAI,EAAE,uBAAuB,GAAG,SAAS,EAAE;yBACtD;qBACF;iBACF;gBACD,SAAS,EAAE;oBACT,KAAK,EAAE;wBACL,WAAW,EAAE,SAAS;wBACtB,OAAO,EAAE;4BACP,kBAAkB,EAAE;gCAClB,MAAM,EAAE,EAAE,IAAI,EAAE,uBAAuB,GAAG,SAAS,EAAE;6BACtD;yBACF;qBACF;oBACD,KAAK,EAAE,EAAE,WAAW,EAAE,cAAc,EAAE;oBACtC,KAAK,EAAE,EAAE,WAAW,EAAE,qBAAqB,EAAE;oBAC7C,KAAK,EAAE,EAAE,WAAW,EAAE,aAAa,EAAE;iBACtC;aACF;YACD,MAAM,EAAE;gBACN,OAAO,EAAE,SAAS,GAAG,SAAS;gBAC9B,WAAW,EAAE,QAAQ,GAAG,SAAS;gBACjC,IAAI,EAAE,CAAC,SAAS,CAAC;gBACjB,UAAU,EAAE,OAAO;gBACnB,QAAQ,EAAE,WAAW;gBACrB,SAAS,EAAE;oBACT,KAAK,EAAE,EAAE,WAAW,EAAE,SAAS,EAAE;oBACjC,KAAK,EAAE,EAAE,WAAW,EAAE,cAAc,EAAE;oBACtC,KAAK,EAAE,EAAE,WAAW,EAAE,WAAW,EAAE;iBACpC;aACF;SACF,CAAC;QAEF,KAAK,MAAM,MAAM,IAAI,iBAAiB,EAAE,CAAC;YACvC,MAAM,OAAO,GAAG,cAAc,GAAG,GAAG,GAAG,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAC/D,MAAM,KAAK,GAA4B;gBACrC,OAAO,EAAE,MAAM,CAAC,IAAI,GAAG,MAAM,GAAG,SAAS;gBACzC,WAAW,EAAE,MAAM,CAAC,IAAI,GAAG,SAAS;gBACpC,IAAI,EAAE,CAAC,SAAS,CAAC;gBACjB,WAAW,EAAE;oBACX,QAAQ,EAAE,IAAI;oBACd,OAAO,EAAE;wBACP,kBAAkB,EAAE;4BAClB,MAAM,EAAE,EAAE,IAAI,EAAE,uBAAuB,GAAG,SAAS,EAAE;yBACtD;qBACF;iBACF;gBACD,SAAS,EAAE;oBACT,KAAK,EAAE;wBACL,WAAW,EAAE,SAAS;wBACtB,OAAO,EAAE;4BACP,kBAAkB,EAAE;gCAClB,MAAM,EAAE;oCACN,IAAI,EAAE,QAAQ;oCACd,UAAU,EAAE;wCACV,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;wCACvB,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;qCAC3B;iCACF;6BACF;yBACF;qBACF;oBACD,KAAK,EAAE,EAAE,WAAW,EAAE,cAAc,EAAE;oBACtC,KAAK,EAAE,EAAE,WAAW,EAAE,qBAAqB,EAAE;oBAC7C,KAAK,EAAE,EAAE,WAAW,EAAE,aAAa,EAAE;iBACtC;aACF,CAAC;YACF,IAAI,MAAM,CAAC,aAAa,EAAE,CAAC;gBACzB,KAAK,CAAC,QAAQ,GAAG,WAAW,CAAC;YAC/B,CAAC;YACD,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;QACnC,CAAC;QAED,MAAM,UAAU,GAA4B,EAAE,CAAC;QAC/C,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;YACjC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,eAAe,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACvD,CAAC;QACD,OAAO,CAAC,SAAS,CAAC,GAAG;YACnB,IAAI,EAAE,QAAQ;YACd,UAAU;SACX,CAAC;IACJ,CAAC;IAED,OAAO;QACL,OAAO,EAAE,OAAO;QAChB,IAAI,EAAE;YACJ,KAAK,EAAE,MAAM,CAAC,IAAI;YAClB,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,WAAW,EAAE,kCAAkC;SAChD;QACD,OAAO,EAAE,CAAC,EAAE,GAAG,EAAE,uBAAuB,EAAE,CAAC;QAC3C,KAAK;QACL,UAAU,EAAE;YACV,eAAe,EAAE;gBACf,UAAU,EAAE;oBACV,IAAI,EAAE,MAAM;oBACZ,MAAM,EAAE,QAAQ;oBAChB,YAAY,EAAE,KAAK;iBACpB;aACF;YACD,OAAO;SACR;KACF,CAAC;AACJ,CAAC;AAED,iFAAiF;AAEjF,SAAgB,eAAe,CAAC,MAAmB;IACjD,MAAM,IAAI,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;IAC/B,MAAM,KAAK,GAAa,CAAC,oCAAoC,CAAC,CAAC;IAC/D,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;IAC5B,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AACjC,CAAC;AALD,0CAKC;AAED,SAAgB,eAAe,CAAC,MAAmB;IACjD,OAAO,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AACpD,CAAC;AAFD,0CAEC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * BoneScript Postman Collection Emitter
3
+ * Generates a Postman Collection v2.1 JSON from an IRSystem.
4
+ */
5
+ import * as IR from "./ir";
6
+ export declare function emitPostmanCollection(system: IR.IRSystem): string;
@@ -0,0 +1,126 @@
1
+ "use strict";
2
+ /**
3
+ * BoneScript Postman Collection Emitter
4
+ * Generates a Postman Collection v2.1 JSON from an IRSystem.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.emitPostmanCollection = void 0;
8
+ // ─── Helpers ──────────────────────────────────────────────────────────────────
9
+ function toSnakeCase(s) {
10
+ return s.replace(/([a-z])([A-Z])/g, "$1_$2").toLowerCase();
11
+ }
12
+ function toDashCase(s) {
13
+ return toSnakeCase(s).replace(/_/g, "-");
14
+ }
15
+ function sampleValue(irType) {
16
+ if (irType === "string")
17
+ return "example";
18
+ if (irType === "uint" || irType === "int")
19
+ return 1;
20
+ if (irType === "float")
21
+ return 1.0;
22
+ if (irType === "bool")
23
+ return true;
24
+ if (irType === "uuid")
25
+ return "00000000-0000-0000-0000-000000000001";
26
+ if (irType === "timestamp")
27
+ return "2024-01-01T00:00:00.000Z";
28
+ if (irType === "bytes")
29
+ return "";
30
+ if (irType === "json")
31
+ return {};
32
+ const listMatch = irType.match(/^list<(.+)>$/);
33
+ if (listMatch)
34
+ return [];
35
+ const setMatch = irType.match(/^set<(.+)>$/);
36
+ if (setMatch)
37
+ return [];
38
+ const optMatch = irType.match(/^optional<(.+)>$/);
39
+ if (optMatch)
40
+ return null;
41
+ return "example";
42
+ }
43
+ function buildSampleBody(model) {
44
+ const body = {};
45
+ for (const field of model.fields) {
46
+ body[field.name] = sampleValue(field.type);
47
+ }
48
+ return body;
49
+ }
50
+ function makeRequest(name, method, url, body) {
51
+ const headers = [
52
+ { key: "Content-Type", value: "application/json" },
53
+ { key: "Authorization", value: "Bearer {{token}}" },
54
+ ];
55
+ const req = {
56
+ name,
57
+ request: {
58
+ method,
59
+ header: headers,
60
+ url: {
61
+ raw: url,
62
+ host: ["{{baseUrl}}"],
63
+ path: url
64
+ .replace("{{baseUrl}}/", "")
65
+ .split("/")
66
+ .filter(Boolean),
67
+ },
68
+ },
69
+ };
70
+ if (body !== undefined) {
71
+ req.request.body = {
72
+ mode: "raw",
73
+ raw: JSON.stringify(body, null, 2),
74
+ options: { raw: { language: "json" } },
75
+ };
76
+ }
77
+ return req;
78
+ }
79
+ // ─── Public API ───────────────────────────────────────────────────────────────
80
+ function emitPostmanCollection(system) {
81
+ const folders = [];
82
+ for (const mod of system.modules) {
83
+ if (mod.kind !== "api_service" || mod.models.length === 0)
84
+ continue;
85
+ const model = mod.models[0];
86
+ const tableName = toSnakeCase(model.name);
87
+ const baseUrl = `{{baseUrl}}/${tableName}s`;
88
+ const sampleBody = buildSampleBody(model);
89
+ const items = [
90
+ makeRequest(`List ${model.name}s`, "GET", baseUrl),
91
+ makeRequest(`Create ${model.name}`, "POST", baseUrl, sampleBody),
92
+ makeRequest(`Get ${model.name}`, "GET", `${baseUrl}/:id`),
93
+ makeRequest(`Update ${model.name}`, "PUT", `${baseUrl}/:id`, sampleBody),
94
+ makeRequest(`Delete ${model.name}`, "DELETE", `${baseUrl}/:id`),
95
+ ];
96
+ const crudNames = new Set(["create", "read", "update", "delete", "list"]);
97
+ const allMethods = mod.interfaces.flatMap((i) => i.methods);
98
+ const capabilityMethods = allMethods.filter((m) => !crudNames.has(m.name.toLowerCase()));
99
+ for (const method of capabilityMethods) {
100
+ const dashName = toDashCase(method.name);
101
+ items.push(makeRequest(method.name + " " + model.name, "POST", `${baseUrl}/${dashName}`, sampleBody));
102
+ }
103
+ folders.push({
104
+ name: mod.name,
105
+ item: items,
106
+ });
107
+ }
108
+ const collection = {
109
+ info: {
110
+ name: system.name,
111
+ schema: "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
112
+ },
113
+ auth: {
114
+ type: "bearer",
115
+ bearer: [{ key: "token", value: "{{token}}", type: "string" }],
116
+ },
117
+ variable: [
118
+ { key: "baseUrl", value: "http://localhost:3000" },
119
+ { key: "token", value: "" },
120
+ ],
121
+ item: folders,
122
+ };
123
+ return JSON.stringify(collection, null, 2);
124
+ }
125
+ exports.emitPostmanCollection = emitPostmanCollection;
126
+ //# sourceMappingURL=emit_postman.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emit_postman.js","sourceRoot":"","sources":["../src/emit_postman.ts"],"names":[],"mappings":"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"}
@@ -209,6 +209,11 @@ function emitEntityRouter(mod, system) {
209
209
  lines.push(`import { query, queryOne, execute, pool } from "../db";`);
210
210
  lines.push(`import { eventBus } from "../events";`);
211
211
  lines.push(`import { requireAuth, AuthContext } from "../auth";`);
212
+ lines.push(`import rateLimit from "express-rate-limit";`);
213
+ // Only import audit if module has audit: true
214
+ if (mod.config["audit"]) {
215
+ lines.push(`import { auditLog } from "../audit";`);
216
+ }
212
217
  lines.push(`import { logger } from "../logger";`);
213
218
  lines.push(`import { counter } from "../metrics";`);
214
219
  lines.push(`import * as __algorithms from "../algorithms";`);
@@ -230,10 +235,21 @@ function emitEntityRouter(mod, system) {
230
235
  }
231
236
  lines.push(`export const ${toCamelCase(routeBase)}Router = Router();`);
232
237
  lines.push(``);
238
+ // Per-module rate limiter (from policy declaration)
239
+ const modRateLimit = typeof mod.config["rate_limit"] === "number" && mod.config["rate_limit"] > 0
240
+ ? mod.config["rate_limit"] : 0;
241
+ const modRateLimitWindowMs = typeof mod.config["rate_limit_window_ms"] === "number"
242
+ ? mod.config["rate_limit_window_ms"] : 60000;
243
+ if (modRateLimit > 0) {
244
+ lines.push(`// Rate limiter from policy declaration`);
245
+ lines.push(`const __routeRateLimit = rateLimit({ windowMs: ${modRateLimitWindowMs}, max: ${modRateLimit}, standardHeaders: true, legacyHeaders: false });`);
246
+ lines.push(``);
247
+ }
233
248
  // CREATE
234
249
  const insertFields = entityModel.fields.filter(f => f.name !== "id" && f.name !== "created_at" && f.name !== "updated_at");
235
250
  lines.push(`// CREATE`);
236
- lines.push(`${toCamelCase(routeBase)}Router.post("/", requireAuth, async (req: Request, res: Response) => {`);
251
+ const __crudMiddlewares = modRateLimit > 0 ? "__routeRateLimit, requireAuth" : "requireAuth";
252
+ lines.push(`${toCamelCase(routeBase)}Router.post("/", ${__crudMiddlewares}, async (req: Request, res: Response) => {`);
237
253
  lines.push(` try {`);
238
254
  lines.push(` const id = uuid();`);
239
255
  lines.push(` const { ${insertFields.map(f => f.name).join(", ")} } = req.body;`);
@@ -257,7 +273,7 @@ function emitEntityRouter(mod, system) {
257
273
  lines.push(``);
258
274
  // READ
259
275
  lines.push(`// READ`);
260
- lines.push(`${toCamelCase(routeBase)}Router.get("/:id", requireAuth, async (req: Request, res: Response) => {`);
276
+ lines.push(`${toCamelCase(routeBase)}Router.get("/:id", ${__crudMiddlewares}, async (req: Request, res: Response) => {`);
261
277
  lines.push(` try {`);
262
278
  lines.push(` const row = await queryOne(\`SELECT * FROM ${tableName} WHERE id = $1\`, [req.params.id]);`);
263
279
  lines.push(` if (!row) return res.status(404).json({ error: { code: "NOT_FOUND", message: "Not found" } });`);
@@ -270,7 +286,7 @@ function emitEntityRouter(mod, system) {
270
286
  // LIST — with optional JOINs for has_one/belongs_to relations
271
287
  const joinRelations = mod.relations.filter(r => r.kind === "has_one" || r.kind === "belongs_to");
272
288
  lines.push(`// LIST`);
273
- lines.push(`${toCamelCase(routeBase)}Router.get("/", requireAuth, async (req: Request, res: Response) => {`);
289
+ lines.push(`${toCamelCase(routeBase)}Router.get("/", ${__crudMiddlewares}, async (req: Request, res: Response) => {`);
274
290
  lines.push(` try {`);
275
291
  lines.push(` const page = parseInt(req.query.page as string) || 1;`);
276
292
  lines.push(` const pageSize = Math.min(parseInt(req.query.page_size as string) || 50, 100);`);
@@ -299,7 +315,7 @@ function emitEntityRouter(mod, system) {
299
315
  lines.push(``);
300
316
  // UPDATE — with state machine enforcement
301
317
  lines.push(`// UPDATE`);
302
- lines.push(`${toCamelCase(routeBase)}Router.put("/:id", requireAuth, async (req: Request, res: Response) => {`);
318
+ lines.push(`${toCamelCase(routeBase)}Router.put("/:id", ${__crudMiddlewares}, async (req: Request, res: Response) => {`);
303
319
  lines.push(` const fields = { ...req.body };`);
304
320
  if (mod.state_machines.length > 0) {
305
321
  const sm = mod.state_machines[0];
@@ -323,7 +339,7 @@ function emitEntityRouter(mod, system) {
323
339
  lines.push(``);
324
340
  // DELETE
325
341
  lines.push(`// DELETE`);
326
- lines.push(`${toCamelCase(routeBase)}Router.delete("/:id", requireAuth, async (req: Request, res: Response) => {`);
342
+ lines.push(`${toCamelCase(routeBase)}Router.delete("/:id", ${__crudMiddlewares}, async (req: Request, res: Response) => {`);
327
343
  lines.push(` try {`);
328
344
  lines.push(` const count = await execute(\`DELETE FROM ${tableName} WHERE id = $1\`, [req.params.id]);`);
329
345
  lines.push(` if (count === 0) return res.status(404).json({ error: { code: "NOT_FOUND", message: "Not found" } });`);
@@ -381,7 +397,15 @@ function emitCapabilityEndpoint(method, mod, tableName, system) {
381
397
  const endpoint = `/${method.name.replace(/_/g, "-")}`;
382
398
  const isTransactional = method.sync === "transactional";
383
399
  lines.push(`// CAPABILITY: ${method.name}${isTransactional ? " [transactional]" : ""}${method.retry ? ` [retry: ${method.retry.max_attempts}x ${method.retry.backoff}]` : ""}`);
384
- lines.push(`${routerName}.post("${endpoint}", requireAuth, async (req: Request, res: Response) => {`);
400
+ // Build middleware chain: optional rate limiter + requireAuth + optional audit
401
+ const capMiddlewares = ["requireAuth"];
402
+ if (typeof mod.config["rate_limit"] === "number" && mod.config["rate_limit"] > 0) {
403
+ capMiddlewares.unshift("__routeRateLimit");
404
+ }
405
+ if (mod.config["audit"]) {
406
+ capMiddlewares.push(`auditLog("${method.name}", "${mod.models[0]?.name ?? ""}")`);
407
+ }
408
+ lines.push(`${routerName}.post("${endpoint}", ${capMiddlewares.join(", ")}, async (req: Request, res: Response) => {`);
385
409
  lines.push(` const auth: AuthContext = (req as any).auth;`);
386
410
  // Wrap in retry logic if declared
387
411
  if (method.retry) {