@fabrika/provider-zerops 0.8.1 → 0.9.0-dev.2

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 (102) hide show
  1. package/package.json +15 -14
  2. package/src/api/client.ts +6 -0
  3. package/src/api/types.ts +12 -0
  4. package/src/control.ts +26 -0
  5. package/src/execution.ts +147 -14
  6. package/src/installation/generated/platform-proxy-manifest.ts +20 -0
  7. package/src/manifest.ts +9 -5
  8. package/src/namespace/provisioning.ts +4 -2
  9. package/src/namespace/proxy-attempt.ts +29 -0
  10. package/src/plan.ts +1 -1
  11. package/src/provider.ts +40 -0
  12. package/types/src/api/client.d.ts +5 -0
  13. package/types/src/api/errors.d.ts +24 -0
  14. package/types/src/api/polling.d.ts +41 -0
  15. package/types/src/api/readers.d.ts +977 -0
  16. package/types/src/api/service-env-limit.d.ts +17 -0
  17. package/types/src/api/service-env-response.d.ts +23 -0
  18. package/types/src/api/types.d.ts +638 -0
  19. package/types/src/api.d.ts +6 -0
  20. package/types/src/authoring.d.ts +9 -0
  21. package/types/src/cli-args.d.ts +19 -0
  22. package/types/src/cli.d.ts +10 -0
  23. package/types/src/collaborators.d.ts +18 -0
  24. package/types/src/compile.d.ts +91 -0
  25. package/types/src/control.d.ts +41 -0
  26. package/types/src/database-budget.d.ts +38 -0
  27. package/types/src/descriptor.d.ts +28 -0
  28. package/types/src/execution.d.ts +137 -0
  29. package/types/src/exposure.d.ts +47 -0
  30. package/types/src/index.d.ts +31 -0
  31. package/types/src/installation/admin-options.d.ts +24 -0
  32. package/types/src/installation/admin.d.ts +35 -0
  33. package/types/src/installation/checker-release.d.ts +12 -0
  34. package/types/src/installation/deploy-options.d.ts +37 -0
  35. package/types/src/installation/deploy.d.ts +96 -0
  36. package/types/src/installation/generated/platform-console-schema.d.ts +2 -0
  37. package/types/src/installation/generated/platform-proxy-manifest.d.ts +2 -0
  38. package/types/src/installation/hosts.d.ts +38 -0
  39. package/types/src/installation/index.d.ts +9 -0
  40. package/types/src/installation/init-options.d.ts +38 -0
  41. package/types/src/installation/init.d.ts +60 -0
  42. package/types/src/installation/install-collaborators.d.ts +24 -0
  43. package/types/src/installation/install-environment.d.ts +70 -0
  44. package/types/src/installation/install-options.d.ts +49 -0
  45. package/types/src/installation/install-project.d.ts +13 -0
  46. package/types/src/installation/install-provisioning.d.ts +58 -0
  47. package/types/src/installation/install-secrets.d.ts +30 -0
  48. package/types/src/installation/install-token.d.ts +10 -0
  49. package/types/src/installation/install.d.ts +23 -0
  50. package/types/src/installation/installation-source.d.ts +8 -0
  51. package/types/src/installation/log.d.ts +44 -0
  52. package/types/src/installation/machine-key.d.ts +19 -0
  53. package/types/src/installation/mcp.d.ts +18 -0
  54. package/types/src/installation/placement-options.d.ts +11 -0
  55. package/types/src/installation/platform-archive.d.ts +7 -0
  56. package/types/src/installation/platform-source.d.ts +8 -0
  57. package/types/src/installation/platform-upload.d.ts +3 -0
  58. package/types/src/installation/proxy-manifest.d.ts +105 -0
  59. package/types/src/installation/secrets.d.ts +17 -0
  60. package/types/src/installation/sidecar.d.ts +51 -0
  61. package/types/src/installation/source-options.d.ts +1 -0
  62. package/types/src/installation/topology/artifacts.d.ts +8 -0
  63. package/types/src/installation/topology/index.d.ts +5 -0
  64. package/types/src/installation/topology/installation-schemas.d.ts +2242 -0
  65. package/types/src/installation/topology/invariants.d.ts +23 -0
  66. package/types/src/installation/topology/setups.d.ts +58 -0
  67. package/types/src/installation/topology/topology.d.ts +148 -0
  68. package/types/src/installation/topology/validate.d.ts +11 -0
  69. package/types/src/installation/upgrade-options.d.ts +26 -0
  70. package/types/src/installation/upgrade-output.d.ts +6 -0
  71. package/types/src/installation/upgrade.d.ts +88 -0
  72. package/types/src/managed-env.d.ts +57 -0
  73. package/types/src/manifest.d.ts +206 -0
  74. package/types/src/namespace/claims.d.ts +3 -0
  75. package/types/src/namespace/codec.d.ts +334 -0
  76. package/types/src/namespace/constants.d.ts +25 -0
  77. package/types/src/namespace/errors.d.ts +15 -0
  78. package/types/src/namespace/manifest.d.ts +4 -0
  79. package/types/src/namespace/operator.d.ts +12 -0
  80. package/types/src/namespace/provisioning.d.ts +4 -0
  81. package/types/src/namespace/proxy-attempt.d.ts +36 -0
  82. package/types/src/namespace/proxy-reads.d.ts +33 -0
  83. package/types/src/namespace/proxy-secrets.d.ts +8 -0
  84. package/types/src/namespace/proxy-source.d.ts +74 -0
  85. package/types/src/namespace/topology.d.ts +9 -0
  86. package/types/src/namespace/types.d.ts +99 -0
  87. package/types/src/namespace/update.d.ts +6 -0
  88. package/types/src/namespace/validation.d.ts +12 -0
  89. package/types/src/namespace.d.ts +9 -0
  90. package/types/src/plan.d.ts +12 -0
  91. package/types/src/provider.d.ts +65 -0
  92. package/types/src/proxy-manifest-carrier.d.ts +16 -0
  93. package/types/src/schema.generated.d.ts +376 -0
  94. package/types/src/service-names.d.ts +4 -0
  95. package/types/src/service-references.d.ts +13 -0
  96. package/types/src/source/constants.d.ts +6 -0
  97. package/types/src/source/port.d.ts +60 -0
  98. package/types/src/source/types.d.ts +4 -0
  99. package/types/src/source/validation.d.ts +19 -0
  100. package/types/src/source.d.ts +2 -0
  101. package/types/src/types.d.ts +121 -0
  102. package/types/src/version-attempt.d.ts +8 -0
