@figulus/schema 0.4.0-alpha-dev → 0.5.0-alpha-dev-1

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 (192) hide show
  1. package/.openapi-meta/index.ts +12 -0
  2. package/dist/core/entities/figspec.d.ts +659 -0
  3. package/dist/core/entities/figspec.d.ts.map +1 -0
  4. package/dist/core/entities/figspec.js +73 -0
  5. package/dist/core/entities/figspec.js.map +1 -0
  6. package/dist/core/entities/figstack.d.ts +139 -0
  7. package/dist/core/entities/figstack.d.ts.map +1 -0
  8. package/dist/core/entities/figstack.js +79 -0
  9. package/dist/core/entities/figstack.js.map +1 -0
  10. package/dist/core/entities/index.d.ts +3 -0
  11. package/dist/core/entities/index.d.ts.map +1 -0
  12. package/dist/core/entities/index.js +3 -0
  13. package/dist/core/entities/index.js.map +1 -0
  14. package/dist/core/execute.d.ts +64 -0
  15. package/dist/core/execute.d.ts.map +1 -0
  16. package/dist/core/execute.js +45 -0
  17. package/dist/core/execute.js.map +1 -0
  18. package/dist/core/generic.d.ts +35 -0
  19. package/dist/core/generic.d.ts.map +1 -0
  20. package/dist/core/generic.js +28 -0
  21. package/dist/core/generic.js.map +1 -0
  22. package/dist/core/index.d.ts +6 -0
  23. package/dist/core/index.d.ts.map +1 -0
  24. package/dist/core/index.js +6 -0
  25. package/dist/core/index.js.map +1 -0
  26. package/dist/core/primitives.d.ts +3 -0
  27. package/dist/core/primitives.d.ts.map +1 -0
  28. package/dist/core/primitives.js +11 -0
  29. package/dist/core/primitives.js.map +1 -0
  30. package/dist/core/volumes.d.ts +160 -0
  31. package/dist/core/volumes.d.ts.map +1 -0
  32. package/dist/core/volumes.js +100 -0
  33. package/dist/core/volumes.js.map +1 -0
  34. package/dist/engine/deprecated/index.d.ts +2 -0
  35. package/dist/engine/deprecated/index.d.ts.map +1 -0
  36. package/dist/engine/deprecated/index.js +2 -0
  37. package/dist/engine/deprecated/index.js.map +1 -0
  38. package/dist/engine/deprecated/run-request.d.ts +73 -0
  39. package/dist/engine/deprecated/run-request.d.ts.map +1 -0
  40. package/dist/engine/deprecated/run-request.js +47 -0
  41. package/dist/engine/deprecated/run-request.js.map +1 -0
  42. package/dist/engine/dry-run.d.ts +15 -0
  43. package/dist/engine/dry-run.d.ts.map +1 -0
  44. package/dist/engine/dry-run.js +12 -0
  45. package/dist/engine/dry-run.js.map +1 -0
  46. package/dist/engine/health.d.ts +11 -0
  47. package/dist/engine/health.d.ts.map +1 -0
  48. package/dist/engine/health.js +9 -0
  49. package/dist/engine/health.js.map +1 -0
  50. package/dist/engine/images.d.ts +49 -0
  51. package/dist/engine/images.d.ts.map +1 -0
  52. package/dist/engine/images.js +44 -0
  53. package/dist/engine/images.js.map +1 -0
  54. package/dist/engine/index.d.ts +9 -0
  55. package/dist/engine/index.d.ts.map +1 -0
  56. package/dist/engine/index.js +9 -0
  57. package/dist/engine/index.js.map +1 -0
  58. package/dist/engine/paths/containers.d.ts +8 -0
  59. package/dist/engine/paths/containers.d.ts.map +1 -0
  60. package/dist/engine/paths/containers.js +297 -0
  61. package/dist/engine/paths/containers.js.map +1 -0
  62. package/dist/engine/paths/dry-run.d.ts +3 -0
  63. package/dist/engine/paths/dry-run.d.ts.map +1 -0
  64. package/dist/engine/paths/dry-run.js +43 -0
  65. package/dist/engine/paths/dry-run.js.map +1 -0
  66. package/dist/engine/paths/health.d.ts +3 -0
  67. package/dist/engine/paths/health.d.ts.map +1 -0
  68. package/dist/engine/paths/health.js +36 -0
  69. package/dist/engine/paths/health.js.map +1 -0
  70. package/dist/engine/paths/images.d.ts +5 -0
  71. package/dist/engine/paths/images.d.ts.map +1 -0
  72. package/dist/engine/paths/images.js +144 -0
  73. package/dist/engine/paths/images.js.map +1 -0
  74. package/dist/engine/paths/index.d.ts +8 -0
  75. package/dist/engine/paths/index.d.ts.map +1 -0
  76. package/dist/engine/paths/index.js +8 -0
  77. package/dist/engine/paths/index.js.map +1 -0
  78. package/dist/engine/paths/sessions.d.ts +10 -0
  79. package/dist/engine/paths/sessions.d.ts.map +1 -0
  80. package/dist/engine/paths/sessions.js +484 -0
  81. package/dist/engine/paths/sessions.js.map +1 -0
  82. package/dist/engine/paths/stacks.d.ts +6 -0
  83. package/dist/engine/paths/stacks.d.ts.map +1 -0
  84. package/dist/engine/paths/stacks.js +175 -0
  85. package/dist/engine/paths/stacks.js.map +1 -0
  86. package/dist/engine/paths/volumes.d.ts +6 -0
  87. package/dist/engine/paths/volumes.d.ts.map +1 -0
  88. package/dist/engine/paths/volumes.js +164 -0
  89. package/dist/engine/paths/volumes.js.map +1 -0
  90. package/dist/engine/response.d.ts +8 -0
  91. package/dist/engine/response.d.ts.map +1 -0
  92. package/dist/engine/response.js +9 -0
  93. package/dist/engine/response.js.map +1 -0
  94. package/dist/engine/sessions.d.ts +343 -0
  95. package/dist/engine/sessions.d.ts.map +1 -0
  96. package/dist/engine/sessions.js +118 -0
  97. package/dist/engine/sessions.js.map +1 -0
  98. package/dist/engine/volumes.d.ts +52 -0
  99. package/dist/engine/volumes.d.ts.map +1 -0
  100. package/dist/engine/volumes.js +39 -0
  101. package/dist/engine/volumes.js.map +1 -0
  102. package/dist/index.d.ts +2 -8
  103. package/dist/index.d.ts.map +1 -1
  104. package/dist/index.js +2 -8
  105. package/dist/index.js.map +1 -1
  106. package/dist/registry/index.d.ts +3 -0
  107. package/dist/registry/index.d.ts.map +1 -0
  108. package/dist/registry/index.js +3 -0
  109. package/dist/registry/index.js.map +1 -0
  110. package/dist/registry/json-schema.d.ts +82 -0
  111. package/dist/registry/json-schema.d.ts.map +1 -0
  112. package/dist/registry/json-schema.js +149 -0
  113. package/dist/registry/json-schema.js.map +1 -0
  114. package/dist/registry/metadata.d.ts +216 -0
  115. package/dist/registry/metadata.d.ts.map +1 -0
  116. package/dist/registry/metadata.js +143 -0
  117. package/dist/registry/metadata.js.map +1 -0
  118. package/package.json +10 -3
  119. package/scripts/fix-openapi-3.1.ts +183 -0
  120. package/scripts/generate-openapi-data.ts +51 -0
  121. package/scripts/generate-openapi-meta.ts +540 -0
  122. package/src/core/entities/figspec.ts +94 -0
  123. package/src/core/entities/figstack.ts +101 -0
  124. package/src/core/entities/index.ts +2 -0
  125. package/src/core/execute.ts +66 -0
  126. package/src/core/generic.ts +38 -0
  127. package/src/core/index.ts +5 -0
  128. package/src/core/primitives.ts +12 -0
  129. package/src/core/volumes.ts +124 -0
  130. package/src/engine/deprecated/index.ts +1 -0
  131. package/src/engine/deprecated/run-request.ts +53 -0
  132. package/src/engine/dry-run.ts +14 -0
  133. package/src/engine/health.ts +11 -0
  134. package/src/engine/images.ts +58 -0
  135. package/src/engine/index.ts +8 -0
  136. package/src/engine/paths/containers.ts +304 -0
  137. package/src/engine/paths/dry-run.ts +44 -0
  138. package/src/engine/paths/health.ts +37 -0
  139. package/src/engine/paths/images.ts +148 -0
  140. package/src/engine/paths/index.ts +7 -0
  141. package/src/engine/paths/sessions.ts +493 -0
  142. package/src/engine/paths/stacks.ts +179 -0
  143. package/src/engine/paths/volumes.ts +168 -0
  144. package/src/engine/response.ts +11 -0
  145. package/src/engine/sessions.ts +146 -0
  146. package/src/engine/volumes.ts +53 -0
  147. package/src/index.ts +2 -8
  148. package/src/registry/index.ts +2 -0
  149. package/src/registry/json-schema.ts +178 -0
  150. package/src/registry/metadata.ts +181 -0
  151. package/tests/figspec.test.ts +1 -1
  152. package/tests/schemas.test.ts +31 -31
  153. package/dist/figspec.d.ts +0 -4394
  154. package/dist/figspec.d.ts.map +0 -1
  155. package/dist/figspec.js +0 -214
  156. package/dist/figspec.js.map +0 -1
  157. package/dist/figstack.d.ts +0 -419
  158. package/dist/figstack.d.ts.map +0 -1
  159. package/dist/figstack.js +0 -72
  160. package/dist/figstack.js.map +0 -1
  161. package/dist/health.d.ts +0 -16
  162. package/dist/health.d.ts.map +0 -1
  163. package/dist/health.js +0 -7
  164. package/dist/health.js.map +0 -1
  165. package/dist/image.d.ts +0 -115
  166. package/dist/image.d.ts.map +0 -1
  167. package/dist/image.js +0 -42
  168. package/dist/image.js.map +0 -1
  169. package/dist/run-request.d.ts +0 -244
  170. package/dist/run-request.d.ts.map +0 -1
  171. package/dist/run-request.js +0 -43
  172. package/dist/run-request.js.map +0 -1
  173. package/dist/session.d.ts +0 -447
  174. package/dist/session.d.ts.map +0 -1
  175. package/dist/session.js +0 -78
  176. package/dist/session.js.map +0 -1
  177. package/dist/shared.d.ts +0 -90
  178. package/dist/shared.d.ts.map +0 -1
  179. package/dist/shared.js +0 -41
  180. package/dist/shared.js.map +0 -1
  181. package/dist/volume.d.ts +0 -140
  182. package/dist/volume.d.ts.map +0 -1
  183. package/dist/volume.js +0 -37
  184. package/dist/volume.js.map +0 -1
  185. package/src/figspec.ts +0 -279
  186. package/src/figstack.ts +0 -92
  187. package/src/health.ts +0 -8
  188. package/src/image.ts +0 -55
  189. package/src/run-request.ts +0 -55
  190. package/src/session.ts +0 -101
  191. package/src/shared.ts +0 -56
  192. package/src/volume.ts +0 -50
