@sequenceholdings/artifact-studio 0.2.5 → 0.3.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 +35 -0
- package/VERSION-PIN.md +14 -3
- package/dist/api.d.ts +11 -1
- package/dist/api.js +26 -9
- package/dist/atlas-ui-css-plugin.d.ts +11 -7
- package/dist/atlas-ui-css-plugin.js +38 -10
- package/dist/build.d.ts +2 -2
- package/dist/build.js +14 -6
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +108 -37
- package/dist/dev-link.d.ts +1 -1
- package/dist/dev-link.js +2 -1
- package/dist/gateway/admission.d.ts +55 -0
- package/dist/gateway/admission.js +132 -0
- package/dist/gateway/control.d.ts +91 -0
- package/dist/gateway/control.js +113 -0
- package/dist/gateway/hosted-proxy.d.ts +36 -0
- package/dist/gateway/hosted-proxy.js +347 -0
- package/dist/gateway/release-admission.d.ts +23 -0
- package/dist/gateway/release-admission.js +259 -0
- package/dist/gateway/server.d.ts +29 -0
- package/dist/gateway/server.js +298 -0
- package/dist/gateway/session.d.ts +71 -0
- package/dist/gateway/session.js +143 -0
- package/dist/gateway/usage.d.ts +37 -0
- package/dist/gateway/usage.js +46 -0
- package/dist/gateway-main.d.ts +1 -0
- package/dist/gateway-main.js +84 -0
- package/dist/git-clone.d.ts +23 -1
- package/dist/git-clone.js +32 -15
- package/dist/manifest-next.d.ts +217 -0
- package/dist/manifest-next.js +120 -0
- package/dist/manifest-static.d.ts +76 -0
- package/dist/manifest-static.js +124 -0
- package/dist/manifest.d.ts +124 -19
- package/dist/manifest.js +12 -118
- package/dist/next/bridge.d.ts +47 -0
- package/dist/next/bridge.js +229 -0
- package/dist/next/browser-logs.d.ts +7 -0
- package/dist/next/browser-logs.js +51 -0
- package/dist/next/browser.d.ts +6 -0
- package/dist/next/browser.js +57 -0
- package/dist/next/protocol.d.ts +73 -0
- package/dist/next/protocol.js +56 -0
- package/dist/next/release.d.ts +15 -0
- package/dist/next/release.js +46 -0
- package/dist/next/requests.d.ts +15 -0
- package/dist/next/requests.js +59 -0
- package/dist/next/routes.d.ts +4 -0
- package/dist/next/routes.js +21 -0
- package/dist/next/streams.d.ts +16 -0
- package/dist/next/streams.js +132 -0
- package/dist/next/theme.d.ts +10 -0
- package/dist/next/theme.js +45 -0
- package/dist/next/transport.d.ts +15 -0
- package/dist/next/transport.js +13 -0
- package/dist/next-build.d.ts +29 -0
- package/dist/next-build.js +129 -0
- package/dist/next-cache/adapter-runtime.d.ts +51 -0
- package/dist/next-cache/adapter-runtime.js +73 -0
- package/dist/next-cache/component-handler.d.ts +3 -0
- package/dist/next-cache/component-handler.js +10 -0
- package/dist/next-cache/components.d.ts +20 -0
- package/dist/next-cache/components.js +113 -0
- package/dist/next-cache/config.d.ts +9 -0
- package/dist/next-cache/config.js +78 -0
- package/dist/next-cache/incremental-handler.d.ts +18 -0
- package/dist/next-cache/incremental-handler.js +17 -0
- package/dist/next-cache/incremental.d.ts +27 -0
- package/dist/next-cache/incremental.js +99 -0
- package/dist/next-cache/index.d.ts +18 -0
- package/dist/next-cache/index.js +16 -0
- package/dist/next-cache/memory-store.d.ts +27 -0
- package/dist/next-cache/memory-store.js +118 -0
- package/dist/next-cache/redis-scripts.d.ts +3 -0
- package/dist/next-cache/redis-scripts.js +85 -0
- package/dist/next-cache/redis-store.d.ts +18 -0
- package/dist/next-cache/redis-store.js +56 -0
- package/dist/next-cache/runtime.d.ts +29 -0
- package/dist/next-cache/runtime.js +63 -0
- package/dist/next-cache/serialization.d.ts +39 -0
- package/dist/next-cache/serialization.js +333 -0
- package/dist/next-cache/store.d.ts +54 -0
- package/dist/next-cache/store.js +45 -0
- package/dist/next-cache/valkey-transport.d.ts +52 -0
- package/dist/next-cache/valkey-transport.js +189 -0
- package/dist/next-dev-control.d.ts +48 -0
- package/dist/next-dev-control.js +63 -0
- package/dist/next-dev-process.d.ts +17 -0
- package/dist/next-dev-process.js +110 -0
- package/dist/next-dev.d.ts +12 -0
- package/dist/next-dev.js +267 -0
- package/dist/next-image-config.d.ts +9 -0
- package/dist/next-image-config.js +101 -0
- package/dist/next-project.d.ts +22 -0
- package/dist/next-project.js +64 -0
- package/dist/next-public-fonts-local.d.ts +7 -0
- package/dist/next-public-fonts-local.js +64 -0
- package/dist/next-public-fonts.d.ts +13 -0
- package/dist/next-public-fonts.js +49 -0
- package/dist/next-source-hash.d.ts +14 -0
- package/dist/next-source-hash.js +18 -0
- package/dist/next-source.d.ts +35 -0
- package/dist/next-source.js +155 -0
- package/dist/next.d.ts +9 -0
- package/dist/next.js +5 -0
- package/dist/project.d.ts +3 -0
- package/dist/project.js +9 -1
- package/dist/runtime-contracts.d.ts +229 -0
- package/dist/runtime-contracts.js +224 -0
- package/dist/sdk.d.ts +420 -1
- package/dist/sdk.js +35 -0
- package/dist/server/broker-contract.d.ts +68 -0
- package/dist/server/broker-contract.js +33 -0
- package/dist/server/client.d.ts +67 -0
- package/dist/server/client.js +159 -0
- package/dist/server/logger.d.ts +26 -0
- package/dist/server/logger.js +24 -0
- package/dist/server/release-forms.d.ts +13 -0
- package/dist/server/release-forms.js +124 -0
- package/dist/server/request-context.d.ts +74 -0
- package/dist/server/request-context.js +103 -0
- package/dist/server/runtime-config.d.ts +53 -0
- package/dist/server/runtime-config.js +64 -0
- package/dist/server.d.ts +8 -0
- package/dist/server.js +4 -0
- package/dist/templates/nextjs/.artifactignore +14 -0
- package/dist/templates/nextjs/README.md +151 -0
- package/dist/templates/nextjs/app/about/page.tsx +26 -0
- package/dist/templates/nextjs/app/actions.ts +18 -0
- package/dist/templates/nextjs/app/api/health/route.ts +6 -0
- package/dist/templates/nextjs/app/components/counter.tsx +19 -0
- package/dist/templates/nextjs/app/components/greeting-form.tsx +38 -0
- package/dist/templates/nextjs/app/globals.css +4 -0
- package/dist/templates/nextjs/app/layout.tsx +27 -0
- package/dist/templates/nextjs/app/page.tsx +51 -0
- package/dist/templates/nextjs/app/providers.tsx +107 -0
- package/dist/templates/nextjs/artifact.bundle.yml +55 -0
- package/dist/templates/nextjs/next.config.mjs +27 -0
- package/dist/templates/nextjs/package.json +32 -0
- package/dist/templates/nextjs/pnpm-workspace.yaml +8 -0
- package/dist/templates/nextjs/postcss.config.mjs +5 -0
- package/dist/templates/nextjs/tsconfig.json +20 -0
- package/dist/templates/react-vite/artifact.bundle.yml +0 -1
- package/dist/templates/react-vite/package.json +1 -1
- package/dist/tunnel/broker.d.ts +75 -0
- package/dist/tunnel/broker.js +451 -0
- package/dist/tunnel/client.d.ts +22 -0
- package/dist/tunnel/client.js +177 -0
- package/dist/tunnel/index.d.ts +6 -0
- package/dist/tunnel/index.js +4 -0
- package/dist/tunnel/io.d.ts +33 -0
- package/dist/tunnel/io.js +108 -0
- package/dist/tunnel/peer.d.ts +25 -0
- package/dist/tunnel/peer.js +147 -0
- package/dist/tunnel/protocol.d.ts +69 -0
- package/dist/tunnel/protocol.js +196 -0
- package/dist/tunnel/redis-ownership.d.ts +38 -0
- package/dist/tunnel/redis-ownership.js +100 -0
- package/dist/tunnel/redis-socket.d.ts +13 -0
- package/dist/tunnel/redis-socket.js +48 -0
- package/dist/tunnel/redis-types.d.ts +70 -0
- package/dist/tunnel/redis-types.js +9 -0
- package/dist/tunnel/redis.d.ts +53 -0
- package/dist/tunnel/redis.js +391 -0
- package/dist/tunnel/transport.d.ts +17 -0
- package/dist/tunnel/transport.js +1 -0
- package/package.json +68 -4
- package/templates/nextjs/.artifactignore +14 -0
- package/templates/nextjs/README.md +151 -0
- package/templates/nextjs/app/about/page.tsx +26 -0
- package/templates/nextjs/app/actions.ts +18 -0
- package/templates/nextjs/app/api/health/route.ts +6 -0
- package/templates/nextjs/app/components/counter.tsx +19 -0
- package/templates/nextjs/app/components/greeting-form.tsx +38 -0
- package/templates/nextjs/app/globals.css +4 -0
- package/templates/nextjs/app/layout.tsx +27 -0
- package/templates/nextjs/app/page.tsx +51 -0
- package/templates/nextjs/app/providers.tsx +107 -0
- package/templates/nextjs/artifact.bundle.yml +55 -0
- package/templates/nextjs/next.config.mjs +27 -0
- package/templates/nextjs/package.json +32 -0
- package/templates/nextjs/pnpm-workspace.yaml +8 -0
- package/templates/nextjs/postcss.config.mjs +5 -0
- package/templates/nextjs/tsconfig.json +20 -0
- package/templates/react-vite/artifact.bundle.yml +0 -1
- package/templates/react-vite/package.json +1 -1
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { createHmac, createPrivateKey, randomUUID, sign, timingSafeEqual } from 'node:crypto';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { artifactRequestContextSchema } from '../server/request-context.js';
|
|
4
|
+
import { normalizePublicOrigin } from '../tunnel/protocol.js';
|
|
5
|
+
import { artifactNextFrameworkBuildIdSchema } from '../runtime-contracts.js';
|
|
6
|
+
export const GATEWAY_SESSION_COOKIE = '__Host-atlas-artifact-session';
|
|
7
|
+
export function gatewayReleaseCookieName(deploymentId) {
|
|
8
|
+
if (!/^[a-zA-Z0-9_-]{1,128}$/.test(deploymentId))
|
|
9
|
+
throw new Error('Invalid deployment identifier');
|
|
10
|
+
return `${GATEWAY_SESSION_COOKIE}-${deploymentId}`;
|
|
11
|
+
}
|
|
12
|
+
const origin = z.string().refine(value => { try {
|
|
13
|
+
return normalizePublicOrigin(value) === value;
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return false;
|
|
17
|
+
} });
|
|
18
|
+
const id = z.string().min(1).max(255);
|
|
19
|
+
const sessionSchema = z.object({
|
|
20
|
+
version: z.literal(1), kind: z.enum(['development', 'hosted']),
|
|
21
|
+
sessionId: id.nullable(), deploymentId: id.nullable(),
|
|
22
|
+
frameworkBuildId: artifactNextFrameworkBuildIdSchema.nullable(), projectId: id, ownerId: id,
|
|
23
|
+
environment: id, publicOrigin: origin, expiresAt: z.number().int().positive(), maxExpiresAt: z.number().int().positive().optional(), published: z.boolean(),
|
|
24
|
+
bridge: artifactRequestContextSchema.shape.bridge,
|
|
25
|
+
lattice: artifactRequestContextSchema.shape.lattice.optional(),
|
|
26
|
+
taskRoutes: artifactRequestContextSchema.shape.taskRoutes,
|
|
27
|
+
}).strict().refine(value => value.kind === 'development'
|
|
28
|
+
? value.sessionId !== null && value.deploymentId === null && value.frameworkBuildId === null && !value.published
|
|
29
|
+
: value.sessionId === null && value.deploymentId !== null && value.frameworkBuildId !== null);
|
|
30
|
+
function sameSessionScope(left, right) {
|
|
31
|
+
return left.ownerId === right.ownerId && left.projectId === right.projectId &&
|
|
32
|
+
left.environment === right.environment && left.publicOrigin === right.publicOrigin &&
|
|
33
|
+
left.kind === right.kind && left.published === right.published;
|
|
34
|
+
}
|
|
35
|
+
/** Active use renews the idle window, but never extends the original admission's eight-hour bound. */
|
|
36
|
+
export function renewGatewaySession({ session, authorizedExpiresAt, now = Date.now() }) {
|
|
37
|
+
if (session.expiresAt <= now || session.expiresAt - now > 15 * 60_000)
|
|
38
|
+
return null;
|
|
39
|
+
const expiresAt = Math.min(authorizedExpiresAt, session.maxExpiresAt ?? session.expiresAt);
|
|
40
|
+
return expiresAt > session.expiresAt ? { ...session, expiresAt } : null;
|
|
41
|
+
}
|
|
42
|
+
export function gatewaySessionCookies({ session, key, cookie, renewal = false, now = Date.now() }) {
|
|
43
|
+
const maxAge = Math.max(0, Math.floor((session.expiresAt - now) / 1000));
|
|
44
|
+
const value = `${encodeGatewaySession({ session, key })}; Path=/; HttpOnly; Secure; SameSite=Strict; Max-Age=${maxAge}`;
|
|
45
|
+
const current = renewal ? decodeGatewaySession({ cookie, key, origin: session.publicOrigin, now }) : session;
|
|
46
|
+
if (!current || !sameSessionScope(current, session))
|
|
47
|
+
return [];
|
|
48
|
+
const sameTarget = current.deploymentId === session.deploymentId && current.sessionId === session.sessionId;
|
|
49
|
+
const cookies = sameTarget ? [`${GATEWAY_SESSION_COOKIE}=${value}`] : [];
|
|
50
|
+
if (session.deploymentId)
|
|
51
|
+
cookies.push(`${gatewayReleaseCookieName(session.deploymentId)}=${value}`);
|
|
52
|
+
if (!renewal) {
|
|
53
|
+
const selected = session.deploymentId ? gatewayReleaseCookieName(session.deploymentId) : '';
|
|
54
|
+
const retained = [...new Set((cookie ?? '').split(';').map(item => item.trim().split('=', 1)[0])
|
|
55
|
+
.filter(name => name.startsWith(`${GATEWAY_SESSION_COOKIE}-`) && name !== selected && /^[a-zA-Z0-9_-]+$/.test(name)))];
|
|
56
|
+
const sameScope = [], revoked = [];
|
|
57
|
+
for (const name of retained) {
|
|
58
|
+
const release = decodeSignedCookie({ cookie, key, origin: session.publicOrigin, now,
|
|
59
|
+
deploymentId: name.slice(GATEWAY_SESSION_COOKIE.length + 1) });
|
|
60
|
+
if (release && sameSessionScope(release, session))
|
|
61
|
+
sameScope.push(name);
|
|
62
|
+
else
|
|
63
|
+
revoked.push(name);
|
|
64
|
+
}
|
|
65
|
+
for (const name of [...revoked, ...sameScope.slice(0, Math.max(0, sameScope.length - 5))]) {
|
|
66
|
+
cookies.push(`${name}=; Path=/; HttpOnly; Secure; SameSite=Strict; Max-Age=0`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return cookies;
|
|
70
|
+
}
|
|
71
|
+
function mac(value, key) {
|
|
72
|
+
if (Buffer.byteLength(key) < 32)
|
|
73
|
+
throw new Error('Gateway session key requires at least 32 bytes');
|
|
74
|
+
return createHmac('sha256', key).update(value).digest();
|
|
75
|
+
}
|
|
76
|
+
export function encodeGatewaySession({ session, key }) {
|
|
77
|
+
const payload = Buffer.from(JSON.stringify(sessionSchema.parse(session))).toString('base64url');
|
|
78
|
+
return `${payload}.${mac(payload, key).toString('base64url')}`;
|
|
79
|
+
}
|
|
80
|
+
function decodeSignedCookie({ cookie, key, origin: expectedOrigin, now = Date.now(), deploymentId }) {
|
|
81
|
+
try {
|
|
82
|
+
if (!cookie || cookie.length > 32768)
|
|
83
|
+
return null;
|
|
84
|
+
const name = deploymentId ? gatewayReleaseCookieName(deploymentId) : GATEWAY_SESSION_COOKIE;
|
|
85
|
+
const values = cookie.split(';').map(value => value.trim()).filter(value => value.startsWith(`${name}=`));
|
|
86
|
+
if (values.length !== 1)
|
|
87
|
+
return null;
|
|
88
|
+
const [payload, signature, extra] = values[0].slice(name.length + 1).split('.');
|
|
89
|
+
if (!payload || !signature || extra || !/^[A-Za-z0-9_-]+$/.test(payload) || !/^[A-Za-z0-9_-]+$/.test(signature))
|
|
90
|
+
return null;
|
|
91
|
+
const expected = mac(payload, key), actual = Buffer.from(signature, 'base64url');
|
|
92
|
+
if (actual.length !== expected.length || !timingSafeEqual(expected, actual))
|
|
93
|
+
return null;
|
|
94
|
+
const session = sessionSchema.parse(JSON.parse(Buffer.from(payload, 'base64url').toString('utf8')));
|
|
95
|
+
return session.expiresAt > now && session.publicOrigin === expectedOrigin &&
|
|
96
|
+
(!deploymentId || session.deploymentId === deploymentId) ? session : null;
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/** Retained releases preserve tabs, never a different login or runtime scope. */
|
|
103
|
+
export function decodeGatewaySession(input) {
|
|
104
|
+
const current = decodeSignedCookie({ ...input, deploymentId: undefined });
|
|
105
|
+
if (!current)
|
|
106
|
+
return null;
|
|
107
|
+
const matchesBuild = (candidate) => !input.frameworkBuildId || candidate.frameworkBuildId === input.frameworkBuildId;
|
|
108
|
+
if (input.deploymentId) {
|
|
109
|
+
const selected = current.deploymentId === input.deploymentId ? current : decodeSignedCookie(input);
|
|
110
|
+
return selected && sameSessionScope(current, selected) && matchesBuild(selected) ? selected : null;
|
|
111
|
+
}
|
|
112
|
+
// Next's build id identifies immutable assets, not an activation. A rollback
|
|
113
|
+
// can reuse that build: prefer the current activation when it matches.
|
|
114
|
+
if (matchesBuild(current))
|
|
115
|
+
return current;
|
|
116
|
+
const names = [...new Set((input.cookie ?? '').split(';').map(item => item.trim().split('=', 1)[0])
|
|
117
|
+
.filter(name => name.startsWith(`${GATEWAY_SESSION_COOKIE}-`) && /^[a-zA-Z0-9_-]+$/.test(name)))];
|
|
118
|
+
const matches = names.map(name => decodeSignedCookie({ ...input,
|
|
119
|
+
deploymentId: name.slice(GATEWAY_SESSION_COOKIE.length + 1),
|
|
120
|
+
})).filter((candidate) => candidate !== null && sameSessionScope(current, candidate) && matchesBuild(candidate));
|
|
121
|
+
// Several retained activations may share identical output. Prefer the latest
|
|
122
|
+
// admitted session deterministically; every candidate is reauthorized below.
|
|
123
|
+
return matches.sort((left, right) => right.expiresAt - left.expiresAt || (left.deploymentId ?? '').localeCompare(right.deploymentId ?? ''))[0] ?? null;
|
|
124
|
+
}
|
|
125
|
+
export function signGatewayContext({ session, method, path, privateKey, keyId, now = Math.floor(Date.now() / 1000), capabilities = [], serviceAccountId = null, requestId = randomUUID() }) {
|
|
126
|
+
const context = artifactRequestContextSchema.parse({
|
|
127
|
+
v: 1, iss: 'atlas-artifact-gateway', aud: `artifact:${session.projectId}`,
|
|
128
|
+
sub: session.ownerId, projectId: session.projectId, environment: session.environment,
|
|
129
|
+
mode: session.kind === 'development' ? 'development' : session.published ? 'published' : 'preview',
|
|
130
|
+
deploymentId: session.deploymentId, devSessionId: session.sessionId, requestId,
|
|
131
|
+
method, path, iat: now, exp: now + 60, capabilities, serviceAccountId,
|
|
132
|
+
bridge: session.bridge, theme: 'system',
|
|
133
|
+
lattice: session.lattice ?? { runId: null, nodeId: null, stepId: null, processId: null },
|
|
134
|
+
...(session.taskRoutes ? { taskRoutes: session.taskRoutes } : {}),
|
|
135
|
+
});
|
|
136
|
+
const header = Buffer.from(JSON.stringify({ alg: 'EdDSA', typ: 'JWT', kid: keyId })).toString('base64url');
|
|
137
|
+
const payload = Buffer.from(JSON.stringify(context)).toString('base64url');
|
|
138
|
+
const key = createPrivateKey(privateKey);
|
|
139
|
+
if (key.asymmetricKeyType !== 'ed25519')
|
|
140
|
+
throw new Error('Gateway context key must be Ed25519');
|
|
141
|
+
const signed = `${header}.${payload}`;
|
|
142
|
+
return `${signed}.${sign(null, Buffer.from(signed), key).toString('base64url')}`;
|
|
143
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export interface GatewayUsageEvent {
|
|
2
|
+
eventId: string;
|
|
3
|
+
requestId: string;
|
|
4
|
+
projectId: string;
|
|
5
|
+
deploymentId?: string;
|
|
6
|
+
devSessionId?: string;
|
|
7
|
+
environment: string;
|
|
8
|
+
startedAt: string;
|
|
9
|
+
endedAt: string;
|
|
10
|
+
measurements: {
|
|
11
|
+
requestCount: 1;
|
|
12
|
+
durationMs: number;
|
|
13
|
+
};
|
|
14
|
+
outcome: 'success' | 'error' | 'cancelled';
|
|
15
|
+
httpStatus: number;
|
|
16
|
+
}
|
|
17
|
+
/** Bounded delivery of observations; accepted records are deduplicated durably by Atlas.
|
|
18
|
+
* Process loss can drop pending observations, so these are not provider billing totals.
|
|
19
|
+
*/
|
|
20
|
+
export declare class GatewayUsageReporter {
|
|
21
|
+
private readonly options;
|
|
22
|
+
private readonly queue;
|
|
23
|
+
private readonly pending;
|
|
24
|
+
constructor(options: {
|
|
25
|
+
send: (event: GatewayUsageEvent) => Promise<void>;
|
|
26
|
+
onFailure: (event: {
|
|
27
|
+
kind: string;
|
|
28
|
+
requestId: string;
|
|
29
|
+
}) => void;
|
|
30
|
+
capacity?: number;
|
|
31
|
+
concurrency?: number;
|
|
32
|
+
});
|
|
33
|
+
record(event: GatewayUsageEvent): void;
|
|
34
|
+
private pump;
|
|
35
|
+
private deliver;
|
|
36
|
+
flush(): Promise<void>;
|
|
37
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/** Bounded delivery of observations; accepted records are deduplicated durably by Atlas.
|
|
2
|
+
* Process loss can drop pending observations, so these are not provider billing totals.
|
|
3
|
+
*/
|
|
4
|
+
export class GatewayUsageReporter {
|
|
5
|
+
options;
|
|
6
|
+
queue = [];
|
|
7
|
+
pending = new Set();
|
|
8
|
+
constructor(options) {
|
|
9
|
+
this.options = options;
|
|
10
|
+
}
|
|
11
|
+
record(event) {
|
|
12
|
+
if (this.queue.length + this.pending.size >= (this.options.capacity ?? 1024)) {
|
|
13
|
+
this.options.onFailure({ kind: 'usage_queue_full', requestId: event.requestId });
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
this.queue.push(event);
|
|
17
|
+
this.pump();
|
|
18
|
+
}
|
|
19
|
+
pump() {
|
|
20
|
+
while (this.queue.length && this.pending.size < (this.options.concurrency ?? 8)) {
|
|
21
|
+
const event = this.queue.shift();
|
|
22
|
+
const pending = this.deliver(event).finally(() => { this.pending.delete(pending); this.pump(); });
|
|
23
|
+
this.pending.add(pending);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
async deliver(event) {
|
|
27
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
28
|
+
try {
|
|
29
|
+
await this.options.send(event);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
const status = typeof error === 'object' && error !== null && 'status' in error ? error.status : undefined;
|
|
34
|
+
if (typeof status === 'number' && status >= 400 && status < 500 && status !== 429)
|
|
35
|
+
break;
|
|
36
|
+
if (attempt < 2)
|
|
37
|
+
await new Promise(resolve => setTimeout(resolve, 100 * 2 ** attempt));
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
this.options.onFailure({ kind: 'usage_delivery_failed', requestId: event.requestId });
|
|
41
|
+
}
|
|
42
|
+
async flush() {
|
|
43
|
+
while (this.pending.size)
|
|
44
|
+
await Promise.all(this.pending);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { Redis } from 'ioredis';
|
|
3
|
+
import { ArtifactGatewayControl } from './gateway/control.js';
|
|
4
|
+
import { createArtifactGateway } from './gateway/server.js';
|
|
5
|
+
import { RedisTunnelOwnership, RedisTunnelRelay } from './tunnel/redis.js';
|
|
6
|
+
import { ArtifactRequestAdmission } from './gateway/admission.js';
|
|
7
|
+
function required(name) {
|
|
8
|
+
const value = process.env[name];
|
|
9
|
+
if (!value)
|
|
10
|
+
throw new Error(`Missing required gateway configuration: ${name}`);
|
|
11
|
+
return value;
|
|
12
|
+
}
|
|
13
|
+
/** A separate process/identity from Atlas and all application/build containers. */
|
|
14
|
+
async function main() {
|
|
15
|
+
if (process.env.ARTIFACT_NEXT_ENABLED !== 'true')
|
|
16
|
+
throw new Error('Artifact Next gateway is disabled');
|
|
17
|
+
const redisUrl = required('ARTIFACT_NEXT_REDIS_URL');
|
|
18
|
+
if (new URL(redisUrl).protocol !== 'rediss:')
|
|
19
|
+
throw new Error('Artifact tunnel routing requires private Redis with TLS');
|
|
20
|
+
const namespace = required('APP_ENV'), nodeId = randomUUID();
|
|
21
|
+
const redisOptions = {
|
|
22
|
+
enableOfflineQueue: false, autoResendUnfulfilledCommands: false, maxRetriesPerRequest: 0,
|
|
23
|
+
lazyConnect: true, connectTimeout: 10_000,
|
|
24
|
+
tls: { rejectUnauthorized: true, ...(process.env.REDIS_CA_CERT ? { ca: process.env.REDIS_CA_CERT } : {}) },
|
|
25
|
+
};
|
|
26
|
+
const command = new Redis(redisUrl, redisOptions);
|
|
27
|
+
const publisher = new Redis(redisUrl, redisOptions);
|
|
28
|
+
const subscriber = new Redis(redisUrl, { ...redisOptions, autoResubscribe: false });
|
|
29
|
+
const clients = [command, publisher, subscriber];
|
|
30
|
+
for (const client of clients)
|
|
31
|
+
client.on('error', () => {
|
|
32
|
+
// Provider messages may include connection strings; keep them out of logs.
|
|
33
|
+
process.stderr.write(`${JSON.stringify({ event: 'artifact_gateway.redis_unavailable', nodeId })}\n`);
|
|
34
|
+
});
|
|
35
|
+
await Promise.all(clients.map(client => client.connect()));
|
|
36
|
+
const ownership = new RedisTunnelOwnership({ command, namespace });
|
|
37
|
+
const relay = new RedisTunnelRelay({ nodeId, namespace, ownership, publisher, subscriber });
|
|
38
|
+
await relay.start();
|
|
39
|
+
let stopping = false;
|
|
40
|
+
subscriber.on('ready', () => { if (!stopping)
|
|
41
|
+
void relay.start().catch(() => undefined); });
|
|
42
|
+
const controlUrl = required('ARTIFACT_NEXT_CONTROL_PLANE_URL');
|
|
43
|
+
const gateway = createArtifactGateway({
|
|
44
|
+
control: new ArtifactGatewayControl({ url: controlUrl, token: required('ARTIFACT_NEXT_CONTROL_TOKEN') }),
|
|
45
|
+
runtimeDomain: required('ARTIFACT_NEXT_RUNTIME_DOMAIN'),
|
|
46
|
+
parentOrigin: process.env.ARTIFACT_NEXT_PARENT_ORIGIN ?? new URL(controlUrl).origin,
|
|
47
|
+
gatewayOrigin: required('ARTIFACT_NEXT_GATEWAY_URL'),
|
|
48
|
+
sessionKey: required('ARTIFACT_NEXT_SESSION_KEY'),
|
|
49
|
+
contextPrivateKey: required('ARTIFACT_NEXT_CONTEXT_PRIVATE_KEY'),
|
|
50
|
+
contextKeyId: required('ARTIFACT_NEXT_CONTEXT_KEY_ID'),
|
|
51
|
+
nodeId, routing: ownership, relay,
|
|
52
|
+
admission: new ArtifactRequestAdmission({ redis: command, namespace }),
|
|
53
|
+
resolveLease: sessionId => ownership.resolveLease(sessionId),
|
|
54
|
+
renewLease: lease => ownership.renew(lease, nodeId),
|
|
55
|
+
ready: () => clients.every(client => client.status === 'ready') && relay.isReady(),
|
|
56
|
+
onError: event => process.stderr.write(`${JSON.stringify({ event: 'artifact_gateway.request', ...event, nodeId })}\n`),
|
|
57
|
+
});
|
|
58
|
+
const port = Number(process.env.PORT ?? '8080');
|
|
59
|
+
if (!Number.isSafeInteger(port) || port < 1 || port > 65535)
|
|
60
|
+
throw new Error('Invalid gateway PORT');
|
|
61
|
+
await new Promise((resolve, reject) => {
|
|
62
|
+
gateway.server.once('error', reject);
|
|
63
|
+
gateway.server.listen(port, '0.0.0.0', resolve);
|
|
64
|
+
});
|
|
65
|
+
process.stdout.write(`${JSON.stringify({ event: 'artifact_gateway.ready', nodeId, port })}\n`);
|
|
66
|
+
const stop = async () => {
|
|
67
|
+
if (stopping)
|
|
68
|
+
return;
|
|
69
|
+
stopping = true;
|
|
70
|
+
const deadline = setTimeout(() => { gateway.server.closeAllConnections(); process.exit(1); }, 9_000);
|
|
71
|
+
deadline.unref();
|
|
72
|
+
await gateway.drain();
|
|
73
|
+
await relay.close();
|
|
74
|
+
for (const client of clients)
|
|
75
|
+
client.disconnect();
|
|
76
|
+
clearTimeout(deadline);
|
|
77
|
+
};
|
|
78
|
+
process.once('SIGTERM', () => { void stop(); });
|
|
79
|
+
process.once('SIGINT', () => { void stop(); });
|
|
80
|
+
}
|
|
81
|
+
void main().catch(() => {
|
|
82
|
+
process.stderr.write(`${JSON.stringify({ event: 'artifact_gateway.start_failed' })}\n`);
|
|
83
|
+
process.exit(1);
|
|
84
|
+
});
|
package/dist/git-clone.d.ts
CHANGED
|
@@ -15,11 +15,29 @@ export type RunGitClone = (args: {
|
|
|
15
15
|
destDir: string;
|
|
16
16
|
/** Optional branch, tag, or commit SHA. */
|
|
17
17
|
ref?: string;
|
|
18
|
+
/**
|
|
19
|
+
* `git clone --depth`. Omitted for commit-SHA refs: those clone the
|
|
20
|
+
* default branch in full, then check the SHA out.
|
|
21
|
+
*/
|
|
22
|
+
depth?: number;
|
|
18
23
|
/** PAT used as Basic-auth password (username `git`). */
|
|
19
24
|
pat: string;
|
|
20
25
|
/** Hard ceiling before the git child is killed (default 5m). */
|
|
21
26
|
timeoutMs?: number;
|
|
22
27
|
}) => Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* Argv for `git clone`, excluding the askpass environment.
|
|
30
|
+
* Positive `depth` is a shallow tip clone. Commit SHAs ignore `depth`.
|
|
31
|
+
*/
|
|
32
|
+
export declare function gitCloneArgs({ cloneUrl, destDir, ref, depth, }: {
|
|
33
|
+
cloneUrl: string;
|
|
34
|
+
destDir: string;
|
|
35
|
+
ref?: string;
|
|
36
|
+
depth?: number;
|
|
37
|
+
}): {
|
|
38
|
+
args: string[];
|
|
39
|
+
shaRef?: string;
|
|
40
|
+
};
|
|
23
41
|
/**
|
|
24
42
|
* Run `fn` with GIT_ASKPASS set to a short-lived script that answers username
|
|
25
43
|
* with `git` and password with `pat`. The script is deleted afterward.
|
|
@@ -31,11 +49,15 @@ export declare function withGitAskpass<T>({ pat, fn, }: {
|
|
|
31
49
|
/**
|
|
32
50
|
* Clone with a clean URL + PAT via askpass. Branch/tag refs use `--branch`;
|
|
33
51
|
* commit SHAs clone default HEAD then `git checkout <sha>`.
|
|
52
|
+
* `depth` (repos clone passes 1) fetches only that many commits — a full
|
|
53
|
+
* history walk against git-service reads every object from object storage
|
|
54
|
+
* before the pack starts, which stalls large repos.
|
|
34
55
|
*/
|
|
35
|
-
export declare function runGitClone({ cloneUrl, destDir, ref, pat, timeoutMs, }: {
|
|
56
|
+
export declare function runGitClone({ cloneUrl, destDir, ref, depth, pat, timeoutMs, }: {
|
|
36
57
|
cloneUrl: string;
|
|
37
58
|
destDir: string;
|
|
38
59
|
ref?: string;
|
|
60
|
+
depth?: number;
|
|
39
61
|
pat: string;
|
|
40
62
|
timeoutMs?: number;
|
|
41
63
|
}): Promise<void>;
|
package/dist/git-clone.js
CHANGED
|
@@ -55,6 +55,28 @@ const GIT_LOW_SPEED_LIMIT_BYTES = 1000;
|
|
|
55
55
|
const GIT_LOW_SPEED_TIME_SEC = 180;
|
|
56
56
|
/** Backstop hard deadline: SIGKILL the child even if it trickles above the low-speed floor. */
|
|
57
57
|
const GIT_CLONE_TIMEOUT_MS = 6 * 60_000;
|
|
58
|
+
/**
|
|
59
|
+
* Argv for `git clone`, excluding the askpass environment.
|
|
60
|
+
* Positive `depth` is a shallow tip clone. Commit SHAs ignore `depth`.
|
|
61
|
+
*/
|
|
62
|
+
export function gitCloneArgs({ cloneUrl, destDir, ref, depth, }) {
|
|
63
|
+
const shaRef = ref && looksLikeCommitSha(ref) ? ref : undefined;
|
|
64
|
+
const branchRef = ref && !shaRef ? ref : undefined;
|
|
65
|
+
const args = [
|
|
66
|
+
'-c',
|
|
67
|
+
`http.lowSpeedLimit=${GIT_LOW_SPEED_LIMIT_BYTES}`,
|
|
68
|
+
'-c',
|
|
69
|
+
`http.lowSpeedTime=${GIT_LOW_SPEED_TIME_SEC}`,
|
|
70
|
+
'clone',
|
|
71
|
+
];
|
|
72
|
+
if (depth != null && depth > 0 && !shaRef) {
|
|
73
|
+
args.push('--depth', String(depth));
|
|
74
|
+
}
|
|
75
|
+
if (branchRef)
|
|
76
|
+
args.push('--branch', branchRef);
|
|
77
|
+
args.push(cloneUrl, destDir);
|
|
78
|
+
return shaRef ? { args, shaRef } : { args };
|
|
79
|
+
}
|
|
58
80
|
/** First non-flag arg (the git subcommand), skipping `-c <value>` config pairs. */
|
|
59
81
|
function gitVerb(args) {
|
|
60
82
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -142,8 +164,11 @@ export async function withGitAskpass({ pat, fn, }) {
|
|
|
142
164
|
/**
|
|
143
165
|
* Clone with a clean URL + PAT via askpass. Branch/tag refs use `--branch`;
|
|
144
166
|
* commit SHAs clone default HEAD then `git checkout <sha>`.
|
|
167
|
+
* `depth` (repos clone passes 1) fetches only that many commits — a full
|
|
168
|
+
* history walk against git-service reads every object from object storage
|
|
169
|
+
* before the pack starts, which stalls large repos.
|
|
145
170
|
*/
|
|
146
|
-
export async function runGitClone({ cloneUrl, destDir, ref, pat, timeoutMs, }) {
|
|
171
|
+
export async function runGitClone({ cloneUrl, destDir, ref, depth, pat, timeoutMs, }) {
|
|
147
172
|
let cleanUrl = cloneUrl;
|
|
148
173
|
try {
|
|
149
174
|
const parsed = new URL(cloneUrl);
|
|
@@ -156,23 +181,15 @@ export async function runGitClone({ cloneUrl, destDir, ref, pat, timeoutMs, }) {
|
|
|
156
181
|
catch {
|
|
157
182
|
// leave as-is; git will fail loudly
|
|
158
183
|
}
|
|
159
|
-
// Global `-c` config must precede the subcommand. Low-speed settings bound
|
|
160
|
-
// the fetch so a stalled transport aborts fast rather than wedging the deploy.
|
|
161
|
-
const lowSpeed = [
|
|
162
|
-
'-c',
|
|
163
|
-
`http.lowSpeedLimit=${GIT_LOW_SPEED_LIMIT_BYTES}`,
|
|
164
|
-
'-c',
|
|
165
|
-
`http.lowSpeedTime=${GIT_LOW_SPEED_TIME_SEC}`,
|
|
166
|
-
];
|
|
167
184
|
await withGitAskpass({
|
|
168
185
|
pat,
|
|
169
186
|
fn: async (env) => {
|
|
170
|
-
const args =
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
187
|
+
const { args, shaRef } = gitCloneArgs({
|
|
188
|
+
cloneUrl: cleanUrl,
|
|
189
|
+
destDir,
|
|
190
|
+
...(ref ? { ref } : {}),
|
|
191
|
+
...(depth != null ? { depth } : {}),
|
|
192
|
+
});
|
|
176
193
|
try {
|
|
177
194
|
await spawnGit({ args, env, ...(timeoutMs ? { timeoutMs } : {}) });
|
|
178
195
|
}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/** Approved major/minor profile. Builds additionally record exact patch versions. */
|
|
3
|
+
export declare const ARTIFACT_NEXT_RUNTIME: Readonly<{
|
|
4
|
+
sdk: "sequence";
|
|
5
|
+
node: 24;
|
|
6
|
+
react: 19;
|
|
7
|
+
next: "16.3";
|
|
8
|
+
}>;
|
|
9
|
+
/** Admission policy for new development sessions and builds; retained releases keep their recorded toolchain. */
|
|
10
|
+
export declare const ARTIFACT_NEXT_APPROVED_PACKAGES: Readonly<{
|
|
11
|
+
next: "16.3.4";
|
|
12
|
+
react: "19.2.6";
|
|
13
|
+
'react-dom': "19.2.6";
|
|
14
|
+
}>;
|
|
15
|
+
export declare const ARTIFACT_NEXT_RESOURCE_DEFAULTS: {
|
|
16
|
+
cpu: number;
|
|
17
|
+
memory_mb: number;
|
|
18
|
+
concurrency: number;
|
|
19
|
+
min_instances: number;
|
|
20
|
+
max_instances: number;
|
|
21
|
+
timeout_seconds: number;
|
|
22
|
+
};
|
|
23
|
+
export declare const artifactNextServerCapabilitiesSchema: z.ZodPrefault<z.ZodObject<{
|
|
24
|
+
storage: z.ZodOptional<z.ZodRecord<z.ZodUUID, z.ZodEnum<{
|
|
25
|
+
viewer: "viewer";
|
|
26
|
+
editor: "editor";
|
|
27
|
+
admin: "admin";
|
|
28
|
+
}>>>;
|
|
29
|
+
data: z.ZodPrefault<z.ZodObject<{
|
|
30
|
+
read: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
31
|
+
write: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
32
|
+
}, z.core.$strict>>;
|
|
33
|
+
api: z.ZodPrefault<z.ZodObject<{
|
|
34
|
+
read: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
35
|
+
write: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
36
|
+
}, z.core.$strict>>;
|
|
37
|
+
functions: z.ZodPrefault<z.ZodObject<{
|
|
38
|
+
invoke: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
39
|
+
}, z.core.$strict>>;
|
|
40
|
+
}, z.core.$strict>>;
|
|
41
|
+
export type ArtifactNextServerCapabilities = z.infer<typeof artifactNextServerCapabilitiesSchema>;
|
|
42
|
+
export declare const artifactNextResourceLimitsSchema: z.ZodPrefault<z.ZodObject<{
|
|
43
|
+
cpu: z.ZodDefault<z.ZodUnion<readonly [z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<4>, z.ZodLiteral<8>]>>;
|
|
44
|
+
memory_mb: z.ZodDefault<z.ZodNumber>;
|
|
45
|
+
concurrency: z.ZodDefault<z.ZodNumber>;
|
|
46
|
+
min_instances: z.ZodDefault<z.ZodNumber>;
|
|
47
|
+
max_instances: z.ZodDefault<z.ZodNumber>;
|
|
48
|
+
timeout_seconds: z.ZodDefault<z.ZodNumber>;
|
|
49
|
+
}, z.core.$strict>>;
|
|
50
|
+
export declare const artifactNextSecretNameSchema: z.ZodString;
|
|
51
|
+
export declare const artifactNextSecretBindingSchema: z.ZodObject<{
|
|
52
|
+
name: z.ZodString;
|
|
53
|
+
secret_id: z.ZodUUID;
|
|
54
|
+
version: z.ZodNumber;
|
|
55
|
+
}, z.core.$strict>;
|
|
56
|
+
/** Pinned public assets fetched by the trusted preparation stage before offline compilation. */
|
|
57
|
+
export declare const artifactNextPublicFontSchema: z.ZodObject<{
|
|
58
|
+
path: z.ZodString;
|
|
59
|
+
url: z.ZodString;
|
|
60
|
+
sha256: z.ZodString;
|
|
61
|
+
}, z.core.$strict>;
|
|
62
|
+
export type ArtifactNextPublicFont = z.infer<typeof artifactNextPublicFontSchema>;
|
|
63
|
+
export declare const artifactNextBuildConfigSchema: z.ZodPrefault<z.ZodObject<{
|
|
64
|
+
public_env: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
65
|
+
public_fonts: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
66
|
+
path: z.ZodString;
|
|
67
|
+
url: z.ZodString;
|
|
68
|
+
sha256: z.ZodString;
|
|
69
|
+
}, z.core.$strict>>>;
|
|
70
|
+
}, z.core.$strict>>;
|
|
71
|
+
export declare const artifactNextServerSchema: z.ZodPrefault<z.ZodObject<{
|
|
72
|
+
capabilities: z.ZodPrefault<z.ZodObject<{
|
|
73
|
+
storage: z.ZodOptional<z.ZodRecord<z.ZodUUID, z.ZodEnum<{
|
|
74
|
+
viewer: "viewer";
|
|
75
|
+
editor: "editor";
|
|
76
|
+
admin: "admin";
|
|
77
|
+
}>>>;
|
|
78
|
+
data: z.ZodPrefault<z.ZodObject<{
|
|
79
|
+
read: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
80
|
+
write: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
81
|
+
}, z.core.$strict>>;
|
|
82
|
+
api: z.ZodPrefault<z.ZodObject<{
|
|
83
|
+
read: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
84
|
+
write: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
85
|
+
}, z.core.$strict>>;
|
|
86
|
+
functions: z.ZodPrefault<z.ZodObject<{
|
|
87
|
+
invoke: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
88
|
+
}, z.core.$strict>>;
|
|
89
|
+
}, z.core.$strict>>;
|
|
90
|
+
service_account: z.ZodOptional<z.ZodString>;
|
|
91
|
+
secrets: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
92
|
+
name: z.ZodString;
|
|
93
|
+
secret_id: z.ZodUUID;
|
|
94
|
+
version: z.ZodNumber;
|
|
95
|
+
}, z.core.$strict>>>;
|
|
96
|
+
egress: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
97
|
+
resources: z.ZodPrefault<z.ZodObject<{
|
|
98
|
+
cpu: z.ZodDefault<z.ZodUnion<readonly [z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<4>, z.ZodLiteral<8>]>>;
|
|
99
|
+
memory_mb: z.ZodDefault<z.ZodNumber>;
|
|
100
|
+
concurrency: z.ZodDefault<z.ZodNumber>;
|
|
101
|
+
min_instances: z.ZodDefault<z.ZodNumber>;
|
|
102
|
+
max_instances: z.ZodDefault<z.ZodNumber>;
|
|
103
|
+
timeout_seconds: z.ZodDefault<z.ZodNumber>;
|
|
104
|
+
}, z.core.$strict>>;
|
|
105
|
+
cache: z.ZodOptional<z.ZodObject<{
|
|
106
|
+
capacity_gb: z.ZodNumber;
|
|
107
|
+
}, z.core.$strict>>;
|
|
108
|
+
}, z.core.$strict>>;
|
|
109
|
+
export declare const artifactStudioNextManifestSchema: z.ZodObject<{
|
|
110
|
+
bundle: z.ZodObject<{
|
|
111
|
+
name: z.ZodString;
|
|
112
|
+
schema_version: z.ZodLiteral<2>;
|
|
113
|
+
}, z.core.$strict>;
|
|
114
|
+
artifact: z.ZodObject<{
|
|
115
|
+
project_id: z.ZodString;
|
|
116
|
+
title: z.ZodString;
|
|
117
|
+
description: z.ZodOptional<z.ZodString>;
|
|
118
|
+
type: z.ZodLiteral<"nextjs">;
|
|
119
|
+
app_root: z.ZodDefault<z.ZodString>;
|
|
120
|
+
}, z.core.$strict>;
|
|
121
|
+
runtime: z.ZodPrefault<z.ZodObject<{
|
|
122
|
+
sdk: z.ZodDefault<z.ZodLiteral<"sequence">>;
|
|
123
|
+
node: z.ZodDefault<z.ZodLiteral<24>>;
|
|
124
|
+
react: z.ZodDefault<z.ZodLiteral<19>>;
|
|
125
|
+
next: z.ZodDefault<z.ZodLiteral<"16.3">>;
|
|
126
|
+
}, z.core.$strict>>;
|
|
127
|
+
capabilities: z.ZodPrefault<z.ZodObject<{
|
|
128
|
+
allow_downloads: z.ZodDefault<z.ZodBoolean>;
|
|
129
|
+
allow_popups: z.ZodDefault<z.ZodBoolean>;
|
|
130
|
+
roles: z.ZodOptional<z.ZodObject<{
|
|
131
|
+
definitions: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
132
|
+
description: z.ZodOptional<z.ZodString>;
|
|
133
|
+
default: z.ZodDefault<z.ZodBoolean>;
|
|
134
|
+
inherits: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
135
|
+
grants: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
136
|
+
}, z.core.$strict>>>;
|
|
137
|
+
capabilities: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
138
|
+
description: z.ZodOptional<z.ZodString>;
|
|
139
|
+
}, z.core.$strict>>>;
|
|
140
|
+
}, z.core.$strict>>;
|
|
141
|
+
uses: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
142
|
+
storage: z.ZodOptional<z.ZodRecord<z.ZodUUID, z.ZodEnum<{
|
|
143
|
+
viewer: "viewer";
|
|
144
|
+
editor: "editor";
|
|
145
|
+
admin: "admin";
|
|
146
|
+
}>>>;
|
|
147
|
+
data: z.ZodPrefault<z.ZodObject<{
|
|
148
|
+
read: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
149
|
+
write: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
150
|
+
}, z.core.$strict>>;
|
|
151
|
+
api: z.ZodPrefault<z.ZodObject<{
|
|
152
|
+
read: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
153
|
+
write: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
154
|
+
}, z.core.$strict>>;
|
|
155
|
+
functions: z.ZodPrefault<z.ZodObject<{
|
|
156
|
+
invoke: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
157
|
+
}, z.core.$strict>>;
|
|
158
|
+
}, z.core.$strict>>;
|
|
159
|
+
build: z.ZodPrefault<z.ZodObject<{
|
|
160
|
+
public_env: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
161
|
+
public_fonts: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
162
|
+
path: z.ZodString;
|
|
163
|
+
url: z.ZodString;
|
|
164
|
+
sha256: z.ZodString;
|
|
165
|
+
}, z.core.$strict>>>;
|
|
166
|
+
}, z.core.$strict>>;
|
|
167
|
+
server: z.ZodPrefault<z.ZodObject<{
|
|
168
|
+
capabilities: z.ZodPrefault<z.ZodObject<{
|
|
169
|
+
storage: z.ZodOptional<z.ZodRecord<z.ZodUUID, z.ZodEnum<{
|
|
170
|
+
viewer: "viewer";
|
|
171
|
+
editor: "editor";
|
|
172
|
+
admin: "admin";
|
|
173
|
+
}>>>;
|
|
174
|
+
data: z.ZodPrefault<z.ZodObject<{
|
|
175
|
+
read: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
176
|
+
write: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
177
|
+
}, z.core.$strict>>;
|
|
178
|
+
api: z.ZodPrefault<z.ZodObject<{
|
|
179
|
+
read: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
180
|
+
write: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
181
|
+
}, z.core.$strict>>;
|
|
182
|
+
functions: z.ZodPrefault<z.ZodObject<{
|
|
183
|
+
invoke: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
184
|
+
}, z.core.$strict>>;
|
|
185
|
+
}, z.core.$strict>>;
|
|
186
|
+
service_account: z.ZodOptional<z.ZodString>;
|
|
187
|
+
secrets: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
188
|
+
name: z.ZodString;
|
|
189
|
+
secret_id: z.ZodUUID;
|
|
190
|
+
version: z.ZodNumber;
|
|
191
|
+
}, z.core.$strict>>>;
|
|
192
|
+
egress: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
193
|
+
resources: z.ZodPrefault<z.ZodObject<{
|
|
194
|
+
cpu: z.ZodDefault<z.ZodUnion<readonly [z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<4>, z.ZodLiteral<8>]>>;
|
|
195
|
+
memory_mb: z.ZodDefault<z.ZodNumber>;
|
|
196
|
+
concurrency: z.ZodDefault<z.ZodNumber>;
|
|
197
|
+
min_instances: z.ZodDefault<z.ZodNumber>;
|
|
198
|
+
max_instances: z.ZodDefault<z.ZodNumber>;
|
|
199
|
+
timeout_seconds: z.ZodDefault<z.ZodNumber>;
|
|
200
|
+
}, z.core.$strict>>;
|
|
201
|
+
cache: z.ZodOptional<z.ZodObject<{
|
|
202
|
+
capacity_gb: z.ZodNumber;
|
|
203
|
+
}, z.core.$strict>>;
|
|
204
|
+
}, z.core.$strict>>;
|
|
205
|
+
targets: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
206
|
+
url: z.ZodString;
|
|
207
|
+
}, z.core.$strict>>>;
|
|
208
|
+
sidebarIcon: z.ZodOptional<z.ZodString>;
|
|
209
|
+
sidebarBadge: z.ZodOptional<z.ZodObject<{
|
|
210
|
+
endpoint: z.ZodString;
|
|
211
|
+
tone: z.ZodOptional<z.ZodEnum<{
|
|
212
|
+
alert: "alert";
|
|
213
|
+
muted: "muted";
|
|
214
|
+
}>>;
|
|
215
|
+
}, z.core.$strict>>;
|
|
216
|
+
}, z.core.$strict>;
|
|
217
|
+
export type ArtifactStudioNextManifest = z.infer<typeof artifactStudioNextManifestSchema>;
|