stitchkit 0.59.4 → 0.60.0

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 (108) hide show
  1. package/README.md +7 -3
  2. package/dist/agent-runtime/admission-lanes.d.ts +35 -0
  3. package/dist/agent-runtime/admission-lanes.d.ts.map +1 -0
  4. package/dist/agent-runtime/coordinator.d.ts +36 -2
  5. package/dist/agent-runtime/coordinator.d.ts.map +1 -1
  6. package/dist/agent-runtime/events.d.ts +7 -0
  7. package/dist/agent-runtime/events.d.ts.map +1 -1
  8. package/dist/agent-runtime/history.d.ts +7 -0
  9. package/dist/agent-runtime/history.d.ts.map +1 -1
  10. package/dist/agent-runtime/models.d.ts +2 -3
  11. package/dist/agent-runtime/models.d.ts.map +1 -1
  12. package/dist/agent-runtime/observability.d.ts.map +1 -1
  13. package/dist/agent-runtime/run-execution.d.ts +31 -0
  14. package/dist/agent-runtime/run-execution.d.ts.map +1 -0
  15. package/dist/agent-runtime/runtime-internals.d.ts +25 -0
  16. package/dist/agent-runtime/runtime-internals.d.ts.map +1 -0
  17. package/dist/agent-runtime/runtime-result.d.ts +11 -0
  18. package/dist/agent-runtime/runtime-result.d.ts.map +1 -0
  19. package/dist/agent-runtime/runtime.d.ts +16 -15
  20. package/dist/agent-runtime/runtime.d.ts.map +1 -1
  21. package/dist/agent-runtime/schemas.d.ts.map +1 -1
  22. package/dist/agent-runtime/store-driver.d.ts.map +1 -1
  23. package/dist/agent-runtime/store.d.ts +9 -2
  24. package/dist/agent-runtime/store.d.ts.map +1 -1
  25. package/dist/agent-runtime/terminal-commit.d.ts.map +1 -1
  26. package/dist/agent-runtime/terminal-status.d.ts +13 -0
  27. package/dist/agent-runtime/terminal-status.d.ts.map +1 -0
  28. package/dist/agent-runtime.d.ts +5 -3
  29. package/dist/agent-runtime.d.ts.map +1 -1
  30. package/dist/agent-runtime.js +211 -156
  31. package/dist/application/activity.d.ts +7 -2
  32. package/dist/application/activity.d.ts.map +1 -1
  33. package/dist/application/grammy.d.ts +6 -0
  34. package/dist/application/grammy.d.ts.map +1 -1
  35. package/dist/application/health.d.ts +9 -2
  36. package/dist/application/health.d.ts.map +1 -1
  37. package/dist/application/kernel.d.ts +49 -3
  38. package/dist/application/kernel.d.ts.map +1 -1
  39. package/dist/application/schemas.d.ts +42 -0
  40. package/dist/application/schemas.d.ts.map +1 -1
  41. package/dist/application-opentelemetry.js +2 -2
  42. package/dist/application.d.ts +3 -3
  43. package/dist/application.d.ts.map +1 -1
  44. package/dist/application.js +16 -8
  45. package/dist/cli.js +6 -6
  46. package/dist/contract/errors.d.ts +6 -0
  47. package/dist/contract/errors.d.ts.map +1 -1
  48. package/dist/contract/index.js +1 -1
  49. package/dist/declaration.d.ts +345 -0
  50. package/dist/declaration.d.ts.map +1 -0
  51. package/dist/declaration.js +183 -0
  52. package/dist/{index-5c8n2123.js → index-1zencgcb.js} +64 -30
  53. package/dist/{index-sa2mbwa7.js → index-2akar0rq.js} +2 -2
  54. package/dist/{index-41wm56v0.js → index-3hn10n4c.js} +1 -1
  55. package/dist/{index-f6pymtqe.js → index-3w74r08v.js} +2 -2
  56. package/dist/{index-7c0gkyvj.js → index-6fbp58g8.js} +2 -2
  57. package/dist/{index-gnvzyw0k.js → index-8es1hrv4.js} +2 -2
  58. package/dist/{index-mgf6cdkm.js → index-8eywc9zv.js} +35 -1
  59. package/dist/{index-3nwng7hs.js → index-8pc5s3pz.js} +4 -4
  60. package/dist/{index-28cqssm3.js → index-9h1vba8n.js} +6 -0
  61. package/dist/{index-0nc0cddp.js → index-a59da114.js} +17 -28
  62. package/dist/{index-s8w2y7kr.js → index-f7521k0x.js} +22 -16
  63. package/dist/{index-y2rb7dwx.js → index-pzyt11ch.js} +5 -1
  64. package/dist/{index-mr617n62.js → index-svqhcrrj.js} +1 -1
  65. package/dist/{index-ps5pxp3h.js → index-v6e8hnkx.js} +7 -1
  66. package/dist/{index-1tkyng3g.js → index-vkk06pv1.js} +1 -9
  67. package/dist/{index-2nzpcfbp.js → index-x8v8b7rc.js} +2 -2
  68. package/dist/index.js +1 -1
  69. package/dist/internal/observability-sink.d.ts.map +1 -1
  70. package/dist/internal/route-pattern.d.ts +9 -0
  71. package/dist/internal/route-pattern.d.ts.map +1 -1
  72. package/dist/node.d.ts +1 -1
  73. package/dist/node.d.ts.map +1 -1
  74. package/dist/node.js +5 -5
  75. package/dist/observability/index.js +5 -5
  76. package/dist/observability/sanitize.d.ts +3 -1
  77. package/dist/observability/sanitize.d.ts.map +1 -1
  78. package/dist/remote.js +3 -3
  79. package/dist/server/error-hook.d.ts +10 -3
  80. package/dist/server/error-hook.d.ts.map +1 -1
  81. package/dist/server/index.d.ts +1 -1
  82. package/dist/server/index.d.ts.map +1 -1
  83. package/dist/server/index.js +9 -12
  84. package/dist/server/middleware/cors.d.ts +5 -2
  85. package/dist/server/middleware/cors.d.ts.map +1 -1
  86. package/dist/server/openapi.d.ts.map +1 -1
  87. package/dist/server/router.d.ts.map +1 -1
  88. package/dist/server/socket-io-config.d.ts +68 -3
  89. package/dist/server/socket-io-config.d.ts.map +1 -1
  90. package/dist/server/socket-io-node.d.ts +1 -1
  91. package/dist/server/socket-io-node.d.ts.map +1 -1
  92. package/dist/server/socket-io.d.ts +1 -1
  93. package/dist/server/socket-io.d.ts.map +1 -1
  94. package/dist/testing/surface-manifest.d.ts.map +1 -1
  95. package/dist/testing.js +8 -10
  96. package/dist/tools/flatten-join.d.ts +2 -0
  97. package/dist/tools/flatten-join.d.ts.map +1 -1
  98. package/dist/tools/flatten.d.ts.map +1 -1
  99. package/dist/tools/mcp-prepare.d.ts +0 -2
  100. package/dist/tools/mcp-prepare.d.ts.map +1 -1
  101. package/dist/tools/runtime-tool.d.ts +0 -3
  102. package/dist/tools/runtime-tool.d.ts.map +1 -1
  103. package/dist/tools/schema.d.ts +0 -4
  104. package/dist/tools/schema.d.ts.map +1 -1
  105. package/dist/tools.js +9 -9
  106. package/llms-full.txt +924 -68
  107. package/llms.txt +1 -0
  108. package/package.json +6 -2
