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.
- package/README.md +7 -3
- package/dist/agent-runtime/admission-lanes.d.ts +35 -0
- package/dist/agent-runtime/admission-lanes.d.ts.map +1 -0
- package/dist/agent-runtime/coordinator.d.ts +36 -2
- package/dist/agent-runtime/coordinator.d.ts.map +1 -1
- package/dist/agent-runtime/events.d.ts +7 -0
- package/dist/agent-runtime/events.d.ts.map +1 -1
- package/dist/agent-runtime/history.d.ts +7 -0
- package/dist/agent-runtime/history.d.ts.map +1 -1
- package/dist/agent-runtime/models.d.ts +2 -3
- package/dist/agent-runtime/models.d.ts.map +1 -1
- package/dist/agent-runtime/observability.d.ts.map +1 -1
- package/dist/agent-runtime/run-execution.d.ts +31 -0
- package/dist/agent-runtime/run-execution.d.ts.map +1 -0
- package/dist/agent-runtime/runtime-internals.d.ts +25 -0
- package/dist/agent-runtime/runtime-internals.d.ts.map +1 -0
- package/dist/agent-runtime/runtime-result.d.ts +11 -0
- package/dist/agent-runtime/runtime-result.d.ts.map +1 -0
- package/dist/agent-runtime/runtime.d.ts +16 -15
- package/dist/agent-runtime/runtime.d.ts.map +1 -1
- package/dist/agent-runtime/schemas.d.ts.map +1 -1
- package/dist/agent-runtime/store-driver.d.ts.map +1 -1
- package/dist/agent-runtime/store.d.ts +9 -2
- package/dist/agent-runtime/store.d.ts.map +1 -1
- package/dist/agent-runtime/terminal-commit.d.ts.map +1 -1
- package/dist/agent-runtime/terminal-status.d.ts +13 -0
- package/dist/agent-runtime/terminal-status.d.ts.map +1 -0
- package/dist/agent-runtime.d.ts +5 -3
- package/dist/agent-runtime.d.ts.map +1 -1
- package/dist/agent-runtime.js +211 -156
- package/dist/application/activity.d.ts +7 -2
- package/dist/application/activity.d.ts.map +1 -1
- package/dist/application/grammy.d.ts +6 -0
- package/dist/application/grammy.d.ts.map +1 -1
- package/dist/application/health.d.ts +9 -2
- package/dist/application/health.d.ts.map +1 -1
- package/dist/application/kernel.d.ts +49 -3
- package/dist/application/kernel.d.ts.map +1 -1
- package/dist/application/schemas.d.ts +42 -0
- package/dist/application/schemas.d.ts.map +1 -1
- package/dist/application-opentelemetry.js +2 -2
- package/dist/application.d.ts +3 -3
- package/dist/application.d.ts.map +1 -1
- package/dist/application.js +16 -8
- package/dist/cli.js +6 -6
- package/dist/contract/errors.d.ts +6 -0
- package/dist/contract/errors.d.ts.map +1 -1
- package/dist/contract/index.js +1 -1
- package/dist/declaration.d.ts +345 -0
- package/dist/declaration.d.ts.map +1 -0
- package/dist/declaration.js +183 -0
- package/dist/{index-5c8n2123.js → index-1zencgcb.js} +64 -30
- package/dist/{index-sa2mbwa7.js → index-2akar0rq.js} +2 -2
- package/dist/{index-41wm56v0.js → index-3hn10n4c.js} +1 -1
- package/dist/{index-f6pymtqe.js → index-3w74r08v.js} +2 -2
- package/dist/{index-7c0gkyvj.js → index-6fbp58g8.js} +2 -2
- package/dist/{index-gnvzyw0k.js → index-8es1hrv4.js} +2 -2
- package/dist/{index-mgf6cdkm.js → index-8eywc9zv.js} +35 -1
- package/dist/{index-3nwng7hs.js → index-8pc5s3pz.js} +4 -4
- package/dist/{index-28cqssm3.js → index-9h1vba8n.js} +6 -0
- package/dist/{index-0nc0cddp.js → index-a59da114.js} +17 -28
- package/dist/{index-s8w2y7kr.js → index-f7521k0x.js} +22 -16
- package/dist/{index-y2rb7dwx.js → index-pzyt11ch.js} +5 -1
- package/dist/{index-mr617n62.js → index-svqhcrrj.js} +1 -1
- package/dist/{index-ps5pxp3h.js → index-v6e8hnkx.js} +7 -1
- package/dist/{index-1tkyng3g.js → index-vkk06pv1.js} +1 -9
- package/dist/{index-2nzpcfbp.js → index-x8v8b7rc.js} +2 -2
- package/dist/index.js +1 -1
- package/dist/internal/observability-sink.d.ts.map +1 -1
- package/dist/internal/route-pattern.d.ts +9 -0
- package/dist/internal/route-pattern.d.ts.map +1 -1
- package/dist/node.d.ts +1 -1
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +5 -5
- package/dist/observability/index.js +5 -5
- package/dist/observability/sanitize.d.ts +3 -1
- package/dist/observability/sanitize.d.ts.map +1 -1
- package/dist/remote.js +3 -3
- package/dist/server/error-hook.d.ts +10 -3
- package/dist/server/error-hook.d.ts.map +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +9 -12
- package/dist/server/middleware/cors.d.ts +5 -2
- package/dist/server/middleware/cors.d.ts.map +1 -1
- package/dist/server/openapi.d.ts.map +1 -1
- package/dist/server/router.d.ts.map +1 -1
- package/dist/server/socket-io-config.d.ts +68 -3
- package/dist/server/socket-io-config.d.ts.map +1 -1
- package/dist/server/socket-io-node.d.ts +1 -1
- package/dist/server/socket-io-node.d.ts.map +1 -1
- package/dist/server/socket-io.d.ts +1 -1
- package/dist/server/socket-io.d.ts.map +1 -1
- package/dist/testing/surface-manifest.d.ts.map +1 -1
- package/dist/testing.js +8 -10
- package/dist/tools/flatten-join.d.ts +2 -0
- package/dist/tools/flatten-join.d.ts.map +1 -1
- package/dist/tools/flatten.d.ts.map +1 -1
- package/dist/tools/mcp-prepare.d.ts +0 -2
- package/dist/tools/mcp-prepare.d.ts.map +1 -1
- package/dist/tools/runtime-tool.d.ts +0 -3
- package/dist/tools/runtime-tool.d.ts.map +1 -1
- package/dist/tools/schema.d.ts +0 -4
- package/dist/tools/schema.d.ts.map +1 -1
- package/dist/tools.js +9 -9
- package/llms-full.txt +924 -68
- package/llms.txt +1 -0
- 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
|
+
};
|