@sequenceholdings/artifact-studio 0.6.4 → 0.6.6
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 +183 -6
- package/VERSION-PIN.md +56 -1
- package/dist/gateway/admission.d.ts +23 -1
- package/dist/gateway/admission.js +30 -13
- package/dist/gateway/control.d.ts +40 -0
- package/dist/gateway/control.js +76 -7
- package/dist/gateway/hosted-proxy.d.ts +11 -8
- package/dist/gateway/hosted-proxy.js +99 -91
- package/dist/gateway/ingress-admission.d.ts +77 -0
- package/dist/gateway/ingress-admission.js +150 -0
- package/dist/gateway/ingress-audit.d.ts +46 -0
- package/dist/gateway/ingress-audit.js +84 -0
- package/dist/gateway/ingress-context.d.ts +57 -0
- package/dist/gateway/ingress-context.js +57 -0
- package/dist/gateway/ingress-development.d.ts +12 -0
- package/dist/gateway/ingress-development.js +23 -0
- package/dist/gateway/ingress-handler.d.ts +44 -0
- package/dist/gateway/ingress-handler.js +320 -0
- package/dist/gateway/ingress-pass.d.ts +83 -0
- package/dist/gateway/ingress-pass.js +143 -0
- package/dist/gateway/ingress-response.d.ts +74 -0
- package/dist/gateway/ingress-response.js +98 -0
- package/dist/gateway/ingress-runtime.d.ts +86 -0
- package/dist/gateway/ingress-runtime.js +126 -0
- package/dist/gateway/ingress-snapshot.d.ts +54 -0
- package/dist/gateway/ingress-snapshot.js +63 -0
- package/dist/gateway/ingress-stages.d.ts +60 -0
- package/dist/gateway/ingress-stages.js +387 -0
- package/dist/gateway/ingress.d.ts +259 -0
- package/dist/gateway/ingress.js +204 -0
- package/dist/gateway/lattice-invoke.js +2 -0
- package/dist/gateway/lattice-upstream.d.ts +3 -4
- package/dist/gateway/lattice-upstream.js +11 -9
- package/dist/gateway/runtime-transport.d.ts +17 -0
- package/dist/gateway/runtime-transport.js +26 -0
- package/dist/gateway/server.d.ts +3 -0
- package/dist/gateway/server.js +21 -0
- package/dist/gateway/session-response.d.ts +15 -0
- package/dist/gateway/session-response.js +40 -0
- package/dist/gateway/usage.d.ts +8 -1
- package/dist/gateway-main.js +32 -8
- package/dist/manifest-next-ingress.d.ts +5 -2
- package/dist/manifest-next-ingress.js +27 -2
- package/dist/manifest-next.d.ts +20 -6
- package/dist/manifest-next.js +13 -5
- package/dist/manifest.d.ts +7 -2
- package/dist/next-project.d.ts +4 -0
- package/dist/sdk.d.ts +12 -3
- package/dist/server/broker-contract.d.ts +62 -0
- package/dist/server/broker-contract.js +43 -1
- package/dist/server/client.js +38 -13
- package/dist/server/external-context.d.ts +8 -1
- package/dist/server/external-context.js +7 -2
- package/dist/server/internal/v1-context.js +17 -1
- package/dist/server/internal/verify-any.d.ts +11 -2
- package/dist/server/internal/verify-any.js +11 -3
- package/dist/server/process-run-sharing.d.ts +86 -0
- package/dist/server/process-run-sharing.js +29 -0
- package/dist/server/process-runs-page.d.ts +39 -0
- package/dist/server/process-runs-page.js +10 -0
- package/dist/server/processes.d.ts +8 -9
- package/dist/server/processes.js +4 -0
- package/dist/server/request-context.js +10 -3
- package/dist/server/runtime-config.d.ts +10 -1
- package/dist/server/runtime-config.js +11 -1
- package/dist/server/testing.d.ts +1 -1
- package/dist/server.d.ts +2 -2
- package/dist/server.js +1 -1
- package/dist/tunnel/broker.d.ts +18 -0
- package/dist/tunnel/broker.js +175 -17
- package/dist/tunnel/client.d.ts +13 -0
- package/dist/tunnel/client.js +40 -2
- package/dist/tunnel/external.d.ts +51 -0
- package/dist/tunnel/external.js +10 -0
- package/dist/tunnel/index.d.ts +1 -1
- package/dist/tunnel/protocol.d.ts +20 -2
- package/dist/tunnel/protocol.js +42 -6
- package/package.json +4 -4
package/dist/gateway/server.js
CHANGED
|
@@ -13,6 +13,9 @@ import { gatewayClientAddress } from './control-admission.js';
|
|
|
13
13
|
import { gatewayAuthorizationKey, PositiveDecisionCache, tunnelAuthorizationKey } from './authorization-cache.js';
|
|
14
14
|
import { GatewayUsageReporter } from './usage.js';
|
|
15
15
|
import { artifactNextFrameworkBuildIdSchema } from '../runtime-contracts.js';
|
|
16
|
+
import { createDevelopmentIngressProxy } from './ingress-development.js';
|
|
17
|
+
import { createIngressHandler, handleClientErrors } from './ingress-handler.js';
|
|
18
|
+
import { resemblesIngressHost } from './ingress.js';
|
|
16
19
|
function json(response, status, error) {
|
|
17
20
|
if (response.headersSent) {
|
|
18
21
|
response.destroy();
|
|
@@ -115,6 +118,7 @@ export function createArtifactGateway(options) {
|
|
|
115
118
|
const tunnelAuthorizations = new PositiveDecisionCache({ ttlMs: options.authorizationCacheTtlMs });
|
|
116
119
|
const usage = new GatewayUsageReporter({ send: events => options.control.usage(events), onFailure: failure => options.onError?.(failure) });
|
|
117
120
|
let draining = false;
|
|
121
|
+
const isIngressRequest = (request) => resemblesIngressHost({ rawHost: header(request, 'host'), runtimeDomain: options.runtimeDomain });
|
|
118
122
|
/** Admission for the unauthenticated endpoints; the release frees the in-flight permit. */
|
|
119
123
|
async function admitControlCall(endpoint, request) {
|
|
120
124
|
if (!options.controlAdmission)
|
|
@@ -254,7 +258,16 @@ export function createArtifactGateway(options) {
|
|
|
254
258
|
onTunnelClosed: lease => { if (leases.get(lease.sessionId)?.lease.epoch === lease.epoch)
|
|
255
259
|
leases.delete(lease.sessionId); },
|
|
256
260
|
});
|
|
261
|
+
// A development target is proxied over its session tunnel only by a development gateway (design §(j.3) step 3).
|
|
262
|
+
const proxyDevelopment = options.ingress?.appEnv === 'development' ? createDevelopmentIngressProxy({ broker }) : undefined;
|
|
263
|
+
const ingress = createIngressHandler({ options: options.ingress, runtimeDomain: options.runtimeDomain, protocol: new URL(gatewayOrigin).protocol, isDraining: () => draining, onError: options.onError, usage, proxyDevelopment });
|
|
257
264
|
const server = createServer({ maxHeaderSize: 32 * 1024, requestTimeout: 60_000, headersTimeout: 15_000 }, (request, response) => {
|
|
265
|
+
// Before readiness, bootstrap and requestOrigin(): an ingress host, canonical or not, never
|
|
266
|
+
// reaches a session-path handler or the 401/404→403 remap below (design §(e) "Branch placement").
|
|
267
|
+
if (isIngressRequest(request)) {
|
|
268
|
+
void ingress.handle(request, response);
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
258
271
|
const startedAt = new Date().toISOString(), started = performance.now();
|
|
259
272
|
void (async () => {
|
|
260
273
|
if (isReadinessRequest({ request, gatewayOrigin, runtimeDomain: options.runtimeDomain })) {
|
|
@@ -350,6 +363,11 @@ export function createArtifactGateway(options) {
|
|
|
350
363
|
rejectUpgrade(socket, 503);
|
|
351
364
|
return;
|
|
352
365
|
}
|
|
366
|
+
// An ingress answers data requests only: no WebSocket, no tunnel.
|
|
367
|
+
if (isIngressRequest(request)) {
|
|
368
|
+
rejectUpgrade(socket, 404);
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
353
371
|
if (request.url === '/_sequence/tunnel') {
|
|
354
372
|
await broker.handleTunnelUpgrade(request, socket, head);
|
|
355
373
|
return;
|
|
@@ -362,6 +380,9 @@ export function createArtifactGateway(options) {
|
|
|
362
380
|
await broker.handleWebSocketUpgrade(request, socket, head, authorized.target.sessionId);
|
|
363
381
|
})().catch(() => rejectUpgrade(socket));
|
|
364
382
|
});
|
|
383
|
+
// Only with the ingress branch configured: without it, Node's own clientError replies stay untouched.
|
|
384
|
+
if (options.ingress)
|
|
385
|
+
handleClientErrors({ server, ingress });
|
|
365
386
|
return {
|
|
366
387
|
server,
|
|
367
388
|
/** Observations recorded but not yet confirmed by Atlas: what a forced exit would lose. */
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
|
+
/** Response-header rules for the iframe session path of the hosted proxy (an ingress uses `ingress-response.ts`). */
|
|
3
|
+
export declare function rewriteLocation({ location, upstream, publicOrigin }: {
|
|
4
|
+
location: string;
|
|
5
|
+
upstream: URL;
|
|
6
|
+
publicOrigin: string;
|
|
7
|
+
}): string;
|
|
8
|
+
export declare function mergeFramePolicy({ application, trusted }: {
|
|
9
|
+
application: string | string[];
|
|
10
|
+
trusted: string | string[];
|
|
11
|
+
}): string[];
|
|
12
|
+
export declare function enforceBrowserCachePolicy({ request, response }: {
|
|
13
|
+
request: IncomingMessage;
|
|
14
|
+
response: ServerResponse;
|
|
15
|
+
}): void;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { isSharedCacheDirective, SHARED_CACHE_REFUSAL } from '../tunnel/protocol.js';
|
|
2
|
+
/** Response-header rules for the iframe session path of the hosted proxy (an ingress uses `ingress-response.ts`). */
|
|
3
|
+
export function rewriteLocation({ location, upstream, publicOrigin }) {
|
|
4
|
+
if (!/^(?:https?:)?\/\//i.test(location))
|
|
5
|
+
return location;
|
|
6
|
+
const url = new URL(location, upstream);
|
|
7
|
+
return url.origin === upstream.origin ? `${publicOrigin}${url.pathname}${url.search}${url.hash}` : location;
|
|
8
|
+
}
|
|
9
|
+
export function mergeFramePolicy({ application, trusted }) {
|
|
10
|
+
const policies = Array.isArray(application) ? application : [application];
|
|
11
|
+
const frames = Array.isArray(trusted) ? trusted : [trusted];
|
|
12
|
+
return [...policies.map((policy) => policy.split(';')
|
|
13
|
+
.filter((directive) => directive.trim().split(/\s/, 1)[0].toLowerCase() !== 'frame-ancestors')
|
|
14
|
+
.map((directive) => directive.trim()).filter(Boolean).join('; ')).filter(Boolean), ...frames];
|
|
15
|
+
}
|
|
16
|
+
export function enforceBrowserCachePolicy({ request, response }) {
|
|
17
|
+
const path = (request.url ?? '/').split('?', 1)[0];
|
|
18
|
+
let staticAsset = false;
|
|
19
|
+
try {
|
|
20
|
+
const segments = path.slice(1).split('/').map((segment) => decodeURIComponent(segment));
|
|
21
|
+
staticAsset = path.startsWith('/_next/static/') && /\.(?:js|css)$/.test(path) &&
|
|
22
|
+
segments.every((segment) => segment !== '' && segment !== '.' && segment !== '..' && !/[\\/]/.test(segment));
|
|
23
|
+
}
|
|
24
|
+
catch { /* Malformed or ambiguous asset paths retain the default no-store policy. */ }
|
|
25
|
+
const contentType = response.getHeader('content-type');
|
|
26
|
+
const mediaType = typeof contentType === 'string' ? contentType.split(';', 1)[0].trim().toLowerCase() : undefined;
|
|
27
|
+
const assetType = path.endsWith('.css') ? mediaType === 'text/css' : mediaType === 'text/javascript' || mediaType === 'application/javascript';
|
|
28
|
+
const revalidateAsset = staticAsset && ['GET', 'HEAD'].includes(request.method ?? 'GET') &&
|
|
29
|
+
(response.statusCode === 200 || response.statusCode === 304) &&
|
|
30
|
+
(assetType || (response.statusCode === 304 && contentType === undefined));
|
|
31
|
+
// Every protected reuse must reach authorization. Retain only private static
|
|
32
|
+
// JS/CSS bytes and validators; app output may never opt into browser/CDN TTLs.
|
|
33
|
+
for (const name of response.getHeaderNames()) {
|
|
34
|
+
if (isSharedCacheDirective(name))
|
|
35
|
+
response.removeHeader(name);
|
|
36
|
+
}
|
|
37
|
+
response.setHeader('cache-control', revalidateAsset ? 'private, no-cache' : 'private, no-store');
|
|
38
|
+
for (const [name, value] of Object.entries(SHARED_CACHE_REFUSAL))
|
|
39
|
+
response.setHeader(name, value);
|
|
40
|
+
}
|
package/dist/gateway/usage.d.ts
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
export interface GatewayUsageEvent {
|
|
2
2
|
eventId: string;
|
|
3
3
|
requestId: string;
|
|
4
|
-
/**
|
|
4
|
+
/**
|
|
5
|
+
* Who the gateway acted for: the session owner Atlas authorized for this request, or for an
|
|
6
|
+
* external ingress request the namespaced external subject (`ext|<provider>|<subject>`).
|
|
7
|
+
*/
|
|
5
8
|
actorId: string;
|
|
9
|
+
/** Present only for an external ingress request. */
|
|
10
|
+
principalKind?: 'external';
|
|
11
|
+
/** The ingress an external request arrived through. */
|
|
12
|
+
ingressId?: string;
|
|
6
13
|
projectId: string;
|
|
7
14
|
deploymentId?: string;
|
|
8
15
|
devSessionId?: string;
|
package/dist/gateway-main.js
CHANGED
|
@@ -7,6 +7,7 @@ import { gatewaySessionSameSite } from './gateway/session.js';
|
|
|
7
7
|
import { RedisTunnelOwnership, RedisTunnelRelay } from './tunnel/redis.js';
|
|
8
8
|
import { ArtifactRequestAdmission } from './gateway/admission.js';
|
|
9
9
|
import { GatewayControlAdmission } from './gateway/control-admission.js';
|
|
10
|
+
import { createIngressRuntime, IngressSecretsError } from './gateway/ingress-runtime.js';
|
|
10
11
|
function required(name) {
|
|
11
12
|
const value = process.env[name];
|
|
12
13
|
if (!value)
|
|
@@ -40,6 +41,8 @@ async function main() {
|
|
|
40
41
|
// A flood of refused requests or forged frames must not become a log flood: count and report every 10 s.
|
|
41
42
|
const refusals = new Map();
|
|
42
43
|
const refuse = (kind) => { refusals.set(kind, (refusals.get(kind) ?? 0) + 1); };
|
|
44
|
+
// Kinds a caller can produce at request rate: counted, never logged one by one.
|
|
45
|
+
const counted = new Set(['admission_rejected', 'ingress_pass_rejected', 'ingress_token_rejected', 'ingress_edge_header_invalid', 'ingress_client_ip_invalid']);
|
|
43
46
|
setInterval(() => {
|
|
44
47
|
for (const [kind, count] of refusals)
|
|
45
48
|
log({ event: 'artifact_gateway.request', kind, count });
|
|
@@ -56,26 +59,43 @@ async function main() {
|
|
|
56
59
|
subscriber.on('ready', () => { if (!stopping)
|
|
57
60
|
void relay.start().catch(() => undefined); });
|
|
58
61
|
const controlUrl = required('ARTIFACT_NEXT_CONTROL_PLANE_URL');
|
|
62
|
+
const controlToken = required('ARTIFACT_NEXT_CONTROL_TOKEN');
|
|
63
|
+
const control = new ArtifactGatewayControl({ url: controlUrl, token: controlToken });
|
|
64
|
+
const contextPrivateKey = required('ARTIFACT_NEXT_CONTEXT_PRIVATE_KEY');
|
|
65
|
+
const contextKeyId = required('ARTIFACT_NEXT_CONTEXT_KEY_ID');
|
|
66
|
+
const admission = new ArtifactRequestAdmission({ redis: command, namespace });
|
|
67
|
+
// The external ingress branch: off (every ingress host 404) unless all four of its secrets are
|
|
68
|
+
// set; the process refuses to boot when they are set but unusable or not independent.
|
|
69
|
+
const ingress = createIngressRuntime({
|
|
70
|
+
env: process.env, control, controlToken, sessionKey, contextPrivateKey, contextKeyId, redis: command, namespace, admission, log,
|
|
71
|
+
onFailure: failure => { if ('count' in failure)
|
|
72
|
+
log({ event: 'artifact_gateway.request', ...failure });
|
|
73
|
+
else
|
|
74
|
+
refuse(failure.kind); },
|
|
75
|
+
});
|
|
59
76
|
const gateway = createArtifactGateway({
|
|
60
|
-
control
|
|
77
|
+
control,
|
|
61
78
|
runtimeDomain: required('ARTIFACT_NEXT_RUNTIME_DOMAIN'),
|
|
62
79
|
parentOrigin: process.env.ARTIFACT_NEXT_PARENT_ORIGIN ?? new URL(controlUrl).origin,
|
|
63
80
|
gatewayOrigin: required('ARTIFACT_NEXT_GATEWAY_URL'),
|
|
64
81
|
sessionKey,
|
|
65
82
|
sessionCookieSameSite: gatewaySessionSameSite({ value: process.env.ARTIFACT_NEXT_SESSION_COOKIE_SAMESITE, appEnv: namespace }),
|
|
66
|
-
contextPrivateKey
|
|
67
|
-
contextKeyId
|
|
83
|
+
contextPrivateKey,
|
|
84
|
+
contextKeyId,
|
|
68
85
|
nodeId, routing: ownership, relay,
|
|
69
|
-
admission
|
|
86
|
+
admission,
|
|
87
|
+
ingress: ingress.ingress,
|
|
70
88
|
controlAdmission: new GatewayControlAdmission({ redis: command, namespace }),
|
|
71
89
|
resolveLease: sessionId => ownership.resolveLease(sessionId),
|
|
72
90
|
renewLease: lease => ownership.renew(lease, nodeId),
|
|
73
91
|
ready: () => clients.every(client => client.status === 'ready') && relay.isReady(),
|
|
74
|
-
onError: event => { if (event.kind
|
|
92
|
+
onError: event => { if (counted.has(event.kind))
|
|
75
93
|
refuse(event.kind);
|
|
76
94
|
else
|
|
77
95
|
log({ event: 'artifact_gateway.request', ...event }); },
|
|
78
96
|
});
|
|
97
|
+
// The first snapshot pull starts before traffic; until it lands, ingress hosts answer 503.
|
|
98
|
+
ingress.start();
|
|
79
99
|
const port = Number(process.env.PORT ?? '8080');
|
|
80
100
|
if (!Number.isSafeInteger(port) || port < 1 || port > 65535)
|
|
81
101
|
throw new Error('Invalid gateway PORT');
|
|
@@ -102,7 +122,8 @@ async function main() {
|
|
|
102
122
|
}, 9_000);
|
|
103
123
|
deadline.unref();
|
|
104
124
|
await gateway.drain();
|
|
105
|
-
|
|
125
|
+
// Audit delivery runs beside tunnel cleanup, never in front of it.
|
|
126
|
+
await Promise.allSettled([ingress.close(), relay.close()]);
|
|
106
127
|
for (const client of clients)
|
|
107
128
|
client.disconnect();
|
|
108
129
|
clearTimeout(deadline);
|
|
@@ -110,7 +131,10 @@ async function main() {
|
|
|
110
131
|
process.once('SIGTERM', () => { void stop(); });
|
|
111
132
|
process.once('SIGINT', () => { void stop(); });
|
|
112
133
|
}
|
|
113
|
-
void main().catch(() => {
|
|
114
|
-
|
|
134
|
+
void main().catch((error) => {
|
|
135
|
+
// Only an ingress-secrets refusal is described: its message names variables, never values, by
|
|
136
|
+
// construction. Other errors (Redis, URLs) may carry connection strings.
|
|
137
|
+
const reason = error instanceof IngressSecretsError ? error.message : undefined;
|
|
138
|
+
process.stderr.write(`${JSON.stringify({ event: 'artifact_gateway.start_failed', ...(reason ? { reason } : {}) })}\n`);
|
|
115
139
|
process.exit(1);
|
|
116
140
|
});
|
|
@@ -58,6 +58,7 @@ export declare const artifactNextExternalIngressSchema: z.ZodObject<{
|
|
|
58
58
|
data: z.ZodPrefault<z.ZodObject<{
|
|
59
59
|
read: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
60
60
|
write: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
61
|
+
operations: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
61
62
|
}, z.core.$strict>>;
|
|
62
63
|
api: z.ZodPrefault<z.ZodObject<{
|
|
63
64
|
read: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
@@ -123,6 +124,7 @@ export declare const artifactNextIngressSchema: z.ZodObject<{
|
|
|
123
124
|
data: z.ZodPrefault<z.ZodObject<{
|
|
124
125
|
read: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
125
126
|
write: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
127
|
+
operations: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
126
128
|
}, z.core.$strict>>;
|
|
127
129
|
api: z.ZodPrefault<z.ZodObject<{
|
|
128
130
|
read: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
@@ -203,8 +205,9 @@ interface ServerForIngress {
|
|
|
203
205
|
/**
|
|
204
206
|
* Cross-field rules between `server.ingress` and the rest of `server`: one ingress per provider,
|
|
205
207
|
* a service account distinct from the app's, capabilities inside the server's own (verbatim list
|
|
206
|
-
* membership; for storage, the same workspace at a role no higher than the server's
|
|
207
|
-
* the ingress
|
|
208
|
+
* membership; for storage, the same workspace at a role no higher than the server's; for
|
|
209
|
+
* `data.operations`, a namespace the ingress's own data reaches), and room for the ingress share of
|
|
210
|
+
* concurrency. Called from the server schema's refinement.
|
|
208
211
|
*/
|
|
209
212
|
export declare function validateIngressAgainstServer({ server, ctx }: {
|
|
210
213
|
server: ServerForIngress;
|
|
@@ -62,11 +62,17 @@ const evidenceEntrySchema = z.strictObject({
|
|
|
62
62
|
// external caller run any mutation the ORM role allows over that table, and evidence binding and
|
|
63
63
|
// freshness are keyed per named operation (D28; design C-3, "by name, no globs").
|
|
64
64
|
const ingressDataWriteRef = boundedRef.refine((ref) => ormOperationRef.test(ref), 'Ingress ORM writes name each operation: namespace/ops/Operation');
|
|
65
|
+
// The ingress service account holds grants that span every row its role reaches, so an external
|
|
66
|
+
// caller runs only the ORM operations its ingress names: these (reads) and `write`. The broker
|
|
67
|
+
// refuses any other persisted operation, query or mutation, before the data plane's table check
|
|
68
|
+
// (design §(g.3), "Operation allow-list"). By name, no globs, like `write`.
|
|
69
|
+
const ingressDataOperationRef = boundedRef.refine((ref) => ormOperationRef.test(ref), 'Ingress ORM operations name each operation: namespace/ops/Operation');
|
|
65
70
|
const ingressCapabilitiesSchema = z.strictObject({
|
|
66
71
|
storage: storageScopesSchema.optional(),
|
|
67
72
|
data: z.strictObject({
|
|
68
73
|
read: z.array(dataReadRef).max(256).default([]),
|
|
69
74
|
write: z.array(ingressDataWriteRef).max(256).default([]),
|
|
75
|
+
operations: z.array(ingressDataOperationRef).max(256).refine(unique, 'Ingress ORM operations must be unique').default([]),
|
|
70
76
|
}).prefault({}),
|
|
71
77
|
api: reachShape.api,
|
|
72
78
|
functions: reachShape.functions,
|
|
@@ -130,11 +136,29 @@ function requireMembers({ ctx, path, entries, allowed, label }) {
|
|
|
130
136
|
ctx.addIssue({ code: 'custom', path: [...path, index], message: `${entry} must also be declared in server.capabilities.${label}` });
|
|
131
137
|
}
|
|
132
138
|
}
|
|
139
|
+
/**
|
|
140
|
+
* An ingress operation ref names its namespace, and the ingress's own data capabilities (read or
|
|
141
|
+
* write) must reach it: an external caller's grant is built from those, so an operation in any other
|
|
142
|
+
* namespace could never run (the grant refuses its namespace before the allow-list is consulted).
|
|
143
|
+
* The ingress's read and write already sit inside the server's, so this also keeps the namespace
|
|
144
|
+
* inside the server's data. The operation itself need not be listed anywhere else: the data plane
|
|
145
|
+
* still checks its tables against the grant.
|
|
146
|
+
*/
|
|
147
|
+
function requireReachedNamespaces({ ctx, path, entries, reached }) {
|
|
148
|
+
const namespaces = new Set([...reached.read, ...reached.write].map((ref) => ref.split('/', 1)[0]));
|
|
149
|
+
for (const [index, entry] of entries.entries()) {
|
|
150
|
+
const namespace = entry.split('/', 1)[0];
|
|
151
|
+
if (!namespaces.has(namespace)) {
|
|
152
|
+
ctx.addIssue({ code: 'custom', path: [...path, index], message: `${entry} names namespace ${namespace}, which this ingress's capabilities.data does not reach` });
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
133
156
|
/**
|
|
134
157
|
* Cross-field rules between `server.ingress` and the rest of `server`: one ingress per provider,
|
|
135
158
|
* a service account distinct from the app's, capabilities inside the server's own (verbatim list
|
|
136
|
-
* membership; for storage, the same workspace at a role no higher than the server's
|
|
137
|
-
* the ingress
|
|
159
|
+
* membership; for storage, the same workspace at a role no higher than the server's; for
|
|
160
|
+
* `data.operations`, a namespace the ingress's own data reaches), and room for the ingress share of
|
|
161
|
+
* concurrency. Called from the server schema's refinement.
|
|
138
162
|
*/
|
|
139
163
|
export function validateIngressAgainstServer({ server, ctx }) {
|
|
140
164
|
const external = server.ingress?.external;
|
|
@@ -171,6 +195,7 @@ export function validateIngressAgainstServer({ server, ctx }) {
|
|
|
171
195
|
const capabilities = [...at, 'capabilities'];
|
|
172
196
|
requireMembers({ ctx, path: [...capabilities, 'data', 'read'], entries: own.data.read, allowed: declared.data.read, label: 'data.read' });
|
|
173
197
|
requireMembers({ ctx, path: [...capabilities, 'data', 'write'], entries: own.data.write, allowed: declared.data.write, label: 'data.write' });
|
|
198
|
+
requireReachedNamespaces({ ctx, path: [...capabilities, 'data', 'operations'], entries: own.data.operations, reached: own.data });
|
|
174
199
|
requireMembers({ ctx, path: [...capabilities, 'api', 'read'], entries: own.api.read, allowed: declared.api.read, label: 'api.read' });
|
|
175
200
|
requireMembers({ ctx, path: [...capabilities, 'api', 'write'], entries: own.api.write, allowed: declared.api.write, label: 'api.write' });
|
|
176
201
|
requireMembers({ ctx, path: [...capabilities, 'functions', 'invoke'], entries: own.functions.invoke, allowed: declared.functions.invoke, label: 'functions.invoke' });
|
package/dist/manifest-next.d.ts
CHANGED
|
@@ -59,11 +59,15 @@ export declare const artifactNextResourceLimitsSchema: z.ZodPrefault<z.ZodObject
|
|
|
59
59
|
timeout_seconds: z.ZodDefault<z.ZodNumber>;
|
|
60
60
|
}, z.core.$strict>>;
|
|
61
61
|
export declare const artifactNextSecretNameSchema: z.ZodString;
|
|
62
|
-
export declare const artifactNextSecretBindingSchema: z.ZodObject<{
|
|
62
|
+
export declare const artifactNextSecretBindingSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
63
63
|
name: z.ZodString;
|
|
64
64
|
secret_id: z.ZodUUID;
|
|
65
65
|
version: z.ZodNumber;
|
|
66
|
-
}, z.core.$strict
|
|
66
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
67
|
+
name: z.ZodString;
|
|
68
|
+
secret_name: z.ZodString;
|
|
69
|
+
version: z.ZodNumber;
|
|
70
|
+
}, z.core.$strict>]>;
|
|
67
71
|
/** Pinned public assets fetched by the trusted preparation stage before offline compilation. */
|
|
68
72
|
export declare const artifactNextPublicFontSchema: z.ZodObject<{
|
|
69
73
|
path: z.ZodString;
|
|
@@ -107,11 +111,15 @@ export declare const artifactNextServerSchema: z.ZodPrefault<z.ZodObject<{
|
|
|
107
111
|
}, z.core.$strict>>;
|
|
108
112
|
}, z.core.$strict>>;
|
|
109
113
|
service_account: z.ZodOptional<z.ZodString>;
|
|
110
|
-
secrets: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
114
|
+
secrets: z.ZodDefault<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
|
|
111
115
|
name: z.ZodString;
|
|
112
116
|
secret_id: z.ZodUUID;
|
|
113
117
|
version: z.ZodNumber;
|
|
114
|
-
}, z.core.$strict
|
|
118
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
119
|
+
name: z.ZodString;
|
|
120
|
+
secret_name: z.ZodString;
|
|
121
|
+
version: z.ZodNumber;
|
|
122
|
+
}, z.core.$strict>]>>>;
|
|
115
123
|
egress: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
116
124
|
resources: z.ZodPrefault<z.ZodObject<{
|
|
117
125
|
cpu: z.ZodDefault<z.ZodUnion<readonly [z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<4>, z.ZodLiteral<8>]>>;
|
|
@@ -142,6 +150,7 @@ export declare const artifactNextServerSchema: z.ZodPrefault<z.ZodObject<{
|
|
|
142
150
|
data: z.ZodPrefault<z.ZodObject<{
|
|
143
151
|
read: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
144
152
|
write: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
153
|
+
operations: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
145
154
|
}, z.core.$strict>>;
|
|
146
155
|
api: z.ZodPrefault<z.ZodObject<{
|
|
147
156
|
read: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
@@ -291,11 +300,15 @@ export declare const artifactStudioNextManifestSchema: z.ZodObject<{
|
|
|
291
300
|
}, z.core.$strict>>;
|
|
292
301
|
}, z.core.$strict>>;
|
|
293
302
|
service_account: z.ZodOptional<z.ZodString>;
|
|
294
|
-
secrets: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
303
|
+
secrets: z.ZodDefault<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
|
|
295
304
|
name: z.ZodString;
|
|
296
305
|
secret_id: z.ZodUUID;
|
|
297
306
|
version: z.ZodNumber;
|
|
298
|
-
}, z.core.$strict
|
|
307
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
308
|
+
name: z.ZodString;
|
|
309
|
+
secret_name: z.ZodString;
|
|
310
|
+
version: z.ZodNumber;
|
|
311
|
+
}, z.core.$strict>]>>>;
|
|
299
312
|
egress: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
300
313
|
resources: z.ZodPrefault<z.ZodObject<{
|
|
301
314
|
cpu: z.ZodDefault<z.ZodUnion<readonly [z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<4>, z.ZodLiteral<8>]>>;
|
|
@@ -326,6 +339,7 @@ export declare const artifactStudioNextManifestSchema: z.ZodObject<{
|
|
|
326
339
|
data: z.ZodPrefault<z.ZodObject<{
|
|
327
340
|
read: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
328
341
|
write: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
342
|
+
operations: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
329
343
|
}, z.core.$strict>>;
|
|
330
344
|
api: z.ZodPrefault<z.ZodObject<{
|
|
331
345
|
read: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
package/dist/manifest-next.js
CHANGED
|
@@ -47,11 +47,19 @@ export const artifactNextResourceLimitsSchema = z.strictObject({
|
|
|
47
47
|
const envName = z.string().max(128).regex(/^[A-Z][A-Z0-9_]*$/);
|
|
48
48
|
const reservedServerEnv = /^(NEXT_PUBLIC_|ARTIFACT_|SEQUENCE_|GOOGLE_|GCP_|GCLOUD_|DOPPLER_|MODAL_|AUTH0_|NPM_CONFIG_|npm_config_)/;
|
|
49
49
|
export const artifactNextSecretNameSchema = envName.refine((name) => !reservedServerEnv.test(name) && !['NODE_OPTIONS', 'NODE_ENV', 'PORT', 'HOSTNAME', 'PATH', 'HOME', 'LD_PRELOAD', 'LD_LIBRARY_PATH'].includes(name), 'Secret names cannot override platform/runtime environment variables or expose NEXT_PUBLIC_ values');
|
|
50
|
-
export const artifactNextSecretBindingSchema = z.
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
50
|
+
export const artifactNextSecretBindingSchema = z.union([
|
|
51
|
+
z.strictObject({
|
|
52
|
+
name: artifactNextSecretNameSchema,
|
|
53
|
+
secret_id: z.uuid(),
|
|
54
|
+
version: z.number().int().positive(),
|
|
55
|
+
}),
|
|
56
|
+
z.strictObject({
|
|
57
|
+
name: artifactNextSecretNameSchema,
|
|
58
|
+
// A registry name can differ from the mounted env name, including its reserved prefixes.
|
|
59
|
+
secret_name: z.string().max(255).regex(/^[A-Z][A-Z0-9_]*$/),
|
|
60
|
+
version: z.number().int().positive(),
|
|
61
|
+
}),
|
|
62
|
+
]);
|
|
55
63
|
const publicEnvironmentName = envName.refine((name) => name.startsWith('NEXT_PUBLIC_') && !/(^|_)(SECRET|TOKEN|PASSWORD|CREDENTIALS?|PRIVATE_KEY|API_KEY)(_|$)/.test(name), 'Public build variables require NEXT_PUBLIC_ and must not contain secret, token, password, credential, or private/API key names');
|
|
56
64
|
/** Pinned public assets fetched by the trusted preparation stage before offline compilation. */
|
|
57
65
|
export const artifactNextPublicFontSchema = z.strictObject({
|
package/dist/manifest.d.ts
CHANGED
|
@@ -184,11 +184,15 @@ export declare const artifactStudioManifestSchema: z.ZodUnion<readonly [z.ZodObj
|
|
|
184
184
|
}, z.core.$strict>>;
|
|
185
185
|
}, z.core.$strict>>;
|
|
186
186
|
service_account: z.ZodOptional<z.ZodString>;
|
|
187
|
-
secrets: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
187
|
+
secrets: z.ZodDefault<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
|
|
188
188
|
name: z.ZodString;
|
|
189
189
|
secret_id: z.ZodUUID;
|
|
190
190
|
version: z.ZodNumber;
|
|
191
|
-
}, z.core.$strict
|
|
191
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
192
|
+
name: z.ZodString;
|
|
193
|
+
secret_name: z.ZodString;
|
|
194
|
+
version: z.ZodNumber;
|
|
195
|
+
}, z.core.$strict>]>>>;
|
|
192
196
|
egress: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
193
197
|
resources: z.ZodPrefault<z.ZodObject<{
|
|
194
198
|
cpu: z.ZodDefault<z.ZodUnion<readonly [z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<4>, z.ZodLiteral<8>]>>;
|
|
@@ -219,6 +223,7 @@ export declare const artifactStudioManifestSchema: z.ZodUnion<readonly [z.ZodObj
|
|
|
219
223
|
data: z.ZodPrefault<z.ZodObject<{
|
|
220
224
|
read: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
221
225
|
write: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
226
|
+
operations: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
222
227
|
}, z.core.$strict>>;
|
|
223
228
|
api: z.ZodPrefault<z.ZodObject<{
|
|
224
229
|
read: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
package/dist/next-project.d.ts
CHANGED
|
@@ -17,6 +17,10 @@ export declare function createNextChildEnv({ homeDir, publicEnv, publicOrigin, e
|
|
|
17
17
|
publicEnv: Record<string, string>;
|
|
18
18
|
publicOrigin: string;
|
|
19
19
|
egressHosts?: readonly string[];
|
|
20
|
+
/**
|
|
21
|
+
* The development lease file. Its path in SEQUENCE_ARTIFACT_RUNTIME_FILE is what marks the
|
|
22
|
+
* runtime as a development lease (`runtimeConfig.development`); a hosted runtime never sets it.
|
|
23
|
+
*/
|
|
20
24
|
runtimeFile?: string;
|
|
21
25
|
source?: NodeJS.ProcessEnv;
|
|
22
26
|
}): NodeJS.ProcessEnv;
|
package/dist/sdk.d.ts
CHANGED
|
@@ -13,6 +13,17 @@ export interface SequenceStorageOptions {
|
|
|
13
13
|
}) => void;
|
|
14
14
|
}
|
|
15
15
|
export interface SequenceStorage {
|
|
16
|
+
/** Host-owned, caller-authorized PDF/image dialog. Requires a pinned version and declared Storage scope.
|
|
17
|
+
* Older Atlas builds omit this method; feature-detect before calling. */
|
|
18
|
+
preview?(input: {
|
|
19
|
+
workspaceId: string;
|
|
20
|
+
fileId: string;
|
|
21
|
+
versionId: string;
|
|
22
|
+
filename: string;
|
|
23
|
+
mime: string;
|
|
24
|
+
}): Promise<{
|
|
25
|
+
opened: true;
|
|
26
|
+
}>;
|
|
16
27
|
upload(workspaceId: string, input: StartStorageUpload, file: Blob, options?: SequenceStorageOptions): Promise<StorageUploadSession>;
|
|
17
28
|
resume(workspaceId: string, uploadId: string, file: Blob, options?: SequenceStorageOptions): Promise<StorageUploadSession>;
|
|
18
29
|
}
|
|
@@ -400,9 +411,7 @@ export declare function createArtifactStorageClient(sdk?: SequenceArtifactSdk):
|
|
|
400
411
|
size: number | null;
|
|
401
412
|
contentType: string | null;
|
|
402
413
|
}>;
|
|
403
|
-
createFolder: (name: string, parentId
|
|
404
|
-
/** The specific step run (the assignee's task) — the step-scoped id reviewers hold access to. */
|
|
405
|
-
? /** The specific step run (the assignee's task) — the step-scoped id reviewers hold access to. */: string | null, { idempotencyKey, }?: {
|
|
414
|
+
createFolder: (name: string, parentId?: string | null, { idempotencyKey, }?: {
|
|
406
415
|
idempotencyKey?: string;
|
|
407
416
|
}) => Promise<{
|
|
408
417
|
id: string;
|
|
@@ -18,6 +18,12 @@ export declare function isArtifactApiPath(path: string): boolean;
|
|
|
18
18
|
export declare const ARTIFACT_PROCESS_RUN_STATUSES: readonly ["pending", "running", "completed", "failed", "cancelled", "timed_out"];
|
|
19
19
|
/** Lattice lists at most this many runs per call. */
|
|
20
20
|
export declare const ARTIFACT_PROCESS_RUNS_LIMIT = 500;
|
|
21
|
+
/** A run page can filter to at most this many entities of one type. */
|
|
22
|
+
export declare const ARTIFACT_PROCESS_RUN_ENTITIES_LIMIT = 500;
|
|
23
|
+
/** A run share adds collaborators to at most this many explicitly assigned human nodes. */
|
|
24
|
+
export declare const ARTIFACT_PROCESS_SHARE_NODES_LIMIT = 100;
|
|
25
|
+
/** An owner may list at most this many sharing authorities per page. */
|
|
26
|
+
export declare const ARTIFACT_PROCESS_SHARING_AUTHORITIES_LIMIT = 50;
|
|
21
27
|
/** A run-events read returns at most this many events per call, as the run's stream drains them. */
|
|
22
28
|
export declare const ARTIFACT_PROCESS_RUN_EVENTS_LIMIT = 1000;
|
|
23
29
|
/** Which of the user's human steps to list: parked and waiting on them, or already done. */
|
|
@@ -31,6 +37,8 @@ export declare const ARTIFACT_PROCESS_PRINCIPAL_SEARCH_LIMIT = 50;
|
|
|
31
37
|
export declare const ARTIFACT_PROCESS_PRINCIPAL_TYPES: readonly ["user", "group"];
|
|
32
38
|
/** A start seeds assignments for at most this many human nodes. */
|
|
33
39
|
export declare const ARTIFACT_PROCESS_START_ASSIGNMENT_NODES = 64;
|
|
40
|
+
/** The data plane's own idempotency-key rule (`orm-data-plane/src/runtime.ts`), so a key the SDK sends is one it accepts. */
|
|
41
|
+
export declare const artifactOrmIdempotencyKeySchema: z.ZodString;
|
|
34
42
|
export declare const ARTIFACT_FLAGS_PER_REQUEST = 32;
|
|
35
43
|
/** The audit actions an application may record. Sign-in, identity merges and webhooks stay Atlas's. */
|
|
36
44
|
export declare const ARTIFACT_AUDIT_ACTIONS: readonly ["view", "search", "create", "update", "delete", "download", "upload", "export", "import", "share", "submit", "approve", "reject", "execute"];
|
|
@@ -64,6 +72,7 @@ export declare const artifactServerRequestSchema: z.ZodDiscriminatedUnion<[z.Zod
|
|
|
64
72
|
op: z.ZodString;
|
|
65
73
|
variables: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
66
74
|
jsonScalars: z.ZodOptional<z.ZodLiteral<"lossless-v1">>;
|
|
75
|
+
idempotencyKey: z.ZodOptional<z.ZodString>;
|
|
67
76
|
}, z.core.$strict>, z.ZodObject<{
|
|
68
77
|
operation: z.ZodLiteral<"storage">;
|
|
69
78
|
mode: z.ZodDefault<z.ZodEnum<{
|
|
@@ -128,6 +137,29 @@ export declare const artifactServerRequestSchema: z.ZodDiscriminatedUnion<[z.Zod
|
|
|
128
137
|
}, z.core.$strict>>;
|
|
129
138
|
limit: z.ZodOptional<z.ZodNumber>;
|
|
130
139
|
archived: z.ZodOptional<z.ZodBoolean>;
|
|
140
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
141
|
+
operation: z.ZodLiteral<"lattice_runs_page">;
|
|
142
|
+
mode: z.ZodDefault<z.ZodEnum<{
|
|
143
|
+
service: "service";
|
|
144
|
+
user: "user";
|
|
145
|
+
}>>;
|
|
146
|
+
process: z.ZodString;
|
|
147
|
+
statuses: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
148
|
+
running: "running";
|
|
149
|
+
failed: "failed";
|
|
150
|
+
cancelled: "cancelled";
|
|
151
|
+
pending: "pending";
|
|
152
|
+
completed: "completed";
|
|
153
|
+
timed_out: "timed_out";
|
|
154
|
+
}>>>;
|
|
155
|
+
entity: z.ZodOptional<z.ZodObject<{
|
|
156
|
+
type: z.ZodString;
|
|
157
|
+
id: z.ZodOptional<z.ZodString>;
|
|
158
|
+
ids: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
159
|
+
}, z.core.$strict>>;
|
|
160
|
+
limit: z.ZodOptional<z.ZodNumber>;
|
|
161
|
+
cursor: z.ZodOptional<z.ZodString>;
|
|
162
|
+
archived: z.ZodOptional<z.ZodBoolean>;
|
|
131
163
|
}, z.core.$strict>, z.ZodObject<{
|
|
132
164
|
operation: z.ZodLiteral<"lattice_run">;
|
|
133
165
|
mode: z.ZodDefault<z.ZodEnum<{
|
|
@@ -184,6 +216,36 @@ export declare const artifactServerRequestSchema: z.ZodDiscriminatedUnion<[z.Zod
|
|
|
184
216
|
}>>;
|
|
185
217
|
runId: z.ZodString;
|
|
186
218
|
stepId: z.ZodString;
|
|
219
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
220
|
+
delegateSharing: z.ZodOptional<z.ZodObject<{
|
|
221
|
+
audience: z.ZodLiteral<"current_app">;
|
|
222
|
+
}, z.core.$strict>>;
|
|
223
|
+
users: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
224
|
+
groups: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
225
|
+
operation: z.ZodLiteral<"lattice_share_run">;
|
|
226
|
+
mode: z.ZodDefault<z.ZodEnum<{
|
|
227
|
+
service: "service";
|
|
228
|
+
user: "user";
|
|
229
|
+
}>>;
|
|
230
|
+
runId: z.ZodString;
|
|
231
|
+
nodeIds: z.ZodArray<z.ZodString>;
|
|
232
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
233
|
+
operation: z.ZodLiteral<"lattice_sharing_authorities">;
|
|
234
|
+
mode: z.ZodDefault<z.ZodEnum<{
|
|
235
|
+
service: "service";
|
|
236
|
+
user: "user";
|
|
237
|
+
}>>;
|
|
238
|
+
runId: z.ZodString;
|
|
239
|
+
limit: z.ZodOptional<z.ZodNumber>;
|
|
240
|
+
cursor: z.ZodOptional<z.ZodUUID>;
|
|
241
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
242
|
+
operation: z.ZodLiteral<"lattice_sharing_authority_revoke">;
|
|
243
|
+
mode: z.ZodDefault<z.ZodEnum<{
|
|
244
|
+
service: "service";
|
|
245
|
+
user: "user";
|
|
246
|
+
}>>;
|
|
247
|
+
runId: z.ZodString;
|
|
248
|
+
authorityId: z.ZodUUID;
|
|
187
249
|
}, z.core.$strict>, z.ZodObject<{
|
|
188
250
|
operation: z.ZodLiteral<"lattice_cancel">;
|
|
189
251
|
mode: z.ZodDefault<z.ZodEnum<{
|