@@ -0,0 +1,345 @@
1
+ /**
2
+ * The project declaration — the one machine-readable statement a repository
3
+ * chooses to make about itself.
4
+ *
5
+ * The schema lives in the published framework, not in the repository that fills
6
+ * it in, because it has readers that must never disagree: the project itself,
7
+ * the scaffolder that writes the first copy, and whatever binds an artifact
8
+ * of it into a deployment. A copy on any of those sides is a fork rather than a contract —
9
+ * it diverges silently and nothing fails — so the declaration ships as one
10
+ * versioned surface instead.
11
+ *
12
+ * The boundary rule the schema exists to hold:
13
+ *
14
+ * > A declaration must be complete and meaningful **when no machine exists**. A
15
+ * > field that cannot be filled in without knowing where the code will run is a
16
+ * > binding supplied by the deployment, not a declaration made by the
17
+ * > repository.
18
+ *
19
+ * What holds it, stated at its real strength:
20
+ *
21
+ * 1. **Structure.** There is nowhere a value MUST go. A command is `executable`
22
+ * plus an `args` array — no shell string, no pipe, no redirect — and no part
23
+ * may be an absolute path or an assignment in any form, so `--port=8080` and
24
+ * `--config=/srv/…` have to be written as separate arguments where the same
25
+ * checks reach them. Paths are repository-relative. Bindings are named by
26
+ * variable, never valued, and a listener's variables must exist in the env
27
+ * contract with the right shapes.
28
+ * 2. **Hygiene.** Every remaining free string is checked against
29
+ * `namesAMachine` — a scheme, a protocol-relative host, an absolute or
30
+ * home-relative path, a Windows drive, a `host:port` pair, a bare IPv4
31
+ * literal — and a number after a port flag is refused.
32
+ *
33
+ * The second is a filter for known shapes, not a proof. A secret written as its
34
+ * own argument (`--token`, `sk-live-…`) and a hostname written as a plain word
35
+ * (`db.internal`) are indistinguishable from any other argument, and no schema
36
+ * makes them distinguishable. This is not a secret scanner and does not claim to
37
+ * be one: the guarantee is that nothing here REQUIRES a value of the place, so a
38
+ * complete declaration can be written before any machine exists.
39
+ *
40
+ * **Declaring is optional, and that is a contract rather than a gap.** A
41
+ * project with no declaration is a complete project: nothing else in the
42
+ * framework imports this module, no build, test or start path looks for a
43
+ * `project.json`, and the absence of one is never an error or a warning. A
44
+ * check that demanded a declaration "for convenience" would turn a repository
45
+ * into something only one tool can bring up — which is a fork, not a
46
+ * dependency, and is the outcome this whole surface exists to avoid.
47
+ *
48
+ * Unknown keys are **refused**, not stripped. A declaration is a contract
49
+ * between programs that never meet; a key one side does not recognise is a
50
+ * disagreement, and silently discarding it is how a partially understood
51
+ * declaration becomes a running, wrong deployment.
52
+ */
53
+ import { z } from 'zod';
54
+ /**
55
+ * The declaration format this build understands.
56
+ *
57
+ * A reader that does not recognise the version a repository declares refuses
58
+ * the repository rather than interpreting it partially. Because unknown keys
59
+ * are refused too, the version is what a reader consults when the *shape*
60
+ * changed — and every added field is a version bump, not a silent widening.
61
+ */
62
+ export declare const PROJECT_DECLARATION_SCHEMA_VERSION = 1;
63
+ /** The reason this string names a machine, or `undefined` when it does not. */
64
+ export declare function namesAMachine(value: string): string | undefined;
65
+ /**
66
+ * Lowercase, hyphen-separated identity. Everything named after the project —
67
+ * process names, derived resource names — is derived from it, so it is the one
68
+ * identity field with a machine-checkable shape.
69
+ */
70
+ export declare const ProjectSlugSchema: z.ZodString;
71
+ /**
72
+ * Human description keyed by locale tag. Which locales a project speaks is the
73
+ * project's own business — the framework only insists that it speaks one.
74
+ *
75
+ * To fix an exact set, replace this field entirely when composing a stricter
76
+ * declaration; it is a record, so it has no `extend`.
77
+ */
78
+ export declare const ProjectDescriptionSchema: z.ZodRecord<z.ZodString, z.ZodString>;
79
+ /** Who the project is. True of the code with no machine in existence. */
80
+ export declare const ProjectIdentitySchema: z.ZodObject<{
81
+ slug: z.ZodString;
82
+ name: z.ZodString;
83
+ version: z.ZodString;
84
+ description: z.ZodRecord<z.ZodString, z.ZodString>;
85
+ }, z.core.$strict>;
86
+ /**
87
+ * A path inside the source. Repository-relative on purpose: a path is code
88
+ * only while it is relative to the source — the moment it is absolute, climbs
89
+ * out, or names a drive, it names a machine.
90
+ */
91
+ export declare const RepositoryPathSchema: z.ZodString;
92
+ /** The name of a variable a deployment will fill in. A name, never a value. */
93
+ export declare const BindingVariableSchema: z.ZodString;
94
+ /**
95
+ * How a role is reached over the network.
96
+ *
97
+ * Absent means the role has no listener at all — a queue consumer, a bot, a
98
+ * scheduler. That is a legitimate role, not an incomplete one, so nothing here
99
+ * is required of it.
100
+ *
101
+ * Both bindings are named, never valued: a deployment supplies the port and the
102
+ * interface under these names. There is deliberately no way to say "the port
103
+ * arrives as a command-line argument" — a reader would then have to implement
104
+ * two injection forms forever, and a role that needs an argument builds it from
105
+ * the variable inside its own process.
106
+ */
107
+ export declare const ProjectListenerSchema: z.ZodObject<{
108
+ portVariable: z.ZodString;
109
+ bindVariable: z.ZodString;
110
+ readinessPath: z.ZodString;
111
+ }, z.core.$strict>;
112
+ /** The run modes a role declares commands for. */
113
+ export declare const ProjectRunModeSchema: z.ZodEnum<{
114
+ development: "development";
115
+ production: "production";
116
+ }>;
117
+ export declare const ProjectCommandSchema: z.ZodObject<{
118
+ executable: z.ZodString;
119
+ args: z.ZodArray<z.ZodString>;
120
+ }, z.core.$strict>;
121
+ /**
122
+ * A command run under a supervisor, which additionally may not be a launcher.
123
+ *
124
+ * The rule applies to ROLES and not to `build`: a build is not signalled, so a
125
+ * script runner in front of it costs nothing. A supervised role is signalled,
126
+ * and there the launcher is the defect measured above.
127
+ */
128
+ export declare const ProjectRoleCommandSchema: z.ZodObject<{
129
+ executable: z.ZodString;
130
+ args: z.ZodArray<z.ZodString>;
131
+ }, z.core.$strict>;
132
+ export declare const ProjectRoleSchema: z.ZodObject<{
133
+ name: z.ZodString;
134
+ workingDirectory: z.ZodOptional<z.ZodString>;
135
+ commands: z.ZodRecord<z.ZodEnum<{
136
+ development: "development";
137
+ production: "production";
138
+ }>, z.ZodObject<{
139
+ executable: z.ZodString;
140
+ args: z.ZodArray<z.ZodString>;
141
+ }, z.core.$strict>>;
142
+ listener: z.ZodOptional<z.ZodObject<{
143
+ portVariable: z.ZodString;
144
+ bindVariable: z.ZodString;
145
+ readinessPath: z.ZodString;
146
+ }, z.core.$strict>>;
147
+ drainFloorMs: z.ZodNumber;
148
+ }, z.core.$strict>;
149
+ /**
150
+ * Data the build is allowed to read, named and frozen.
151
+ *
152
+ * The boundary rule separates two things — code, and the values of a place.
153
+ * There is a third that is neither: **data read while building**. Pages
154
+ * prerendered from a database depend on bytes that are not in the source and
155
+ * are not a binding, and no amount of moving environment variables makes such a
156
+ * build portable. Left unnamed, the dependency is invisible: the build succeeds
157
+ * on the machine that happens to have the database, and the artifact silently
158
+ * stops being a function of the source.
159
+ *
160
+ * Declaring an input is what makes it legitimate. `path` points at a frozen
161
+ * export inside the source, `digest` pins its bytes, and a build that reads
162
+ * anything else is a build nobody declared. The other two legitimate answers
163
+ * need no field here at all: render at runtime, or generate the bytes as a
164
+ * release step on the way to the deployment.
165
+ */
166
+ export declare const ProjectBuildInputSchema: z.ZodObject<{
167
+ name: z.ZodString;
168
+ path: z.ZodString;
169
+ digest: z.ZodString;
170
+ }, z.core.$strict>;
171
+ export type ProjectBuildInput = z.infer<typeof ProjectBuildInputSchema>;
172
+ /** What building the source produces. More than one path is the normal case. */
173
+ export declare const ProjectBuildSchema: z.ZodObject<{
174
+ command: z.ZodObject<{
175
+ executable: z.ZodString;
176
+ args: z.ZodArray<z.ZodString>;
177
+ }, z.core.$strict>;
178
+ artifacts: z.ZodArray<z.ZodString>;
179
+ inputs: z.ZodOptional<z.ZodArray<z.ZodObject<{
180
+ name: z.ZodString;
181
+ path: z.ZodString;
182
+ digest: z.ZodString;
183
+ }, z.core.$strict>>>;
184
+ }, z.core.$strict>;
185
+ /** When a requirement has to be there. */
186
+ export declare const ProjectRequirementPhaseSchema: z.ZodEnum<{
187
+ release: "release";
188
+ start: "start";
189
+ }>;
190
+ /**
191
+ * Something the code needs that the code does not provide.
192
+ *
193
+ * `phases` says when: `release` is needed once while bringing a deployment to
194
+ * this source, `start` is needed by every process every time it starts. One
195
+ * requirement can be both, and saying so once beats naming it twice.
196
+ */
197
+ export declare const ProjectRequirementSchema: z.ZodObject<{
198
+ name: z.ZodString;
199
+ phases: z.ZodArray<z.ZodEnum<{
200
+ release: "release";
201
+ start: "start";
202
+ }>>;
203
+ }, z.core.$strict>;
204
+ /**
205
+ * What a migration IS, declared as a fact rather than as a command to run.
206
+ *
207
+ * The repository says which bytes are migrations; whatever brings a deployment
208
+ * to this source reads them and decides — exact contents, admission verdict,
209
+ * whether a preflight can be skipped because nothing touches the database. A
210
+ * free list of shell commands would take that decision away from the side that
211
+ * is able to make it, and hand it to the side that cannot see the deployment.
212
+ *
213
+ * `engine` is a name, checked like every other free string: a connection string
214
+ * fits in a name-shaped field otherwise, credentials and all.
215
+ */
216
+ export declare const ProjectMigrationsSchema: z.ZodObject<{
217
+ engine: z.ZodString;
218
+ root: z.ZodString;
219
+ lockfile: z.ZodString;
220
+ }, z.core.$strict>;
221
+ /** What must happen once, before any role starts, to reach this source. */
222
+ export declare const ProjectReleaseSchema: z.ZodObject<{
223
+ migrations: z.ZodOptional<z.ZodObject<{
224
+ engine: z.ZodString;
225
+ root: z.ZodString;
226
+ lockfile: z.ZodString;
227
+ }, z.core.$strict>>;
228
+ }, z.core.$strict>;
229
+ /** The value shapes a declaration can describe without naming a value. */
230
+ export declare const ProjectEnvShapeSchema: z.ZodEnum<{
231
+ boolean: "boolean";
232
+ enum: "enum";
233
+ integer: "integer";
234
+ string: "string";
235
+ url: "url";
236
+ }>;
237
+ /**
238
+ * A variable a deployment must supply.
239
+ *
240
+ * `members` is required for `enum` and forbidden otherwise: a reader without a
241
+ * TypeScript runtime learns nothing from "one of an unnamed set", which is the
242
+ * only thing this list exists to tell it.
243
+ */
244
+ export declare const ProjectEnvVariableSchema: z.ZodObject<{
245
+ name: z.ZodString;
246
+ shape: z.ZodEnum<{
247
+ boolean: "boolean";
248
+ enum: "enum";
249
+ integer: "integer";
250
+ string: "string";
251
+ url: "url";
252
+ }>;
253
+ required: z.ZodBoolean;
254
+ members: z.ZodOptional<z.ZodArray<z.ZodString>>;
255
+ }, z.core.$strict>;
256
+ /** The declaration itself. Every field here is true about the code alone. */
257
+ export declare const ProjectDeclarationSchema: z.ZodObject<{
258
+ schemaVersion: z.ZodLiteral<1>;
259
+ kind: z.ZodEnum<{
260
+ application: "application";
261
+ library: "library";
262
+ }>;
263
+ identity: z.ZodObject<{
264
+ slug: z.ZodString;
265
+ name: z.ZodString;
266
+ version: z.ZodString;
267
+ description: z.ZodRecord<z.ZodString, z.ZodString>;
268
+ }, z.core.$strict>;
269
+ roles: z.ZodArray<z.ZodObject<{
270
+ name: z.ZodString;
271
+ workingDirectory: z.ZodOptional<z.ZodString>;
272
+ commands: z.ZodRecord<z.ZodEnum<{
273
+ development: "development";
274
+ production: "production";
275
+ }>, z.ZodObject<{
276
+ executable: z.ZodString;
277
+ args: z.ZodArray<z.ZodString>;
278
+ }, z.core.$strict>>;
279
+ listener: z.ZodOptional<z.ZodObject<{
280
+ portVariable: z.ZodString;
281
+ bindVariable: z.ZodString;
282
+ readinessPath: z.ZodString;
283
+ }, z.core.$strict>>;
284
+ drainFloorMs: z.ZodNumber;
285
+ }, z.core.$strict>>;
286
+ build: z.ZodOptional<z.ZodObject<{
287
+ command: z.ZodObject<{
288
+ executable: z.ZodString;
289
+ args: z.ZodArray<z.ZodString>;
290
+ }, z.core.$strict>;
291
+ artifacts: z.ZodArray<z.ZodString>;
292
+ inputs: z.ZodOptional<z.ZodArray<z.ZodObject<{
293
+ name: z.ZodString;
294
+ path: z.ZodString;
295
+ digest: z.ZodString;
296
+ }, z.core.$strict>>>;
297
+ }, z.core.$strict>>;
298
+ requires: z.ZodArray<z.ZodObject<{
299
+ name: z.ZodString;
300
+ phases: z.ZodArray<z.ZodEnum<{
301
+ release: "release";
302
+ start: "start";
303
+ }>>;
304
+ }, z.core.$strict>>;
305
+ release: z.ZodObject<{
306
+ migrations: z.ZodOptional<z.ZodObject<{
307
+ engine: z.ZodString;
308
+ root: z.ZodString;
309
+ lockfile: z.ZodString;
310
+ }, z.core.$strict>>;
311
+ }, z.core.$strict>;
312
+ env: z.ZodObject<{
313
+ variables: z.ZodArray<z.ZodObject<{
314
+ name: z.ZodString;
315
+ shape: z.ZodEnum<{
316
+ boolean: "boolean";
317
+ enum: "enum";
318
+ integer: "integer";
319
+ string: "string";
320
+ url: "url";
321
+ }>;
322
+ required: z.ZodBoolean;
323
+ members: z.ZodOptional<z.ZodArray<z.ZodString>>;
324
+ }, z.core.$strict>>;
325
+ }, z.core.$strict>;
326
+ }, z.core.$strict>;
327
+ export type ProjectDeclaration = z.infer<typeof ProjectDeclarationSchema>;
328
+ export type ProjectRole = z.infer<typeof ProjectRoleSchema>;
329
+ export type ProjectCommand = z.infer<typeof ProjectCommandSchema>;
330
+ export type ProjectRoleCommand = z.infer<typeof ProjectRoleCommandSchema>;
331
+ export type ProjectEnvVariable = z.infer<typeof ProjectEnvVariableSchema>;
332
+ export type ProjectIdentity = z.infer<typeof ProjectIdentitySchema>;
333
+ /**
334
+ * Parse a declaration, refusing an unknown schema version **before** any field
335
+ * is read.
336
+ *
337
+ * Order matters. The object is strict, so a newer declaration would otherwise
338
+ * report as a list of unrecognised keys — which reads like a broken file rather
339
+ * than a version this build is too old to serve. The version check is
340
+ * fail-closed: an unrecognised version is refused, never assumed compatible.
341
+ */
342
+ export declare function parseProjectDeclaration(source: unknown): ProjectDeclaration;
343
+ /** The role with this name, or `undefined`. */
344
+ export declare function findProjectRole(declaration: ProjectDeclaration, name: string): ProjectRole | undefined;
345
+ //# sourceMappingURL=declaration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"declaration.d.ts","sourceRoot":"","sources":["../src/declaration.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;GAOG;AACH,eAAO,MAAM,kCAAkC,IAAI,CAAC;AAoBpD,+EAA+E;AAC/E,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAK/D;AAUD;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,aAO3B,CAAC;AAEJ;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,uCAQlC,CAAC;AAEJ,yEAAyE;AACzE,eAAO,MAAM,qBAAqB;;;;;kBAOvB,CAAC;AAEZ;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,aAW9B,CAAC;AAEJ,+EAA+E;AAC/E,eAAO,MAAM,qBAAqB,aAEiD,CAAC;AAEpF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,qBAAqB;;;;kBAavB,CAAC;AAEZ,kDAAkD;AAClD,eAAO,MAAM,oBAAoB;;;EAAwC,CAAC;AAoG1E,eAAO,MAAM,oBAAoB;;;kBAKtB,CAAC;AAEZ;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB;;;kBAMpC,CAAC;AAEF,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;kBA0BnB,CAAC;AAEZ;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,uBAAuB;;;;kBAezB,CAAC;AAEZ,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE,gFAAgF;AAChF,eAAO,MAAM,kBAAkB;;;;;;;;;;;kBAciD,CAAC;AAEjF,0CAA0C;AAC1C,eAAO,MAAM,6BAA6B;;;EAA+B,CAAC;AAE1E;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB;;;;;;kBAK1B,CAAC;AAEZ;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,uBAAuB;;;;kBAMzB,CAAC;AAEZ,2EAA2E;AAC3E,eAAO,MAAM,oBAAoB;;;;;;kBAEtB,CAAC;AAEZ,0EAA0E;AAC1E,eAAO,MAAM,qBAAqB;;;;;;EAA0D,CAAC;AAE7F;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB;;;;;;;;;;;kBAclC,CAAC;AAEJ,6EAA6E;AAC7E,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBA0DjC,CAAC;AAyCL,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAC1E,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAC5D,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAClE,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAC1E,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAC1E,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAKpE;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,OAAO,GAAG,kBAAkB,CAU3E;AAED,+CAA+C;AAC/C,wBAAgB,eAAe,CAC7B,WAAW,EAAE,kBAAkB,EAC/B,IAAI,EAAE,MAAM,GACX,WAAW,GAAG,SAAS,CAEzB"}
@@ -0,0 +1,183 @@
1
+ // src/declaration.ts
2
+ import { z } from "zod";
3
+ var PROJECT_DECLARATION_SCHEMA_VERSION = 1;
4
+ var MACHINE_PATTERNS = [
5
+ [/:\/\//, "an absolute address"],
6
+ [/^\/\//, "a protocol-relative host"],
7
+ [/^[~]/, "a home-relative path"],
8
+ [/^[A-Za-z]:[\\/]/, "a Windows drive path"],
9
+ [/\\/, "a Windows path separator"],
10
+ [/(?:^|[\s=:])\d{1,3}(?:\.\d{1,3}){3}(?![\d.])/, "an IP address"],
11
+ [/(?:^|[\s=])[A-Za-z][\w.-]*:\d{2,5}(?![\w.])/, "a host and port"]
12
+ ];
13
+ function namesAMachine(value) {
14
+ for (const [pattern, reason] of MACHINE_PATTERNS) {
15
+ if (pattern.test(value))
16
+ return reason;
17
+ }
18
+ return;
19
+ }
20
+ function refuseMachineNames(label) {
21
+ return (schema) => schema.refine((value) => namesAMachine(value) === undefined, {
22
+ error: (issue) => `${label} names a machine — ${namesAMachine(String(issue.input)) ?? "a value of the deployment"} is supplied by the deployment, not written in the code`
23
+ });
24
+ }
25
+ var ProjectSlugSchema = z.string().min(1).max(64).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, "Use lowercase letters, numbers and single hyphens (for example: talk-control)");
26
+ var ProjectDescriptionSchema = z.record(refuseMachineNames("A locale tag")(z.string().min(1)), refuseMachineNames("A description")(z.string().trim().min(1))).refine((value) => Object.keys(value).length > 0, "Describe the project in at least one locale");
27
+ var ProjectIdentitySchema = z.object({
28
+ slug: ProjectSlugSchema,
29
+ name: refuseMachineNames("A project name")(z.string().trim().min(1).max(80)),
30
+ version: z.string().regex(/^\d+\.\d+\.\d+$/, "Use a semantic version such as 0.1.0"),
31
+ description: ProjectDescriptionSchema
32
+ }).strict();
33
+ var RepositoryPathSchema = z.string().min(1).refine((value) => !value.startsWith("/"), "Use a path relative to the repository root").refine((value) => namesAMachine(value) === undefined, {
34
+ error: (issue) => `A path may not contain ${namesAMachine(String(issue.input)) ?? "a machine name"}`
35
+ }).refine((value) => !value.split("/").includes(".."), "A path may not climb out of the repository");
36
+ var BindingVariableSchema = z.string().regex(/^[A-Z][A-Z0-9_]*$/, "Name an environment variable, for example API_PORT");
37
+ var ProjectListenerSchema = z.object({
38
+ portVariable: BindingVariableSchema,
39
+ bindVariable: BindingVariableSchema,
40
+ readinessPath: refuseMachineNames("A readiness path")(z.string().startsWith("/", "Readiness is a path, for example /health"))
41
+ }).strict();
42
+ var ProjectRunModeSchema = z.enum(["development", "production"]);
43
+ var PROJECT_SCRIPT_LAUNCHERS = [
44
+ [/^(?:bun|npm|pnpm|yarn)$/, /^run$/],
45
+ [/^deno$/, /^task$/],
46
+ [/^(?:npx|bunx|pnpx)$/, null]
47
+ ];
48
+ function launchesAScript(executable, firstArgument) {
49
+ for (const [runner, verb] of PROJECT_SCRIPT_LAUNCHERS) {
50
+ if (!runner.test(executable))
51
+ continue;
52
+ if (verb === null)
53
+ return true;
54
+ if (firstArgument !== undefined && verb.test(firstArgument))
55
+ return true;
56
+ }
57
+ return false;
58
+ }
59
+ var PORT_FLAG = /^(?:-p|-{1,2}(?:port|listen))$/i;
60
+ var CommandPartSchema = refuseMachineNames("A command part")(z.string().min(1)).refine((value) => !value.startsWith("/"), "A command part may not be an absolute path — paths are relative to the source").refine((value) => !/^[^=\s]+=/.test(value), "A command part may not carry an inline value — write the flag and its value as separate arguments, so the value is checked like every other one");
61
+ var CommandArgumentsSchema = z.array(CommandPartSchema).refine((args) => args.every((value, index) => !(/^\d{1,5}$/.test(value) && PORT_FLAG.test(args[index - 1] ?? ""))), "A number after a port flag is a port — name the variable that carries it and let the role read it");
62
+ var ProjectCommandSchema = z.object({
63
+ executable: CommandPartSchema,
64
+ args: CommandArgumentsSchema
65
+ }).strict();
66
+ var ProjectRoleCommandSchema = ProjectCommandSchema.refine((value) => !launchesAScript(value.executable, value.args[0]), "Start the role process itself, not a script runner: a launcher between the supervisor and the role duplicates the shutdown signal and forces the drain").refine((value) => !value.args.includes("--filter"), "A workspace filter puts a launcher between the supervisor and the role, and the shutdown signal never reaches it");
67
+ var ProjectRoleSchema = z.object({
68
+ name: ProjectSlugSchema,
69
+ workingDirectory: RepositoryPathSchema.optional(),
70
+ commands: z.record(ProjectRunModeSchema, ProjectRoleCommandSchema),
71
+ listener: ProjectListenerSchema.optional(),
72
+ drainFloorMs: z.number().int().nonnegative()
73
+ }).strict();
74
+ var ProjectBuildInputSchema = z.object({
75
+ name: ProjectSlugSchema,
76
+ path: RepositoryPathSchema,
77
+ digest: z.string().regex(/^sha256:[0-9a-f]{64}$/, 'Use a lowercase sha256 digest, as "sha256:<64 hex>"')
78
+ }).strict();
79
+ var ProjectBuildSchema = z.object({
80
+ command: ProjectCommandSchema,
81
+ artifacts: z.array(RepositoryPathSchema).min(1),
82
+ inputs: z.array(ProjectBuildInputSchema).optional()
83
+ }).strict().refine((build) => {
84
+ const names = (build.inputs ?? []).map((input) => input.name);
85
+ return new Set(names).size === names.length;
86
+ }, "Two build inputs share a name — a failure could then name either of them");
87
+ var ProjectRequirementPhaseSchema = z.enum(["release", "start"]);
88
+ var ProjectRequirementSchema = z.object({
89
+ name: ProjectSlugSchema,
90
+ phases: z.array(ProjectRequirementPhaseSchema).min(1)
91
+ }).strict();
92
+ var ProjectMigrationsSchema = z.object({
93
+ engine: refuseMachineNames("A migration engine name")(z.string().min(1).max(64)),
94
+ root: RepositoryPathSchema,
95
+ lockfile: RepositoryPathSchema
96
+ }).strict();
97
+ var ProjectReleaseSchema = z.object({ migrations: ProjectMigrationsSchema.optional() }).strict();
98
+ var ProjectEnvShapeSchema = z.enum(["string", "integer", "boolean", "url", "enum"]);
99
+ var ProjectEnvVariableSchema = z.object({
100
+ name: BindingVariableSchema,
101
+ shape: ProjectEnvShapeSchema,
102
+ required: z.boolean(),
103
+ members: z.array(refuseMachineNames("An enum member")(z.string().min(1))).min(1).optional()
104
+ }).strict().refine((value) => value.shape === "enum" === (value.members !== undefined), "An enum variable lists its members; every other shape has none");
105
+ var ProjectDeclarationSchema = z.object({
106
+ schemaVersion: z.literal(PROJECT_DECLARATION_SCHEMA_VERSION),
107
+ kind: z.enum(["library", "application"]),
108
+ identity: ProjectIdentitySchema,
109
+ roles: z.array(ProjectRoleSchema),
110
+ build: ProjectBuildSchema.optional(),
111
+ requires: z.array(ProjectRequirementSchema),
112
+ release: ProjectReleaseSchema,
113
+ env: z.object({ variables: z.array(ProjectEnvVariableSchema) }).strict()
114
+ }).strict().refine((value) => value.kind === "application" ? value.roles.length > 0 : value.roles.length === 0, "An application declares at least one role; a library declares none").refine((value) => new Set(value.roles.map((role) => role.name)).size === value.roles.length, "Role names must be unique").refine((value) => new Set(value.requires.map((entry) => entry.name)).size === value.requires.length, "Name each requirement once and list its phases").refine((value) => value.requires.every((entry) => new Set(entry.phases).size === entry.phases.length), "List each phase of a requirement once").refine((value) => new Set(value.env.variables.map((entry) => entry.name)).size === value.env.variables.length, "Declare each environment variable once").refine((value) => listenerBindingProblem(value) === undefined, {
115
+ error: (issue) => listenerBindingProblem(issue.input) ?? "Listener bindings are inconsistent"
116
+ });
117
+ function isListenerBindingSubject(value) {
118
+ return typeof value === "object" && value !== null && "roles" in value && "env" in value;
119
+ }
120
+ function listenerBindingProblem(value) {
121
+ if (!isListenerBindingSubject(value))
122
+ return;
123
+ const shapes = new Map(value.env.variables.map((entry) => [entry.name, entry.shape]));
124
+ for (const role of value.roles) {
125
+ const listener = role.listener;
126
+ if (!listener)
127
+ continue;
128
+ if (listener.portVariable === listener.bindVariable) {
129
+ return `Role "${role.name}" points its port and its bind address at the same variable "${listener.portVariable}"`;
130
+ }
131
+ const expected = [
132
+ [listener.portVariable, "integer"],
133
+ [listener.bindVariable, "string"]
134
+ ];
135
+ for (const [name, shape] of expected) {
136
+ const declared = shapes.get(name);
137
+ if (declared === undefined) {
138
+ return `Role "${role.name}" listens on "${name}", which env.variables does not declare — a deployment reading this cannot know it has to supply it`;
139
+ }
140
+ if (declared !== shape) {
141
+ return `Role "${role.name}" listens on "${name}", declared as "${declared}" where a ${shape} is needed`;
142
+ }
143
+ }
144
+ }
145
+ return;
146
+ }
147
+ var VersionProbeSchema = z.object({ schemaVersion: z.unknown() }).loose();
148
+ function parseProjectDeclaration(source) {
149
+ const probe = VersionProbeSchema.safeParse(source);
150
+ const declared = probe.success ? probe.data.schemaVersion : undefined;
151
+ if (declared !== undefined && declared !== PROJECT_DECLARATION_SCHEMA_VERSION) {
152
+ throw new Error(`Project declaration schema version ${JSON.stringify(declared)} is not supported — ` + `this build understands version ${PROJECT_DECLARATION_SCHEMA_VERSION}.`);
153
+ }
154
+ return ProjectDeclarationSchema.parse(source);
155
+ }
156
+ function findProjectRole(declaration, name) {
157
+ return declaration.roles.find((role) => role.name === name);
158
+ }
159
+ export {
160
+ BindingVariableSchema,
161
+ PROJECT_DECLARATION_SCHEMA_VERSION,
162
+ ProjectBuildInputSchema,
163
+ ProjectBuildSchema,
164
+ ProjectCommandSchema,
165
+ ProjectDeclarationSchema,
166
+ ProjectDescriptionSchema,
167
+ ProjectEnvShapeSchema,
168
+ ProjectEnvVariableSchema,
169
+ ProjectIdentitySchema,
170
+ ProjectListenerSchema,
171
+ ProjectMigrationsSchema,
172
+ ProjectReleaseSchema,
173
+ ProjectRequirementPhaseSchema,
174
+ ProjectRequirementSchema,
175
+ ProjectRoleCommandSchema,
176
+ ProjectRoleSchema,
177
+ ProjectRunModeSchema,
178
+ ProjectSlugSchema,
179
+ RepositoryPathSchema,
180
+ findProjectRole,
181
+ namesAMachine,
182
+ parseProjectDeclaration
183
+ };