@@ -0,0 +1,540 @@
1
+ import { OpenAPIObjectConfig } from "@asteasolutions/zod-to-openapi/dist/v3.0/openapi-generator";
2
+ import { mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
3
+ import path from "path";
4
+ import { fileURLToPath } from "url";
5
+ import z from "zod";
6
+ import { OpenAPIMappedSchemaIterable } from "../.openapi-meta";
7
+
8
+ const fileSchema = z.enum([
9
+ "core-entities-figspec",
10
+ "core-entities-figstack",
11
+ "core-execute",
12
+ "core-generic",
13
+ "core-primitives",
14
+ "core-volumes",
15
+ "engine-dry-run",
16
+ "engine-deprecated-run-request",
17
+ "engine-health",
18
+ "engine-images",
19
+ "engine-response",
20
+ "engine-sessions",
21
+ "engine-volumes",
22
+ "engine-paths-health",
23
+ "engine-paths-containers",
24
+ "engine-paths-images",
25
+ "engine-paths-volumes",
26
+ "engine-paths-dry-run",
27
+ "engine-paths-sessions",
28
+ "engine-paths-stacks",
29
+ "registry-json-schema",
30
+ "registry-metadata",
31
+ ]);
32
+
33
+ type File = z.infer<typeof fileSchema>;
34
+
35
+ const openApiFileSchema = z.enum(["core", "registry"]);
36
+ type OpenAPIFileId = z.infer<typeof openApiFileSchema>;
37
+
38
+ const fileNames: {
39
+ [file in File]: {
40
+ camelCase: string,
41
+ pascalCase: string,
42
+ kebabCase: string,
43
+ type: "schema" | "path",
44
+ dir: string,
45
+ filename: string,
46
+ openApiFileId: OpenAPIFileId,
47
+ }
48
+ } = {
49
+ "core-entities-figspec": {
50
+ camelCase: "coreEntitiesFigSpec",
51
+ pascalCase: "CoreEntitiesFigSpec",
52
+ kebabCase: "core-entities-figspec",
53
+ type: "schema",
54
+ dir: "core/entities",
55
+ filename: "figspec",
56
+ openApiFileId: "core",
57
+ },
58
+ "core-entities-figstack": {
59
+ camelCase: "coreEntitiesFigStack",
60
+ pascalCase: "CoreEntitiesFigStack",
61
+ kebabCase: "core-entities-figstack",
62
+ type: "schema",
63
+ dir: "core/entities",
64
+ filename: "figstack",
65
+ openApiFileId: "core",
66
+ },
67
+ "core-execute": {
68
+ camelCase: "coreExecute",
69
+ pascalCase: "CoreExecute",
70
+ kebabCase: "core-execute",
71
+ type: "schema",
72
+ dir: "core",
73
+ filename: "execute",
74
+ openApiFileId: "core",
75
+ },
76
+ "core-generic": {
77
+ camelCase: "coreGeneric",
78
+ pascalCase: "CoreGeneric",
79
+ kebabCase: "core-generic",
80
+ type: "schema",
81
+ dir: "core",
82
+ filename: "generic",
83
+ openApiFileId: "core",
84
+ },
85
+ "core-primitives": {
86
+ camelCase: "corePrimitives",
87
+ pascalCase: "CorePrimitives",
88
+ kebabCase: "core-primitives",
89
+ type: "schema",
90
+ dir: "core",
91
+ filename: "primitives",
92
+ openApiFileId: "core",
93
+ },
94
+ "core-volumes": {
95
+ camelCase: "coreVolumes",
96
+ pascalCase: "CoreVolumes",
97
+ kebabCase: "core-volumes",
98
+ type: "schema",
99
+ dir: "core",
100
+ filename: "volumes",
101
+ openApiFileId: "core",
102
+ },
103
+ "engine-deprecated-run-request": {
104
+ camelCase: "engineDeprecatedRunRequest",
105
+ pascalCase: "EngineDeprecatedRunRequest",
106
+ kebabCase: "engine-deprecated-run-request",
107
+ type: "schema",
108
+ dir: "engine/deprecated",
109
+ filename: "run-request",
110
+ openApiFileId: "core",
111
+ },
112
+ "engine-dry-run": {
113
+ camelCase: "engineDryRun",
114
+ pascalCase: "EngineDryDry",
115
+ kebabCase: "engine-dry-run",
116
+ type: "schema",
117
+ dir: "engine",
118
+ filename: "dry-run",
119
+ openApiFileId: "core",
120
+ },
121
+ "engine-health": {
122
+ camelCase: "engineHealth",
123
+ pascalCase: "EngineHealth",
124
+ kebabCase: "engine-health",
125
+ type: "schema",
126
+ dir: "engine",
127
+ filename: "health",
128
+ openApiFileId: "core",
129
+ },
130
+ "engine-images": {
131
+ camelCase: "engineImages",
132
+ pascalCase: "EngineImages",
133
+ kebabCase: "engine-images",
134
+ type: "schema",
135
+ dir: "engine",
136
+ filename: "images",
137
+ openApiFileId: "core",
138
+ },
139
+ "engine-response": {
140
+ camelCase: "engineResponse",
141
+ pascalCase: "EngineResponse",
142
+ kebabCase: "engine-response",
143
+ type: "schema",
144
+ dir: "engine",
145
+ filename: "response",
146
+ openApiFileId: "core",
147
+ },
148
+ "engine-sessions": {
149
+ camelCase: "engineSessions",
150
+ pascalCase: "EngineSessions",
151
+ kebabCase: "engine-sessions",
152
+ type: "schema",
153
+ dir: "engine",
154
+ filename: "sessions",
155
+ openApiFileId: "core",
156
+ },
157
+ "engine-volumes": {
158
+ camelCase: "engineVolumes",
159
+ pascalCase: "EngineVolumes",
160
+ kebabCase: "engine-volumes",
161
+ type: "schema",
162
+ dir: "engine",
163
+ filename: "volumes",
164
+ openApiFileId: "core",
165
+ },
166
+ "engine-paths-health": {
167
+ camelCase: "pathsHealth",
168
+ pascalCase: "PathsHealth",
169
+ kebabCase: "paths-health",
170
+ type: "path",
171
+ dir: "engine/paths",
172
+ filename: "health",
173
+ openApiFileId: "core",
174
+ },
175
+ "engine-paths-containers": {
176
+ camelCase: "pathsContainers",
177
+ pascalCase: "PathsContainers",
178
+ kebabCase: "paths-containers",
179
+ type: "path",
180
+ dir: "engine/paths",
181
+ filename: "containers",
182
+ openApiFileId: "core",
183
+ },
184
+ "engine-paths-images": {
185
+ camelCase: "pathsImages",
186
+ pascalCase: "PathsImages",
187
+ kebabCase: "paths-images",
188
+ type: "path",
189
+ dir: "engine/paths",
190
+ filename: "images",
191
+ openApiFileId: "core",
192
+ },
193
+ "engine-paths-volumes": {
194
+ camelCase: "pathsVolumes",
195
+ pascalCase: "PathsVolumes",
196
+ kebabCase: "paths-volumes",
197
+ type: "path",
198
+ dir: "engine/paths",
199
+ filename: "volumes",
200
+ openApiFileId: "core",
201
+ },
202
+ "engine-paths-dry-run": {
203
+ camelCase: "pathsDryRun",
204
+ pascalCase: "PathsDryRun",
205
+ kebabCase: "paths-dry-run",
206
+ type: "path",
207
+ dir: "engine/paths",
208
+ filename: "dry-run",
209
+ openApiFileId: "core",
210
+ },
211
+ "engine-paths-sessions": {
212
+ camelCase: "pathsSessions",
213
+ pascalCase: "PathsSessions",
214
+ kebabCase: "paths-sessions",
215
+ type: "path",
216
+ dir: "engine/paths",
217
+ filename: "sessions",
218
+ openApiFileId: "core",
219
+ },
220
+ "engine-paths-stacks": {
221
+ camelCase: "pathsStacks",
222
+ pascalCase: "PathsStacks",
223
+ kebabCase: "paths-stacks",
224
+ type: "path",
225
+ dir: "engine/paths",
226
+ filename: "stacks",
227
+ openApiFileId: "core",
228
+ },
229
+ "registry-json-schema": {
230
+ camelCase: "registryJsonSchema",
231
+ pascalCase: "RegistryJsonSchema",
232
+ kebabCase: "registry-json-schema",
233
+ type: "schema",
234
+ dir: "registry",
235
+ filename: "json-schema",
236
+ openApiFileId: "registry",
237
+ },
238
+ "registry-metadata": {
239
+ camelCase: "registryMetadata",
240
+ pascalCase: "RegistryMetadata",
241
+ kebabCase: "registry-metadata",
242
+ type: "schema",
243
+ dir: "registry",
244
+ filename: "metadata",
245
+ openApiFileId: "registry",
246
+ },
247
+ };
248
+
249
+ function getVersion() {
250
+ const packageJsonPath = path.resolve(
251
+ path.dirname(fileURLToPath(import.meta.url)),
252
+ "..", "..", "..",
253
+ "package.json"
254
+ );
255
+ const packageJsonData = readFileSync(packageJsonPath);
256
+ const parsed = z.object({ version: z.string() }).parse(
257
+ JSON.parse(packageJsonData.toString())
258
+ );
259
+ return parsed.version;
260
+ }
261
+
262
+ const openApiFiles: {
263
+ [Id in OpenAPIFileId]: {
264
+ fileName: string,
265
+ document: OpenAPIObjectConfig,
266
+ }
267
+ } = {
268
+ core: {
269
+ fileName: "openapi-new",
270
+ document: {
271
+ openapi: "3.1.0",
272
+ info: {
273
+ title: "Figulus Schema",
274
+ version: getVersion(),
275
+ description:
276
+ "Schemas and types for the Figulus ecosystem",
277
+ contact: {
278
+ name: "Figulus Project",
279
+ url: "https://github.com/figulusproject/figulus",
280
+ },
281
+ },
282
+ servers: [
283
+ {
284
+ url: "http://localhost",
285
+ description: "Local Unix socket (connect via /run/figulus/figulus.sock)",
286
+ },
287
+ ],
288
+ tags: [
289
+ {
290
+ name: "health",
291
+ description: "Engine and Docker health checks",
292
+ },
293
+ {
294
+ name: "containers",
295
+ description: "Container job execution and lifecycle management",
296
+ },
297
+ {
298
+ name: "images",
299
+ description: "Docker image build and garbage collection",
300
+ },
301
+ {
302
+ name: "volumes",
303
+ description: "Docker volume management",
304
+ },
305
+ {
306
+ name: "sessions",
307
+ description: "Persistent session container management",
308
+ },
309
+ {
310
+ name: "stacks",
311
+ description: "FigStack multi-container orchestration",
312
+ },
313
+ ]
314
+ }
315
+ },
316
+ registry: {
317
+ fileName: "openapi-registry",
318
+ document: {
319
+ openapi: "3.1.0",
320
+ info: {
321
+ title: "Figulus Registry Schema",
322
+ version: getVersion(),
323
+ description:
324
+ "Schemas and types for the Figulus Registry",
325
+ contact: {
326
+ name: "Figulus Project",
327
+ url: "https://github.com/figulusproject/figulus",
328
+ },
329
+ },
330
+ servers: [
331
+ {
332
+ url: "https://github.com/figulusproject/registry",
333
+ description: "Official GitHub repository for the registry",
334
+ },
335
+ ],
336
+ }
337
+ }
338
+ }
339
+
340
+ function getName(file: File, casing: "camel"|"pascal"|"kebab") {
341
+ return fileNames[file][`${casing}Case`];
342
+ }
343
+
344
+ function getType(file: File) {
345
+ return fileNames[file].type;
346
+ }
347
+
348
+ function getDir(file: File) {
349
+ return fileNames[file].dir || "";
350
+ }
351
+
352
+ function getFilename(file: File) {
353
+ return fileNames[file].filename || getName(file, "kebab");
354
+ }
355
+
356
+ function getOpenAPIFileId(file: File) {
357
+ return fileNames[file].openApiFileId;
358
+ }
359
+
360
+ const generatedDirPath = ".openapi-meta/generated/";
361
+
362
+ type IterableBase = OpenAPIMappedSchemaIterable<string>;
363
+
364
+ function getFileLines(file: File) {
365
+ const dir = getDir(file);
366
+ const dirPath = dir ? `${dir}/` : "";
367
+ const content = readFileSync(path.resolve(`./src/${dirPath}${getFilename(file)}.ts`));
368
+ return content.toString().split("\n");
369
+ }
370
+
371
+ function getFileSrc(file: File): IterableBase {
372
+ const lines = getFileLines(file);
373
+ const output: IterableBase = [];
374
+
375
+ if (getType(file) === "schema") {
376
+ let currentConst: string = "";
377
+
378
+ for (const l of lines) {
379
+ const isConst = l.includes("export const");
380
+ const isOpenAPIRef = /\.openapi\("[^"]+"\)/.test(l);
381
+ if (!isConst && !isOpenAPIRef) continue;
382
+
383
+ const matchAndSlice = (matchPhraseStart: string, matchPhraseEnd: string) => {
384
+ const start = l.slice(l.indexOf(matchPhraseStart) + matchPhraseStart.length);
385
+ const end = start.slice(0, start.indexOf(matchPhraseEnd) + matchPhraseEnd.length - 1);
386
+ return end;
387
+ }
388
+
389
+ if (isConst) {
390
+ const start = l.slice(l.indexOf("export const ") + "export const ".length);
391
+ const match = start.match(/^([a-zA-Z_$][a-zA-Z0-9_$]*)/);
392
+ currentConst = match ? match[1] : "";
393
+ continue;
394
+ }
395
+
396
+ output.push({ key: matchAndSlice(`openapi("`, `"`), obj: currentConst });
397
+ }
398
+ } else {
399
+ const matchPhraseStart = "export const";
400
+ const matchPhraseEnd = ": RouteConfig";
401
+
402
+ const constNames = lines.filter((l) => l.startsWith(matchPhraseStart)).map((l) => {
403
+ const start = l.slice(l.indexOf(matchPhraseStart) + matchPhraseStart.length + 1);
404
+ const end = start.slice(0, start.indexOf(matchPhraseEnd));
405
+ return end;
406
+ });
407
+
408
+ constNames.forEach((name) => {
409
+ output.push({ key: name, obj: name });
410
+ });
411
+ }
412
+
413
+ return output;
414
+ }
415
+
416
+ function generateKeySchema(file: File, refs: IterableBase) {
417
+ const prefix = `export const ${getName(file, "camel")}OpenAPIKeySchema = z.enum([\n`;
418
+ const suffix = `\n]);`;
419
+ return prefix + refs.map((r) => ` "${r.key}",`).join("\n") + suffix;
420
+ }
421
+
422
+ function generateKeyType(file: File) {
423
+ return `type ${getName(file, "pascal")}OpenAPIKey = z.infer<typeof ${getName(file, "camel")}OpenAPIKeySchema>;`
424
+ }
425
+
426
+ function generateMappings(file: File, refs: IterableBase) {
427
+ const isSchema = getType(file) === "schema";
428
+ const valueType = isSchema ? "OpenAPIMappedSchemaValue" : "RouteConfig";
429
+ const prefix = `const ${getName(file, "camel")}OpenAPIMappings: { [key in ${getName(file, "pascal")}OpenAPIKey]: ${valueType} } = {\n`;
430
+ const suffix = `\n};`;
431
+ return prefix + refs.map((r) => ` ${r.key}: ${r.obj},`).join("\n") + suffix;
432
+ }
433
+
434
+ function generateIterable(file: File) {
435
+ const isSchema = getType(file) === "schema";
436
+ const valueType = isSchema ? "OpenAPIMappedSchemaValue" : "RouteConfig";
437
+ return `export const ${getName(file, "camel")}OpenAPIIterable: OpenAPIMappedSchemaIterable<${valueType}> = ${getName(file, "camel")}OpenAPIKeySchema.options.map((key) => ({\n key,\n obj: ${getName(file, "camel")}OpenAPIMappings[key],\n}));`
438
+ }
439
+
440
+ function generateFile(file: File) {
441
+ const refs = getFileSrc(file);
442
+ const isSchema = getType(file) === "schema";
443
+ const dir = getDir(file);
444
+ const dirPath = dir ? `${dir}/` : "";
445
+
446
+ // Calculate relative path depth: for files in subdirs, we need extra ../
447
+ // File at .openapi-meta/generated/paths/health.ts needs ../../../src
448
+ // File at .openapi-meta/generated/health.ts needs ../../src
449
+ const depth = dir ? (dir.split("/").length + 2) : 2;
450
+ const relativePrefix = "../".repeat(depth);
451
+
452
+ const imports = [
453
+ `import z from "zod";`,
454
+ `import { OpenAPIMappedSchemaIterable, OpenAPIMappedSchemaValue } from "${"../".repeat(depth - 1)}index.js";`,
455
+ isSchema ? `` : `import { RouteConfig } from "@asteasolutions/zod-to-openapi";`,
456
+ `import { ${refs.map((v) => v.obj).join(", ")} } from "${relativePrefix}src/${dirPath}${getFilename(file)}.js";`,
457
+ ].filter(Boolean).join("\n");
458
+
459
+ const keySchema = generateKeySchema(file, refs);
460
+ const keyType = generateKeyType(file);
461
+ const mappings = generateMappings(file, refs);
462
+ const iterable = generateIterable(file);
463
+
464
+ return imports + "\n\n" + keySchema + "\n" + keyType + "\n\n" + mappings + "\n\n" + iterable + "\n";
465
+ }
466
+
467
+ function generateIndex(files: File[]) {
468
+ const imports = [
469
+ `import { OpenAPIMappedSchemaIterable, OpenAPIMappedSchemaValue } from "..";\nimport { RouteConfig } from "@asteasolutions/zod-to-openapi";`,
470
+ ...files.map((f) => {
471
+ const dir = getDir(f);
472
+ const dirPath = dir ? `${dir}/` : "";
473
+ return `import { ${getName(f, "camel")}OpenAPIIterable } from "./${dirPath}${getFilename(f)}.js";`;
474
+ }),
475
+ "\n"
476
+ ].join("\n");
477
+
478
+ const schemaFiles = files.filter((f) => getType(f) === "schema");
479
+ const schemaIterables = () => {
480
+ const prefix = `export const openApiSchemaIterables: OpenAPIMappedSchemaIterable<OpenAPIMappedSchemaValue> = [\n`;
481
+ const suffix = `\n];`;
482
+ return prefix + schemaFiles.map((f) => ` ...${getName(f, "camel")}OpenAPIIterable,`).join("\n") + suffix;
483
+ }
484
+
485
+ const pathFiles = files.filter((f) => getType(f) === "path");
486
+ const pathIterables = () => {
487
+ const prefix = `export const openApiPathIterables: OpenAPIMappedSchemaIterable<RouteConfig> = [\n`;
488
+ const suffix = `\n];`;
489
+ return prefix + pathFiles.map((f) => ` ...${getName(f, "camel")}OpenAPIIterable,`).join("\n") + suffix;
490
+ }
491
+
492
+ return imports + schemaIterables() + "\n\n" + pathIterables() + "\n";
493
+ }
494
+
495
+ function writeFile(file: File, content: string) {
496
+ const dir = getDir(file);
497
+ const dirPath = dir ? `${dir}/` : "";
498
+ const fullPath = generatedDirPath + dirPath + getFilename(file) + ".ts";
499
+
500
+ if (dir) {
501
+ mkdirSync(path.resolve(generatedDirPath + dir), { recursive: true });
502
+ }
503
+
504
+ writeFileSync(path.resolve(fullPath), content);
505
+ }
506
+
507
+ function generateFiles(openApiFileId: OpenAPIFileId) {
508
+ rmSync(generatedDirPath, { recursive: true, force: true, });
509
+ mkdirSync(generatedDirPath, { recursive: true });
510
+
511
+ const files = fileSchema.options;
512
+ const filtered = files.filter((f) => getOpenAPIFileId(f) === openApiFileId);
513
+
514
+ for (const file of filtered) {
515
+ const content = generateFile(file);
516
+ writeFile(file, content);
517
+ }
518
+ writeFileSync(path.resolve(generatedDirPath + "index.ts"), generateIndex(filtered));
519
+ writeFileSync(path.resolve(generatedDirPath + "data.json"), JSON.stringify(openApiFiles[openApiFileId], null, 2));
520
+ }
521
+
522
+ function parseArgs() {
523
+ const args = process.argv;
524
+ if(args.length < 3)
525
+ throw new Error("openApiFileId argument is missing!");
526
+
527
+ const arg = args[2].toLowerCase();
528
+ if(!arg.startsWith("--openapifileid="))
529
+ throw new Error(`Invalid argument provided! Expected '--openApiFileId=', received '${arg}'.`);
530
+
531
+ const openApiFileId = openApiFileSchema.parse(
532
+ arg.replace("--openapifileid=", "")
533
+ );
534
+
535
+ return { openApiFileId };
536
+ }
537
+
538
+ const { openApiFileId } = parseArgs();
539
+
540
+ generateFiles(openApiFileId);
@@ -0,0 +1,94 @@
1
+ import { extendZodWithOpenApi } from "@asteasolutions/zod-to-openapi";
2
+ import { z } from 'zod';
3
+ import { buildExecuteStepSchema, executeMainCmdSchema, executeShutdownCmdSchema, executeStartupCmdSchema, scheduledCmdSchema } from '../execute.js';
4
+ import { installGroupSchema, portMappingSchema } from '../generic.js';
5
+ import { buildVolumeMountSchema, runVolumeMountSchema } from '../volumes.js';
6
+
7
+ extendZodWithOpenApi(z);
8
+
9
+ // ---------------------------------------------------------------------------
10
+ // Execute model
11
+ // ---------------------------------------------------------------------------
12
+
13
+ export const figSpecExecuteSchema = z.object({
14
+ startup: z.array(executeStartupCmdSchema).optional().describe("Commands that run before main (sync commands block main start)"),
15
+ main: executeMainCmdSchema,
16
+ shutdown: z.array(executeShutdownCmdSchema).optional().describe("Commands that run before the container stops"),
17
+ scheduled: z.array(scheduledCmdSchema).optional(),
18
+ }).openapi("FigSpecExecute");
19
+ export type FigSpecExecute = z.infer<typeof figSpecExecuteSchema>;
20
+
21
+ // ---------------------------------------------------------------------------
22
+ // Build spec
23
+ // ---------------------------------------------------------------------------
24
+
25
+ export const figSpecBuildBaseSchema = z.object({
26
+ schemaVersion: z.string().optional(),
27
+ specName: z.string().optional(),
28
+ packagesToInstall: z.array(installGroupSchema).optional(),
29
+ platform: z.string().optional().describe("Target platform, e.g. \"linux/amd64\" or \"linux/arm64\"").openapi({ example: "linux/amd64" }),
30
+ env: z.record(z.string(), z.string()).optional().describe("Build-time environment variables"),
31
+ volumes: z.array(buildVolumeMountSchema).optional(),
32
+ execute: z.array(buildExecuteStepSchema).optional().describe("Ordered list of RUN instructions; each step is a separate cacheable layer"),
33
+ workDir: z.string().optional().describe("Working directory inside the image"),
34
+ user: z.string().optional().describe("User to run build commands as (e.g. \"root\")"),
35
+ }).openapi("FigSpecBuildBase");
36
+
37
+ export const figSpecBuildStandaloneSchema = figSpecBuildBaseSchema.extend({
38
+ baseImage: z.string(),
39
+ }).openapi("FigSpecBuildStandalone");
40
+ export type FigSpecBuildStandalone = z.infer<typeof figSpecBuildStandaloneSchema>;
41
+
42
+ export const figSpecBuildWithBaseSpecSchema = figSpecBuildBaseSchema.extend({
43
+ baseSpec: z.string(),
44
+ baseImage: z.string().optional(),
45
+ }).openapi("FigSpecBuildWithBaseSpec");
46
+ export type FigSpecBuildWithBaseSpec = z.infer<typeof figSpecBuildWithBaseSpecSchema>;
47
+
48
+ export const figSpecBuildSchema = z.union([
49
+ figSpecBuildStandaloneSchema,
50
+ figSpecBuildWithBaseSpecSchema,
51
+ ]);
52
+ export type FigSpecBuild = z.infer<typeof figSpecBuildSchema>;
53
+
54
+ // ---------------------------------------------------------------------------
55
+ // Run spec
56
+ // ---------------------------------------------------------------------------
57
+
58
+ export const figSpecRunSchema = z.object({
59
+ schemaVersion: z.string().optional(),
60
+ specName: z.string().optional(),
61
+ execute: figSpecExecuteSchema,
62
+ env: z.record(z.string(), z.string()).optional(),
63
+ secrets: z.record(z.string(), z.string()).optional().describe("Secret references: env_name → \"vault:path#field\""),
64
+ volumes: z.array(runVolumeMountSchema).optional(),
65
+ workDir: z.string().optional(),
66
+ user: z.string().optional(),
67
+ ports: z.array(portMappingSchema).optional(),
68
+ }).openapi("FigSpecRun");
69
+ export type FigSpecRun = z.infer<typeof figSpecRunSchema>;
70
+
71
+ // ---------------------------------------------------------------------------
72
+ // Top-level Figspec document
73
+ // ---------------------------------------------------------------------------
74
+
75
+ export const figSpecBuildInputSchema = z.union([
76
+ figSpecBuildSchema,
77
+ z.string(),
78
+ z.array(z.string()),
79
+ ]).openapi("FigSpecBuildInput");
80
+ export type FigSpecBuildInput = z.infer<typeof figSpecBuildInputSchema>;
81
+
82
+ export const figSpecRunInputSchema = z.union([
83
+ figSpecRunSchema,
84
+ z.string(),
85
+ ]).openapi("FigSpecRunInput");
86
+ export type FigSpecRunInput = z.infer<typeof figSpecRunInputSchema>;
87
+
88
+ export const figSpecSchema = z.object({
89
+ schemaVersion: z.string(),
90
+ specName: z.string().optional(),
91
+ build: figSpecBuildInputSchema,
92
+ run: figSpecRunInputSchema.optional(),
93
+ }).openapi("FigSpec");
94
+ export type FigSpec = z.infer<typeof figSpecSchema>;