@@ -0,0 +1,638 @@
1
+ import type { Sleeper } from '../collaborators';
2
+ /** Every status a Zerops application version can be in. VERIFIED: `OutDtoGetAppVersion.status` enum. */
3
+ export type ZeropsAppVersionStatus = 'UPLOADING' | 'WAITING_TO_BUILD' | 'BUILDING' | 'BUILD_FAILED' | 'BUILD_VALIDATION_FAILED' | 'WAITING_TO_DEPLOY' | 'DEPLOYING' | 'DEPLOY_FAILED' | 'PREPARING_RUNTIME' | 'PREPARING_RUNTIME_FAILED' | 'ACTIVE' | 'BACKUP' | 'CANCELLED';
4
+ /** The one status that means "this version is live". */
5
+ export declare const ZEROPS_ACTIVE: ZeropsAppVersionStatus;
6
+ /**
7
+ * Statuses a version never leaves. `BACKUP` is included deliberately: a version we triggered that has
8
+ * become an archived one was superseded by another deploy, which is an outcome, not a step on the way.
9
+ */
10
+ export declare const ZEROPS_TERMINAL: ReadonlySet<ZeropsAppVersionStatus>;
11
+ /** One application version, narrowed to what the driver reads. VERIFIED: `GET /app-version/{id}`. */
12
+ export interface ZeropsAppVersion {
13
+ id: string;
14
+ /** Absent on a response that omitted it; the poll loop treats that as "not yet known". */
15
+ status?: ZeropsAppVersionStatus;
16
+ serviceStackId?: string;
17
+ projectId?: string;
18
+ /** Monotonic per service — how Zerops orders the 10 versions it retains. */
19
+ sequence?: number;
20
+ /** Build-pipeline timestamps (ISO dateTime), when a build ran. */
21
+ build?: {
22
+ startDate?: string;
23
+ endDate?: string;
24
+ pipelineStart?: string;
25
+ pipelineFinish?: string;
26
+ pipelineFailed?: string;
27
+ };
28
+ }
29
+ /** The credential-bearing result of reserving one upload-backed application version. */
30
+ export interface ZeropsAppVersionUpload {
31
+ id: string;
32
+ /** Presigned object-storage destination. Never log it or include it in an error. */
33
+ uploadUrl: string;
34
+ }
35
+ /** An asynchronous platform operation. VERIFIED: `OutDtoProcess`. */
36
+ export interface ZeropsProcess {
37
+ id: string;
38
+ /** Free-form on the wire (`OutDtoProcess.status` is an untyped string defaulting to `PENDING`). */
39
+ status?: string;
40
+ actionName?: string;
41
+ serviceStackId?: string;
42
+ appVersionId?: string;
43
+ }
44
+ /**
45
+ * The status a process that succeeded carries.
46
+ *
47
+ * VERIFIED AS A VALUE, not as an enum member: `docs/reference/zerops-platform.md` records
48
+ * `stack.updateUserData`, `stack.enableSubdomainAccess` and an import's processes reaching exactly this
49
+ * string live. `ResponseProcess.status` is an untyped string in the published document, so the platform
50
+ * declares no set anywhere.
51
+ */
52
+ export declare const ZEROPS_PROCESS_FINISHED = "FINISHED";
53
+ /**
54
+ * Statuses a process never leaves.
55
+ *
56
+ * UNVERIFIED AS A CLOSED SET — the OpenAPI document gives `ResponseProcess.status` no enum at all. These
57
+ * are the values `zops process list --status` documents (`PENDING, RUNNING, FINISHED, FAILED, CANCELED`)
58
+ * minus the two that mean "still going". Note the single `L` in `CANCELED`: an app VERSION spells the same
59
+ * idea `CANCELLED`, and the two vocabularies are not the same one.
60
+ *
61
+ * `waitForProcess` treats everything not in here as "keep waiting", so a status this build has never seen
62
+ * can only ever cost time — it can never be read as success.
63
+ */
64
+ export declare const ZEROPS_PROCESS_TERMINAL: ReadonlySet<string>;
65
+ /** One service created or updated by an import. VERIFIED: `OutDtoProjectImportServiceStack`. */
66
+ export interface ZeropsImportedService {
67
+ id: string;
68
+ /** The service's name as Zerops stores it (hostname, possibly suffixed by env). */
69
+ name: string;
70
+ processes: ZeropsProcess[];
71
+ }
72
+ /** The result of applying an import document. VERIFIED: `ResponseProjectImport`. */
73
+ export interface ZeropsImportResult {
74
+ projectId: string;
75
+ projectName?: string;
76
+ services: ZeropsImportedService[];
77
+ }
78
+ /** Every documented project lifecycle state. VERIFIED: `ResponseProject.status`. */
79
+ export type ZeropsProjectStatus = 'NEW' | 'CREATING' | 'ACTIVE' | 'DELETING' | 'FAILED' | 'STOPPING' | 'STOPPED' | 'STARTING';
80
+ /** Project compute tier. The import schema calls the same choice `corePackage`. VERIFIED: `ResponseProject.mode`. */
81
+ export type ZeropsProjectMode = 'LIGHT' | 'SERIOUS';
82
+ /** Every documented service-stack lifecycle state. VERIFIED: `ResponseServiceStack.status`. */
83
+ export type ZeropsServiceStatus = 'NEW' | 'CREATING' | 'ACTIVE' | 'STOPPING' | 'STOPPED' | 'STARTING' | 'RESTARTING' | 'RELOADING' | 'DELETING' | 'DELETED' | 'FAILED' | 'ACTION_FAILED' | 'UPGRADING' | 'READY_TO_DEPLOY' | 'SERVICE_CREATING' | 'SERVICE_ACTIVE' | 'SERVICE_STOPPING' | 'SERVICE_STOPPED' | 'SERVICE_STARTING' | 'SERVICE_RESTARTING' | 'SERVICE_RELOADING' | 'SERVICE_DELETING' | 'SERVICE_DELETED' | 'SERVICE_FAILED' | 'SERVICE_ACTION_FAILED' | 'SERVICE_REPAIRING' | 'SERVICE_CONTAINER_FAILED' | 'SERVICE_MOVING_CONTAINER' | 'SERVICE_UPGRADING' | 'SERVICE_SCALING' | 'SERVICE_REPAIR_FAILED' | 'REPAIRING' | 'CONTAINER_FAILED' | 'MOVING_CONTAINER' | 'SCALING' | 'REPAIR_FAILED';
84
+ /** One bound of vertical autoscaling. `null` inherits the service type's value. */
85
+ export interface ZeropsScalingResource {
86
+ cpuCoreCount: number | null;
87
+ memoryGBytes: number | null;
88
+ diskGBytes: number | null;
89
+ }
90
+ /** The free capacity below which vertical autoscaling scales up. `null` inherits the service type's value. */
91
+ export interface ZeropsScalingFreeResource {
92
+ cpuCoreCount: number | null;
93
+ cpuCorePercent: number | null;
94
+ memoryGBytes: number | null;
95
+ memoryPercent: number | null;
96
+ }
97
+ /**
98
+ * A service's autoscaling, as `ResponseServiceStack.customAutoscaling` (what was set) and `.currentAutoscaling` (what
99
+ * applies) carry it, every field nullable. VERIFIED live 2026-10-15 (`docs/reference/zerops-platform.md`).
100
+ */
101
+ export interface ZeropsServiceAutoscaling {
102
+ vertical: {
103
+ cpuMode: 'DEDICATED' | 'SHARED' | null;
104
+ minResource: ZeropsScalingResource;
105
+ maxResource: ZeropsScalingResource;
106
+ minFreeResource: ZeropsScalingFreeResource;
107
+ startCpuCoreCount: number | null;
108
+ swapEnabled: boolean | null;
109
+ };
110
+ horizontal: {
111
+ minContainerCount: number | null;
112
+ maxContainerCount: number | null;
113
+ };
114
+ }
115
+ /** One service. VERIFIED: `ResponseServiceStack` (narrowed to the fields fabrika reads). */
116
+ export interface ZeropsService {
117
+ id: string;
118
+ name: string;
119
+ projectId?: string;
120
+ status?: ZeropsServiceStatus;
121
+ /** Full service-type identifier, e.g. `alpine/bun@1.3`. VERIFIED: `ResponseServiceStack.base`. */
122
+ base?: string;
123
+ activeAppVersionId?: string;
124
+ /** Whether Zerops' public subdomain routes this service. VERIFIED: `ResponseServiceStack.subdomainAccess`. */
125
+ subdomainAccess?: boolean;
126
+ /** Provider profile identifier, e.g. `oltp-production`. VERIFIED: `ResponseServiceStack.autoscalingProfileId`. */
127
+ autoscalingProfileId?: string;
128
+ /** The autoscaling set on the service; absent when the platform sent none or one this client cannot read whole. */
129
+ customAutoscaling?: ZeropsServiceAutoscaling;
130
+ /** The autoscaling that applies, the service type's values included; absent like `customAutoscaling`. */
131
+ currentAutoscaling?: ZeropsServiceAutoscaling;
132
+ }
133
+ /** One project. VERIFIED: `ResponseProject` (narrowed). */
134
+ export interface ZeropsProject {
135
+ id: string;
136
+ name: string;
137
+ status?: ZeropsProjectStatus;
138
+ /** The wire calls the import schema's `corePackage` choice `mode`. */
139
+ mode?: ZeropsProjectMode;
140
+ /** VERIFIED: `ResponseProject.description`. */
141
+ description?: string;
142
+ /** VERIFIED: `ResponseProject.tagList`. */
143
+ tagList?: string[];
144
+ }
145
+ /**
146
+ * A Zerops access role. VERIFIED: the `roleCode` enum, shared by `RequestClientIntegrationToken` and
147
+ * `ResponseClientIntegrationTokenRaw`.
148
+ *
149
+ * Tiered, and the schema's own description states the order: `OWNER > ADMIN > BASIC_USER > READ_ONLY >
150
+ * NO_ACCESS`, with a PROJECT role overriding the client-wide one. That override is the whole mechanism
151
+ * behind a scoped token — client `NO_ACCESS` plus one project `ADMIN` reaches exactly one project.
152
+ */
153
+ export type ZeropsRoleCode = 'OWNER' | 'ADMIN' | 'BASIC_USER' | 'READ_ONLY' | 'NO_ACCESS';
154
+ /**
155
+ * One project grant carried by an integration token. VERIFIED:
156
+ * `ResponseClientIntegrationTokenRaw.projects[]` — `projectId` is required and `roleCode` is not, so a
157
+ * grant read back can omit it (the schema documents `NO_ACCESS` as the default; this client does not
158
+ * substitute one, because an assumed grant is worse than an absent one).
159
+ */
160
+ export interface ZeropsTokenProjectGrant {
161
+ projectId: string;
162
+ roleCode?: ZeropsRoleCode;
163
+ }
164
+ /**
165
+ * A freshly minted integration token. VERIFIED: `ResponseClientIntegrationTokenRaw` — `id`, `name`,
166
+ * `projects` and `token` are all in its `required` list.
167
+ *
168
+ * THE ONE SHAPE ON THIS CLIENT THAT CARRIES A CREDENTIAL, and the `Raw` in the schema's name is why: the
169
+ * plaintext `token` appears in this one response and nowhere afterwards. Everything the module header says
170
+ * about the personal access token applies to it — never logged, never in an error message — with the
171
+ * single exception that it must reach whatever is going to store it.
172
+ */
173
+ export interface ZeropsIntegrationToken {
174
+ id: string;
175
+ name: string;
176
+ /** The bearer value. NEVER log it. */
177
+ token: string;
178
+ /** The grants the platform actually recorded, so a caller can check the scope it asked for. */
179
+ projects: ZeropsTokenProjectGrant[];
180
+ /** The client-wide role. Absent from the response's `required` list, so it can genuinely be missing. */
181
+ roleCode?: ZeropsRoleCode;
182
+ }
183
+ /**
184
+ * The identity a token authenticates as, narrowed to what fabrika reads. VERIFIED: `GET /user/info`,
185
+ * `ResponseUserAuthorize` — `id` and `email` are in its `required` list.
186
+ *
187
+ * Read with an integration token, `id` is the token's own id (measured live, 2026-08-21) and `email` is
188
+ * `token-<id>@zerops.io` (reported by an operator, not measured here).
189
+ */
190
+ export interface ZeropsUserInfo {
191
+ id: string;
192
+ email: string;
193
+ }
194
+ /**
195
+ * ONE service-level environment variable. VERIFIED: `OutDtoUserData`.
196
+ *
197
+ * A SENSITIVE record carries no `content`. Measured 2026-10-15: Zerops answers `REDACTED` to a token
198
+ * outside sudo mode and the value itself to one inside it, so nothing may depend on reading a secret
199
+ * back, and the reader drops it. Never log `content`.
200
+ */
201
+ export type ZeropsServiceEnv = ZeropsServiceEnvRecord & ({
202
+ sensitive: false;
203
+ content: string;
204
+ } | {
205
+ sensitive: true;
206
+ });
207
+ export interface ZeropsServiceEnvRecord {
208
+ id: string;
209
+ key: string;
210
+ serviceStackId?: string;
211
+ /** Record classification, not a sensitivity or ownership guarantee; sensitive records can be USER. */
212
+ type?: string;
213
+ }
214
+ /** One service-level variable to write. */
215
+ export interface ZeropsServiceEnvWrite {
216
+ serviceId: string;
217
+ key: string;
218
+ value: string;
219
+ /**
220
+ * `true` for a credential, `false` for everything else. A sensitive value can never be read back
221
+ * (see `ZeropsServiceEnv`), so a value fabrika compares or merges later must be written `false`.
222
+ */
223
+ sensitive: boolean;
224
+ signal: AbortSignal;
225
+ }
226
+ /**
227
+ * A short-lived grant for the project's LOG service. VERIFIED: `ResponseProjectLog` — the field names and
228
+ * types are exactly these. What the URLs speak was measured separately; see `readBuildLog`.
229
+ */
230
+ export interface ZeropsLogAccess {
231
+ url: string;
232
+ urlPlain: string;
233
+ urlInfo: string;
234
+ urlUi: string;
235
+ /** Bearer for the log service (a different credential from the personal access token). Never log it. */
236
+ accessToken: string;
237
+ /** ISO dateTime after which the grant is dead and must be re-fetched. */
238
+ expiration: string;
239
+ }
240
+ /** One relayed build/runtime log line. Shape is fabrika's, not the platform's. */
241
+ export interface ZeropsLogLine {
242
+ /** RFC3339 stamp Zerops put at the head of the line, when the line carried one. */
243
+ timestamp?: string;
244
+ /** The line as the log service wrote it, timestamp and tag included. */
245
+ message: string;
246
+ }
247
+ /**
248
+ * The Zerops REST surface fabrika depends on, as ONE injectable interface (ADR-0011's collaborator
249
+ * bundle). Every method takes the run's `AbortSignal`: a driver that polls `/app-version` is precisely the
250
+ * case cancellation was added for, and an abandoned run must not keep an HTTP request alive.
251
+ *
252
+ * WIDER THAN THE DRIVER, on purpose. The driver uses four of these; the rest are the CONTROL PLANE's
253
+ * Zerops surface — the service-env methods for the edit-time secret writes that ADR-0004 says are not a
254
+ * deploy step, `importProject` for creating a managed project, and `getAppVersion`/`latestAppVersion` for
255
+ * the startup reconciliation of in-flight runs that ADR-0003 makes a requirement. One client, so there is
256
+ * one place the Zerops contract is written down.
257
+ *
258
+ * NOTE WHAT IS ABSENT. There is no method that writes a PROJECT-level environment variable. That is not
259
+ * an oversight and must not be "completed": project-level variables are injected into every service in the
260
+ * project, and one project holds many apps, so a project-level write hands app A's credentials to app B
261
+ * (ADR-0004). The invariant is enforced by the surface not existing — `POST /project/{id}/env` and
262
+ * `PUT /project-env/{id}` are real endpoints that this client deliberately does not expose. `getProjectEnv`
263
+ * reads one back (for drift reporting); nothing here can create or change one.
264
+ */
265
+ export interface ZeropsApi {
266
+ /**
267
+ * Apply a `zerops-import.yaml` (its `services:` section) to an EXISTING project — fabrika's
268
+ * provisioning step. VERIFIED: `POST /project/{id}/service-stack/import`, body `{ yaml }`.
269
+ *
270
+ * VERIFIED (2026-08-05): re-applying an unchanged document is a complete no-op — same ids, no
271
+ * processes, `lastUpdate` unmoved. That is ADR-0003's idempotency claim, and it is ALL of it: a
272
+ * changed `profile`/`maxContainers`/`objectStorageSize` is silently ignored too, so this reconciles
273
+ * nothing. Change a live field through the API that owns it.
274
+ */
275
+ importServices(input: {
276
+ projectId: string;
277
+ yaml: string;
278
+ signal: AbortSignal;
279
+ }): Promise<ZeropsImportResult>;
280
+ /**
281
+ * Apply a full `zerops-import.yaml` INCLUDING its `project:` section, creating the project.
282
+ * VERIFIED: `POST /client/{id}/project/import`, body `{ yaml }`.
283
+ *
284
+ * Needed because `envIsolation` is settable at project CREATION only — `RequestPutProject` has no such
285
+ * field, so a project made without it cannot be corrected through the project API afterwards. The
286
+ * driver compensates by also setting `envIsolation` per service, which the schema says overrides the
287
+ * project-level value.
288
+ */
289
+ importProject(input: {
290
+ clientId: string;
291
+ yaml: string;
292
+ signal: AbortSignal;
293
+ }): Promise<ZeropsImportResult>;
294
+ /**
295
+ * Trigger a build+deploy pipeline for a service. VERIFIED:
296
+ * `PUT /service-stack/{id}/trigger-pipeline`, body `RequestPutStandardServiceStackTriggerPipeline`.
297
+ *
298
+ * With no `buildFromGit` the platform builds from the service's configured Git integration (the
299
+ * private-repo path); with one it does a one-time build from that PUBLIC repository URL.
300
+ *
301
+ * UNVERIFIED BEHAVIOUR: the response is `{ process?: … }` with EVERY field optional, so whether the
302
+ * new app-version id is reliably reachable at `process.appVersion.id` is a guess. The driver therefore
303
+ * does not depend on it — it resolves the version it triggered via `latestAppVersion` instead.
304
+ */
305
+ triggerPipeline(input: {
306
+ serviceId: string;
307
+ /** Public Git repository URL for a one-time build. Omit to use the service's Git integration. */
308
+ buildFromGit?: string;
309
+ /** Inline `zerops.yaml`, overriding whatever is in the repo. */
310
+ zeropsYaml?: string;
311
+ /** Which `setup:` block of the `zerops.yaml` to use. */
312
+ zeropsSetup?: string;
313
+ signal: AbortSignal;
314
+ }): Promise<ZeropsProcess | null>;
315
+ /**
316
+ * Reserve an upload-backed application version. VERIFIED: `POST /service-stack/{id}/app-version`,
317
+ * optional body `{ name }`, response `ResponsePostAppVersion`.
318
+ */
319
+ createAppVersion(input: {
320
+ serviceId: string;
321
+ name?: string;
322
+ signal: AbortSignal;
323
+ }): Promise<ZeropsAppVersionUpload>;
324
+ /**
325
+ * Build and deploy an application version whose source archive has already been uploaded. VERIFIED:
326
+ * `PUT /app-version/{id}/build-and-deploy`, required body `{ zeropsYaml }` and optional
327
+ * `zeropsYamlSetup`.
328
+ */
329
+ buildAndDeployAppVersion(input: {
330
+ appVersionId: string;
331
+ zeropsYaml: string;
332
+ zeropsYamlSetup?: string;
333
+ signal: AbortSignal;
334
+ }): Promise<ZeropsProcess>;
335
+ /** Delete an application version, including an untriggered upload reservation. VERIFIED: `DELETE /app-version/{id}`. */
336
+ deleteAppVersion(input: {
337
+ appVersionId: string;
338
+ signal: AbortSignal;
339
+ }): Promise<void>;
340
+ /** One application version's current state — the poll target. VERIFIED: `GET /app-version/{id}`. */
341
+ getAppVersion(input: {
342
+ appVersionId: string;
343
+ signal: AbortSignal;
344
+ }): Promise<ZeropsAppVersion>;
345
+ /**
346
+ * The newest application version of a service. VERIFIED: `GET /service-stack/{id}/app-version`
347
+ * (`{ list, total }`, `limit`/`offset`/`statuses` query params).
348
+ *
349
+ * UNVERIFIED BEHAVIOUR: the list's ORDER. The driver does not rely on it — it picks the highest
350
+ * `sequence`, which the schema documents as monotonic per service.
351
+ */
352
+ latestAppVersion(input: {
353
+ serviceId: string;
354
+ signal: AbortSignal;
355
+ }): Promise<ZeropsAppVersion | null>;
356
+ /** Cancel an in-flight build — what a cancelled run does with the work it started. VERIFIED: `PUT /app-version/{id}/cancel-build`. */
357
+ cancelBuild(input: {
358
+ appVersionId: string;
359
+ signal: AbortSignal;
360
+ }): Promise<void>;
361
+ /**
362
+ * One platform operation's current state. VERIFIED: `GET /process/{id}` (`GetProcessDetails`,
363
+ * response `ResponseProcess`).
364
+ *
365
+ * WHAT IT IS FOR. Several writes here return a process INSTEAD of their result and complete afterwards:
366
+ * an import hands back one per service it touched, and `POST /service-stack/{id}/user-data` answers with
367
+ * a process rather than the record it created. A caller that imports services and then immediately
368
+ * writes their environment needs to know the import's processes finished, and until this existed
369
+ * nothing could ask.
370
+ *
371
+ * UNVERIFIED BEHAVIOUR: the status VOCABULARY (see `ZEROPS_PROCESS_TERMINAL`). The published document
372
+ * types `status` as a bare string defaulting to `PENDING`, so it stays `string` here rather than being
373
+ * narrowed to a set nobody publishes.
374
+ */
375
+ getProcess(input: {
376
+ processId: string;
377
+ signal: AbortSignal;
378
+ }): Promise<ZeropsProcess>;
379
+ /**
380
+ * Publish a service on Zerops' generated `*.zerops.app` subdomain. VERIFIED:
381
+ * `PUT /service-stack/{id}/enable-subdomain-access`, no body, `ResponseProcess`.
382
+ *
383
+ * THE ONLY THING THAT ESTABLISHES ONE. `enableSubdomainAccess: true` in an import document is accepted
384
+ * and then silently dropped — on CREATE as well as on `override` — so a service imported with it reads
385
+ * back `subdomainAccess: false` and stays that way (`docs/reference/zerops-platform.md`).
386
+ *
387
+ * TWO ORDERING FACTS the caller must respect. It needs a DEPLOYED HTTP port: before the first deploy
388
+ * the platform answers `400 serviceStackIsNotHttp`. And a 2xx here is NOT a success signal — calling it
389
+ * on a service that already has a subdomain returns a process that then FAILS — so decide by reading
390
+ * `getService().subdomainAccess` back, never by this call returning.
391
+ */
392
+ enableSubdomainAccess(input: {
393
+ serviceId: string;
394
+ signal: AbortSignal;
395
+ }): Promise<void>;
396
+ /**
397
+ * Start a stopped service. VERIFIED AS A SHAPE: `PUT /service-stack/{id}/start` (`StartServiceStack`), no body,
398
+ * `ResponseProcess`. Measured 2026-10-21: a stopped Bun service served again about 9 s after the call. Files written
399
+ * at runtime survive a stop and a start; a deploy replaces the container and loses them.
400
+ */
401
+ startService(input: {
402
+ serviceId: string;
403
+ signal: AbortSignal;
404
+ }): Promise<ZeropsProcess>;
405
+ /** Stop a service. VERIFIED AS A SHAPE: `PUT /service-stack/{id}/stop` (`StopServiceStack`), no body, `ResponseProcess`. */
406
+ stopService(input: {
407
+ serviceId: string;
408
+ signal: AbortSignal;
409
+ }): Promise<ZeropsProcess>;
410
+ /**
411
+ * Move a managed service to another autoscaling profile. VERIFIED AS A SHAPE: `PUT
412
+ * /service-stack/{id}/autoscaling` (`ConfigureAutoscaling`), body `RequestAutoscaling`, response
413
+ * `ResponseProcessNil` — `null` when the platform hands back no process.
414
+ *
415
+ * The ONLY way to change a live profile: a re-import with a changed `profile` is silently ignored
416
+ * (`docs/reference/zerops-platform.md`). UNVERIFIED: whether omitting `customAutoscaling` keeps or
417
+ * resets custom overrides, and how soon `getService().autoscalingProfileId` reads the new value.
418
+ */
419
+ configureAutoscaling(input: {
420
+ serviceId: string;
421
+ autoscalingProfileId: string;
422
+ signal: AbortSignal;
423
+ }): Promise<ZeropsProcess | null>;
424
+ /**
425
+ * Replace a service's custom autoscaling: the same `PUT /service-stack/{id}/autoscaling`, body
426
+ * `{ customAutoscaling: { verticalAutoscaling, horizontalAutoscaling } }`. VERIFIED live 2026-10-15: it answers
427
+ * `{ process: null }` and `currentAutoscaling` reads the change at once. The platform replaces the whole object: a
428
+ * field left out, or under a key it does not know, is reset to the service type's value and the call still answers
429
+ * 200, so the caller sends what `getService().customAutoscaling` read, changed where it means to.
430
+ */
431
+ configureCustomAutoscaling(input: {
432
+ serviceId: string;
433
+ autoscaling: ZeropsServiceAutoscaling;
434
+ signal: AbortSignal;
435
+ }): Promise<ZeropsProcess | null>;
436
+ /** One service by id. VERIFIED: `GET /service-stack/{id}`. */
437
+ getService(input: {
438
+ serviceId: string;
439
+ signal: AbortSignal;
440
+ }): Promise<ZeropsService>;
441
+ /** One service by hostname within a project, or `null` if absent. VERIFIED: `GET /service-stack-by-name/{projectId}/{name}`. */
442
+ findService(input: {
443
+ projectId: string;
444
+ hostname: string;
445
+ signal: AbortSignal;
446
+ }): Promise<ZeropsService | null>;
447
+ /** One project by id. VERIFIED: `GET /project/{id}`. */
448
+ getProject(input: {
449
+ projectId: string;
450
+ signal: AbortSignal;
451
+ }): Promise<ZeropsProject>;
452
+ /**
453
+ * Every project under a client. VERIFIED: `GET /client/{id}/project`.
454
+ * The implementation follows the response's `total` with explicit `limit`/`offset` pages.
455
+ */
456
+ listProjects(input: {
457
+ clientId: string;
458
+ signal: AbortSignal;
459
+ }): Promise<ZeropsProject[]>;
460
+ /**
461
+ * Every exact-name match under a client. VERIFIED: `GET /client/{id}/projects-by-name/{name}`.
462
+ * Zerops returns an array, so duplicate names remain visible to the caller.
463
+ */
464
+ findProjects(input: {
465
+ clientId: string;
466
+ name: string;
467
+ signal: AbortSignal;
468
+ }): Promise<ZeropsProject[]>;
469
+ /**
470
+ * Every service stack under a project. VERIFIED: `GET /project/{id}/service-stack`.
471
+ * The implementation follows the response's `total` with explicit `limit`/`offset` pages.
472
+ */
473
+ listProjectServices(input: {
474
+ projectId: string;
475
+ signal: AbortSignal;
476
+ }): Promise<ZeropsService[]>;
477
+ /**
478
+ * Every environment variable of ONE service, each with the record id `deleteServiceEnv` takes.
479
+ * VERIFIED: `GET /service-stack/{id}/env` (`{ items }`).
480
+ *
481
+ * NOT `GET /service-stack/{id}/user-data`: it answers a list now (2026-08-21), but `/env` is the
482
+ * endpoint whose record ids `PUT`/`DELETE /user-data/{id}` take, and the list's paging terms were
483
+ * never verified — see `docs/reference/zerops-platform.md`. The `/env` reading omits
484
+ * variables the service's own `zerops.yaml` declares (`type: ENV`); fabrika never writes those.
485
+ * The OpenAPI publishes no lookup/pagination terms and live probes found query terms ignored, so the
486
+ * implementation reads one full response under a byte bound rather than imposing an entry-count cap.
487
+ */
488
+ listServiceEnv(input: {
489
+ serviceId: string;
490
+ signal: AbortSignal;
491
+ }): Promise<ZeropsServiceEnv[]>;
492
+ /**
493
+ * Create ONE service-level variable only when the key is absent.
494
+ * VERIFIED: `POST /service-stack/{id}/user-data`, body `{ key, content }`.
495
+ *
496
+ * Unlike `putServiceEnv`, a duplicate is an error and is never followed by a read or update. Error
497
+ * details are intentionally unavailable because both the request and an upstream response can contain
498
+ * the secret value.
499
+ */
500
+ createServiceEnv(input: ZeropsServiceEnvWrite): Promise<void>;
501
+ /**
502
+ * Create-or-update ONE service-level variable — the only way fabrika ever writes a secret, and it is
503
+ * addressed BY SERVICE so ADR-0004's invariant cannot be violated by calling it.
504
+ * VERIFIED: `POST /service-stack/{id}/user-data` creates and `PUT /user-data/{id}` replaces, both
505
+ * `{ key, content }`; POSTing an existing key answers `400 userDataDuplicateKey` and changes nothing.
506
+ *
507
+ * WRITE FIRST, READ ONLY ON CONFLICT. A read before the write cannot be part of this: the create
508
+ * must work on a service that has never been deployed, which is exactly ADR-0004's bring-up order.
509
+ * An update also sets `sensitive`, so a record written sensitive by an older release becomes readable.
510
+ */
511
+ putServiceEnv(input: ZeropsServiceEnvWrite): Promise<void>;
512
+ /** Delete one service-level variable by its record id. VERIFIED: `DELETE /user-data/{id}`. */
513
+ deleteServiceEnv(input: {
514
+ envId: string;
515
+ signal: AbortSignal;
516
+ }): Promise<void>;
517
+ /**
518
+ * Read ONE project-level variable back, by record id. VERIFIED: `GET /project-env/{id}`.
519
+ * Read-only ON PURPOSE — see the note on this interface. Present so drift can be REPORTED.
520
+ */
521
+ getProjectEnv(input: {
522
+ projectEnvId: string;
523
+ signal: AbortSignal;
524
+ }): Promise<ZeropsServiceEnv>;
525
+ /** The identity this client's token authenticates as. VERIFIED: `GET /user/info` (`GetUserInfo`). */
526
+ getUserInfo(input: {
527
+ signal: AbortSignal;
528
+ }): Promise<ZeropsUserInfo>;
529
+ /**
530
+ * Mint a project-scoped INTEGRATION token. VERIFIED: `POST /client/{id}/integration-token`
531
+ * (`createIntegrationToken`), body `RequestClientIntegrationToken` — `name` and `projects` are its two
532
+ * required members — response `ResponseClientIntegrationTokenRaw`, whose `required` list contains
533
+ * `token`.
534
+ *
535
+ * WHAT IT IS FOR. A control plane deployed on Zerops holds a Zerops credential, and it must not be the
536
+ * personal access token an operator or a bootstrap authenticated with: that one carries account-wide
537
+ * admin rights (module header). An integration token left at client `NO_ACCESS` with one project
538
+ * granted `ADMIN` can do nothing outside that project.
539
+ *
540
+ * `roleCode` IS ALWAYS SENT, even when its value is the schema's own default, for the reason
541
+ * `compile.ts` writes `envIsolation` instead of inheriting it: a security-relevant field that never
542
+ * reaches the wire is a field nobody can review.
543
+ *
544
+ * `canCreateProjects` IS EXPOSED, because a control plane that provisions app namespaces cannot work
545
+ * without it: every namespace preset creates a project, and `POST /client/{id}/project/import` answers
546
+ * `403 insufficientPermissions` to a token without the flag (measured live, 2026-08-18; ADR-0034).
547
+ * The cost is real and bounded — the token receives `OWNER` on what it creates, and nothing else:
548
+ * `roleCode` stays `NO_ACCESS`, so a project it never created and was never granted stays unreachable.
549
+ *
550
+ * NOT EXPOSED, deliberately: `canViewFinances` / `canEditFinances`. Both default to `false`, and
551
+ * fabrika has no use for either.
552
+ *
553
+ * UNVERIFIED BEHAVIOUR: EVERYTHING AT RUNTIME. No token has ever been minted against a real account —
554
+ * doing so is a mutation, not an inspection — so the shapes below are the published document's and the
555
+ * behaviours are unmeasured: whether a repeated `name` conflicts, whether client `NO_ACCESS` alongside
556
+ * a project grant is accepted, and what the token's own prefix or length looks like.
557
+ *
558
+ * THE RESPONSE CARRIES A SECRET, so the implementation redacts the server's message from any error it
559
+ * raises. This is the one endpoint whose error envelope could quote a token value back.
560
+ */
561
+ createIntegrationToken(input: {
562
+ clientId: string;
563
+ /** The token's label in the Zerops UI. Not a secret and not unique as far as any document says. */
564
+ name: string;
565
+ /** Per-project grants. `ADMIN` is what a control plane needs on a project it deploys into. */
566
+ projects: readonly {
567
+ projectId: string;
568
+ roleCode: ZeropsRoleCode;
569
+ }[];
570
+ /** The client-wide role. Defaults to `NO_ACCESS`, which is the entire point of a scoped token. */
571
+ roleCode?: ZeropsRoleCode;
572
+ /** Let the token create projects, which it then OWNS. Required to provision app namespaces (ADR-0034). */
573
+ canCreateProjects?: boolean;
574
+ signal: AbortSignal;
575
+ }): Promise<ZeropsIntegrationToken>;
576
+ /** A short-lived grant for the project's log service. VERIFIED: `GET /project/{id}/log`. */
577
+ getLogAccess(input: {
578
+ projectId: string;
579
+ signal: AbortSignal;
580
+ }): Promise<ZeropsLogAccess>;
581
+ /**
582
+ * One window of the project's log, narrowed to ONE app version.
583
+ *
584
+ * VERIFIED 2026-08-21 by measurement, not by document: the OpenAPI file stops at the grant, so the
585
+ * separate log service was read against a live project. `urlPlain` answers one
586
+ * `<RFC3339 timestamp> <tag> <message>` line per record and accepts `serviceStackId`, `limit`, `tags`
587
+ * and `desc`; `from` and unknown terms are accepted and silently ignored, so nothing may depend on them.
588
+ *
589
+ * ORDERING, also measured: `limit=N` without `desc` returns the NEWEST N records in ASCENDING timestamp
590
+ * order (`limit=3` against a log starting 15:50 answered 17:00:00, 17:00:00, 17:15:00), and `desc=1`
591
+ * returns the same newest N, newest first. So `desc` is needed neither for the `since` cut nor for a
592
+ * build longer than the limit: each poll re-reads the newest window and the caller's dedupe streams it.
593
+ *
594
+ * A version's BUILD lines carry the tag `zbuilder@<appVersionId>` and are produced on a separate
595
+ * builder stack, so they are selected by `tags` ALONE — combining that tag with the runtime
596
+ * `serviceStackId` matches nothing. RUNTIME lines carry no version marker at all and the runtime
597
+ * service's window mixes every version that ever ran, so they are selected by `serviceStackId` and cut
598
+ * at `since` (the version's `build.pipelineStart`). Without `since` no runtime line is read: a line
599
+ * from an earlier version is worse in a run log than a missing one. The cut is by TIME, not identity —
600
+ * the outgoing container keeps logging until it is replaced, so its lines stamped after `since` still
601
+ * pass, and only the earlier version's older history is excluded.
602
+ *
603
+ * The two windows are read INDEPENDENTLY: one failing must not discard what the other returned, and only
604
+ * a failure of every window attempted is raised. The driver then treats that as non-fatal anyway — log
605
+ * relay degrades to "no lines" and the deploy succeeds or fails on `/app-version` status alone. A log
606
+ * service must never be able to fail a deploy.
607
+ */
608
+ readBuildLog(input: {
609
+ access: ZeropsLogAccess;
610
+ serviceId: string;
611
+ /** Select this version's build lines by their `zbuilder@` tag; without it no build line is read. */
612
+ appVersionId?: string;
613
+ /** RFC3339 lower bound for runtime lines, normally `build.pipelineStart`; without it none are read. */
614
+ since?: string;
615
+ limit?: number;
616
+ signal: AbortSignal;
617
+ }): Promise<ZeropsLogLine[]>;
618
+ }
619
+ /**
620
+ * Just enough of `fetch` for this client — narrower than `typeof fetch` so a test can supply a plain
621
+ * function without also implementing the runtime's extras (`preconnect`).
622
+ */
623
+ export type FetchLike = (input: string, init?: {
624
+ method?: string;
625
+ headers?: Record<string, string>;
626
+ body?: string;
627
+ signal?: AbortSignal;
628
+ }) => Promise<Response>;
629
+ /** How the real client is built. `fetchImpl` exists so a test can drive the REAL client offline. */
630
+ export interface ZeropsApiOptions {
631
+ /** Zerops personal access token. Sent as `Authorization: Bearer`. NEVER logged. */
632
+ token: string;
633
+ /** Override for a different region's API host. */
634
+ baseUrl?: string;
635
+ fetchImpl?: FetchLike;
636
+ /** How the user-data writes wait for their platform process. Tests pass a sleeper that does not sleep. */
637
+ sleep?: Sleeper;
638
+ }
@@ -0,0 +1,6 @@
1
+ export { createZeropsApi, ZEROPS_API_BASE } from './api/client';
2
+ export { ZEROPS_SERVICE_NOT_HTTP, ZeropsApiError } from './api/errors';
3
+ export { enableSubdomainAccessWhenHttp, waitForProcess } from './api/polling';
4
+ export { assertServiceEnvValueFits, ZEROPS_SERVICE_ENV_VALUE_MAX_BYTES, ZEROPS_SERVICE_ENV_VALUE_TOO_LARGE, ZeropsServiceEnvValueTooLargeError, } from './api/service-env-limit';
5
+ export { readableContent } from './api/service-env-response';
6
+ export { type FetchLike, ZEROPS_ACTIVE, ZEROPS_PROCESS_FINISHED, ZEROPS_PROCESS_TERMINAL, ZEROPS_TERMINAL, type ZeropsApi, type ZeropsApiOptions, type ZeropsAppVersion, type ZeropsAppVersionStatus, type ZeropsAppVersionUpload, type ZeropsImportedService, type ZeropsImportResult, type ZeropsIntegrationToken, type ZeropsLogAccess, type ZeropsLogLine, type ZeropsProcess, type ZeropsProject, type ZeropsProjectMode, type ZeropsProjectStatus, type ZeropsRoleCode, type ZeropsService, type ZeropsServiceAutoscaling, type ZeropsServiceEnv, type ZeropsServiceEnvWrite, type ZeropsServiceStatus, type ZeropsTokenProjectGrant, type ZeropsUserInfo, } from './api/types';
@@ -0,0 +1,9 @@
1
+ export { zeropsSharedServiceHostname, zeropsSharedServicePrefix } from './service-names';
2
+ import { type ProviderAuthoredApp } from '@fabrika/provider-contract';
3
+ import type { ZeropsAppConfig, ZeropsSharedPostgresBinding } from './types';
4
+ /** Declare consumption of the namespace-owned `postgres` service without resolving its credential. */
5
+ export declare const useSharedPostgres: () => ZeropsSharedPostgresBinding;
6
+ /** Provider-specific authoring identity with complete Zerops contextual typing. */
7
+ export type ZeropsAuthoredAppConfig = ZeropsAppConfig & ProviderAuthoredApp<'zerops'>;
8
+ export declare const defineApp: (config: ZeropsAppConfig) => ZeropsAuthoredAppConfig;
9
+ export declare const isZeropsAppConfig: (value: unknown) => value is ZeropsAuthoredAppConfig;