@sequenceholdings/artifact-studio 0.2.6 → 0.3.1
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/VERSION-PIN.md +12 -1
- package/dist/api.d.ts +11 -1
- package/dist/api.js +26 -9
- package/dist/build.d.ts +2 -2
- package/dist/build.js +4 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +134 -13
- package/dist/dev-link.d.ts +1 -1
- package/dist/dev-link.js +2 -1
- package/dist/gateway/admission.d.ts +56 -0
- package/dist/gateway/admission.js +134 -0
- package/dist/gateway/authorization-cache.d.ts +52 -0
- package/dist/gateway/authorization-cache.js +111 -0
- package/dist/gateway/control-admission.d.ts +48 -0
- package/dist/gateway/control-admission.js +127 -0
- package/dist/gateway/control.d.ts +94 -0
- package/dist/gateway/control.js +116 -0
- package/dist/gateway/hosted-proxy.d.ts +36 -0
- package/dist/gateway/hosted-proxy.js +344 -0
- package/dist/gateway/release-admission.d.ts +23 -0
- package/dist/gateway/release-admission.js +259 -0
- package/dist/gateway/server.d.ts +38 -0
- package/dist/gateway/server.js +382 -0
- package/dist/gateway/session.d.ts +72 -0
- package/dist/gateway/session.js +144 -0
- package/dist/gateway/usage.d.ts +70 -0
- package/dist/gateway/usage.js +116 -0
- package/dist/gateway-main.d.ts +1 -0
- package/dist/gateway-main.js +114 -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 +132 -0
- package/dist/manifest-static.d.ts +76 -0
- package/dist/manifest-static.js +124 -0
- package/dist/manifest.d.ts +118 -13
- package/dist/manifest.js +12 -119
- package/dist/next/bridge.d.ts +47 -0
- package/dist/next/bridge.js +236 -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 +105 -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 +2 -2
- package/dist/server/broker-contract.d.ts +68 -0
- package/dist/server/broker-contract.js +33 -0
- package/dist/server/client.d.ts +81 -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 +75 -0
- package/dist/server/request-context.js +107 -0
- package/dist/server/runtime-config.d.ts +54 -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 +157 -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/gitignore +9 -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 +22 -0
- package/dist/templates/nextjs/postcss.config.mjs +5 -0
- package/dist/templates/nextjs/tsconfig.json +20 -0
- package/dist/templates/react-vite/package.json +1 -1
- package/dist/trusted-install.js +62 -2
- package/dist/tunnel/broker.d.ts +75 -0
- package/dist/tunnel/broker.js +463 -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 +78 -0
- package/dist/tunnel/protocol.js +210 -0
- package/dist/tunnel/redis-auth.d.ts +37 -0
- package/dist/tunnel/redis-auth.js +92 -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 +75 -0
- package/dist/tunnel/redis-types.js +9 -0
- package/dist/tunnel/redis.d.ts +56 -0
- package/dist/tunnel/redis.js +404 -0
- package/dist/tunnel/transport.d.ts +17 -0
- package/dist/tunnel/transport.js +1 -0
- package/package.json +67 -4
- package/templates/nextjs/.artifactignore +14 -0
- package/templates/nextjs/README.md +157 -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/gitignore +9 -0
- package/templates/nextjs/next.config.mjs +27 -0
- package/templates/nextjs/package.json +32 -0
- package/templates/nextjs/pnpm-workspace.yaml +22 -0
- package/templates/nextjs/postcss.config.mjs +5 -0
- package/templates/nextjs/tsconfig.json +20 -0
- package/templates/react-vite/package.json +1 -1
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
export interface GatewayUsageEvent {
|
|
2
|
+
eventId: string;
|
|
3
|
+
requestId: string;
|
|
4
|
+
/** The user the gateway acted for: the session owner Atlas authorized for this request. */
|
|
5
|
+
actorId: string;
|
|
6
|
+
projectId: string;
|
|
7
|
+
deploymentId?: string;
|
|
8
|
+
devSessionId?: string;
|
|
9
|
+
environment: string;
|
|
10
|
+
startedAt: string;
|
|
11
|
+
endedAt: string;
|
|
12
|
+
measurements: {
|
|
13
|
+
requestCount: 1;
|
|
14
|
+
durationMs: number;
|
|
15
|
+
};
|
|
16
|
+
outcome: 'success' | 'error' | 'cancelled';
|
|
17
|
+
httpStatus: number;
|
|
18
|
+
}
|
|
19
|
+
export type GatewayUsageFailure = {
|
|
20
|
+
kind: 'usage_queue_full' | 'usage_delivery_failed' | 'usage_delivery_rejected' | 'usage_events_rejected' | 'usage_lost_on_shutdown';
|
|
21
|
+
count: number;
|
|
22
|
+
};
|
|
23
|
+
export interface GatewayUsageLimits {
|
|
24
|
+
/** Events buffered or in delivery; later observations are dropped and counted. */
|
|
25
|
+
capacity: number;
|
|
26
|
+
/** Events per control-plane call. Atlas accepts at most this many per batch. */
|
|
27
|
+
maxBatch: number;
|
|
28
|
+
/** Longest an observation waits in the buffer before a partial batch is sent. */
|
|
29
|
+
flushIntervalMs: number;
|
|
30
|
+
/** Concurrent batch deliveries. */
|
|
31
|
+
concurrency: number;
|
|
32
|
+
}
|
|
33
|
+
export declare const GATEWAY_USAGE_LIMITS: Readonly<GatewayUsageLimits>;
|
|
34
|
+
/**
|
|
35
|
+
* Batched, bounded delivery of request observations: one control-plane call per `maxBatch` events or
|
|
36
|
+
* per flush interval, instead of one per request. Atlas deduplicates accepted records durably, so a
|
|
37
|
+
* retried batch is idempotent. Observations are telemetry, not billing totals. A crash loses at most
|
|
38
|
+
* the unflushed buffer (about one flush interval) and the batches in flight; a graceful drain flushes
|
|
39
|
+
* everything, and a forced exit reports `pending` as `usage_lost_on_shutdown`.
|
|
40
|
+
*/
|
|
41
|
+
export declare class GatewayUsageReporter {
|
|
42
|
+
private readonly options;
|
|
43
|
+
private readonly buffer;
|
|
44
|
+
private readonly inFlight;
|
|
45
|
+
private readonly limits;
|
|
46
|
+
private timer;
|
|
47
|
+
private dropped;
|
|
48
|
+
private closed;
|
|
49
|
+
constructor(options: {
|
|
50
|
+
send: (events: GatewayUsageEvent[]) => Promise<{
|
|
51
|
+
rejected: number;
|
|
52
|
+
}>;
|
|
53
|
+
onFailure: (failure: GatewayUsageFailure) => void;
|
|
54
|
+
limits?: Partial<GatewayUsageLimits>;
|
|
55
|
+
});
|
|
56
|
+
/** Observations accepted but not yet confirmed by Atlas. */
|
|
57
|
+
get pending(): number;
|
|
58
|
+
record(event: GatewayUsageEvent): void;
|
|
59
|
+
/**
|
|
60
|
+
* Stops accepting observations and delivers everything buffered; each batch is bounded by its
|
|
61
|
+
* retries. A process that must exit sooner reports `pending` as lost (see gateway-main).
|
|
62
|
+
*/
|
|
63
|
+
close(): Promise<void>;
|
|
64
|
+
private drainAll;
|
|
65
|
+
private scheduleFlush;
|
|
66
|
+
private reportDropped;
|
|
67
|
+
/** Sends full batches, and a partial batch too when forced by the timer or shutdown. */
|
|
68
|
+
private pump;
|
|
69
|
+
private deliver;
|
|
70
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
export const GATEWAY_USAGE_LIMITS = Object.freeze({ capacity: 10_000, maxBatch: 100, flushIntervalMs: 1_000, concurrency: 4 });
|
|
2
|
+
const ATTEMPTS = 3;
|
|
3
|
+
/**
|
|
4
|
+
* Batched, bounded delivery of request observations: one control-plane call per `maxBatch` events or
|
|
5
|
+
* per flush interval, instead of one per request. Atlas deduplicates accepted records durably, so a
|
|
6
|
+
* retried batch is idempotent. Observations are telemetry, not billing totals. A crash loses at most
|
|
7
|
+
* the unflushed buffer (about one flush interval) and the batches in flight; a graceful drain flushes
|
|
8
|
+
* everything, and a forced exit reports `pending` as `usage_lost_on_shutdown`.
|
|
9
|
+
*/
|
|
10
|
+
export class GatewayUsageReporter {
|
|
11
|
+
options;
|
|
12
|
+
buffer = [];
|
|
13
|
+
inFlight = new Map();
|
|
14
|
+
limits;
|
|
15
|
+
timer;
|
|
16
|
+
dropped = 0;
|
|
17
|
+
closed = false;
|
|
18
|
+
constructor(options) {
|
|
19
|
+
this.options = options;
|
|
20
|
+
this.limits = { ...GATEWAY_USAGE_LIMITS, ...options.limits };
|
|
21
|
+
for (const value of Object.values(this.limits)) {
|
|
22
|
+
if (!Number.isSafeInteger(value) || value < 1)
|
|
23
|
+
throw new Error('Gateway usage limits must be positive integers');
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/** Observations accepted but not yet confirmed by Atlas. */
|
|
27
|
+
get pending() {
|
|
28
|
+
let count = this.buffer.length;
|
|
29
|
+
for (const size of this.inFlight.values())
|
|
30
|
+
count += size;
|
|
31
|
+
return count;
|
|
32
|
+
}
|
|
33
|
+
record(event) {
|
|
34
|
+
if (this.closed) {
|
|
35
|
+
this.options.onFailure({ kind: 'usage_lost_on_shutdown', count: 1 });
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (this.pending >= this.limits.capacity) {
|
|
39
|
+
this.dropped++;
|
|
40
|
+
this.scheduleFlush();
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
this.buffer.push(event);
|
|
44
|
+
if (this.buffer.length >= this.limits.maxBatch)
|
|
45
|
+
this.pump();
|
|
46
|
+
else
|
|
47
|
+
this.scheduleFlush();
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Stops accepting observations and delivers everything buffered; each batch is bounded by its
|
|
51
|
+
* retries. A process that must exit sooner reports `pending` as lost (see gateway-main).
|
|
52
|
+
*/
|
|
53
|
+
async close() {
|
|
54
|
+
this.closed = true;
|
|
55
|
+
clearTimeout(this.timer);
|
|
56
|
+
this.timer = undefined;
|
|
57
|
+
await this.drainAll();
|
|
58
|
+
this.reportDropped();
|
|
59
|
+
}
|
|
60
|
+
async drainAll() {
|
|
61
|
+
while (this.buffer.length || this.inFlight.size) {
|
|
62
|
+
this.pump({ force: true });
|
|
63
|
+
if (this.inFlight.size)
|
|
64
|
+
await Promise.race(this.inFlight.keys());
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
scheduleFlush() {
|
|
68
|
+
if (this.timer || this.closed)
|
|
69
|
+
return;
|
|
70
|
+
this.timer = setTimeout(() => {
|
|
71
|
+
this.timer = undefined;
|
|
72
|
+
this.reportDropped();
|
|
73
|
+
this.pump({ force: true });
|
|
74
|
+
if (this.buffer.length)
|
|
75
|
+
this.scheduleFlush();
|
|
76
|
+
}, this.limits.flushIntervalMs);
|
|
77
|
+
this.timer.unref();
|
|
78
|
+
}
|
|
79
|
+
reportDropped() {
|
|
80
|
+
if (!this.dropped)
|
|
81
|
+
return;
|
|
82
|
+
this.options.onFailure({ kind: 'usage_queue_full', count: this.dropped });
|
|
83
|
+
this.dropped = 0;
|
|
84
|
+
}
|
|
85
|
+
/** Sends full batches, and a partial batch too when forced by the timer or shutdown. */
|
|
86
|
+
pump({ force = false } = {}) {
|
|
87
|
+
while (this.inFlight.size < this.limits.concurrency && (this.buffer.length >= this.limits.maxBatch || (force && this.buffer.length))) {
|
|
88
|
+
const batch = this.buffer.splice(0, this.limits.maxBatch);
|
|
89
|
+
const delivery = this.deliver(batch).finally(() => {
|
|
90
|
+
this.inFlight.delete(delivery);
|
|
91
|
+
this.pump({ force: this.closed });
|
|
92
|
+
});
|
|
93
|
+
this.inFlight.set(delivery, batch.length);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
async deliver(batch) {
|
|
97
|
+
for (let attempt = 0; attempt < ATTEMPTS; attempt++) {
|
|
98
|
+
try {
|
|
99
|
+
const { rejected } = await this.options.send(batch);
|
|
100
|
+
if (rejected > 0)
|
|
101
|
+
this.options.onFailure({ kind: 'usage_events_rejected', count: rejected });
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
const status = typeof error === 'object' && error !== null && 'status' in error ? error.status : undefined;
|
|
106
|
+
if (typeof status === 'number' && status >= 400 && status < 500 && status !== 429) {
|
|
107
|
+
this.options.onFailure({ kind: 'usage_delivery_rejected', count: batch.length });
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
if (attempt < ATTEMPTS - 1)
|
|
111
|
+
await new Promise((resolve) => setTimeout(resolve, 100 * 2 ** attempt));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
this.options.onFailure({ kind: 'usage_delivery_failed', count: batch.length });
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { isIP } from 'node:net';
|
|
3
|
+
import { Redis } from 'ioredis';
|
|
4
|
+
import { ArtifactGatewayControl } from './gateway/control.js';
|
|
5
|
+
import { createArtifactGateway } from './gateway/server.js';
|
|
6
|
+
import { RedisTunnelOwnership, RedisTunnelRelay } from './tunnel/redis.js';
|
|
7
|
+
import { ArtifactRequestAdmission } from './gateway/admission.js';
|
|
8
|
+
import { GatewayControlAdmission } from './gateway/control-admission.js';
|
|
9
|
+
function required(name) {
|
|
10
|
+
const value = process.env[name];
|
|
11
|
+
if (!value)
|
|
12
|
+
throw new Error(`Missing required gateway configuration: ${name}`);
|
|
13
|
+
return value;
|
|
14
|
+
}
|
|
15
|
+
/** A separate process/identity from Atlas and all application/build containers. */
|
|
16
|
+
async function main() {
|
|
17
|
+
if (process.env.ARTIFACT_NEXT_ENABLED !== 'true')
|
|
18
|
+
throw new Error('Artifact Next gateway is disabled');
|
|
19
|
+
const redisUrl = required('ARTIFACT_NEXT_REDIS_URL');
|
|
20
|
+
if (new URL(redisUrl).protocol !== 'rediss:')
|
|
21
|
+
throw new Error('Artifact tunnel routing requires private Redis with TLS');
|
|
22
|
+
const namespace = required('APP_ENV'), nodeId = randomUUID();
|
|
23
|
+
const redisOptions = {
|
|
24
|
+
enableOfflineQueue: false, autoResendUnfulfilledCommands: false, maxRetriesPerRequest: 0,
|
|
25
|
+
lazyConnect: true, connectTimeout: 10_000,
|
|
26
|
+
tls: { rejectUnauthorized: true, ...(process.env.REDIS_CA_CERT ? { ca: process.env.REDIS_CA_CERT } : {}) },
|
|
27
|
+
};
|
|
28
|
+
const command = new Redis(redisUrl, redisOptions);
|
|
29
|
+
const publisher = new Redis(redisUrl, redisOptions);
|
|
30
|
+
const subscriber = new Redis(redisUrl, { ...redisOptions, autoResubscribe: false });
|
|
31
|
+
const clients = [command, publisher, subscriber];
|
|
32
|
+
for (const client of clients)
|
|
33
|
+
client.on('error', () => {
|
|
34
|
+
// Provider messages may include connection strings; keep them out of logs.
|
|
35
|
+
process.stderr.write(`${JSON.stringify({ event: 'artifact_gateway.redis_unavailable', nodeId })}\n`);
|
|
36
|
+
});
|
|
37
|
+
await Promise.all(clients.map(client => client.connect()));
|
|
38
|
+
const log = (event) => process.stderr.write(`${JSON.stringify({ ...event, nodeId })}\n`);
|
|
39
|
+
// A flood of refused requests or forged frames must not become a log flood: count and report every 10 s.
|
|
40
|
+
const refusals = new Map();
|
|
41
|
+
const refuse = (kind) => { refusals.set(kind, (refusals.get(kind) ?? 0) + 1); };
|
|
42
|
+
setInterval(() => {
|
|
43
|
+
for (const [kind, count] of refusals)
|
|
44
|
+
log({ event: 'artifact_gateway.request', kind, count });
|
|
45
|
+
refusals.clear();
|
|
46
|
+
}, 10_000).unref();
|
|
47
|
+
const sessionKey = required('ARTIFACT_NEXT_SESSION_KEY');
|
|
48
|
+
const ownership = new RedisTunnelOwnership({ command, namespace });
|
|
49
|
+
// Relay frames are authenticated with a subkey of the gateway-only session key, which every
|
|
50
|
+
// replica already shares and Atlas never holds.
|
|
51
|
+
const relay = new RedisTunnelRelay({ nodeId, namespace, ownership, publisher, subscriber, secret: sessionKey,
|
|
52
|
+
onRejectedFrame: () => refuse('relay_frame_rejected') });
|
|
53
|
+
await relay.start();
|
|
54
|
+
let stopping = false;
|
|
55
|
+
subscriber.on('ready', () => { if (!stopping)
|
|
56
|
+
void relay.start().catch(() => undefined); });
|
|
57
|
+
const controlUrl = required('ARTIFACT_NEXT_CONTROL_PLANE_URL');
|
|
58
|
+
const gateway = createArtifactGateway({
|
|
59
|
+
control: new ArtifactGatewayControl({ url: controlUrl, token: required('ARTIFACT_NEXT_CONTROL_TOKEN') }),
|
|
60
|
+
runtimeDomain: required('ARTIFACT_NEXT_RUNTIME_DOMAIN'),
|
|
61
|
+
parentOrigin: process.env.ARTIFACT_NEXT_PARENT_ORIGIN ?? new URL(controlUrl).origin,
|
|
62
|
+
gatewayOrigin: required('ARTIFACT_NEXT_GATEWAY_URL'),
|
|
63
|
+
sessionKey,
|
|
64
|
+
contextPrivateKey: required('ARTIFACT_NEXT_CONTEXT_PRIVATE_KEY'),
|
|
65
|
+
contextKeyId: required('ARTIFACT_NEXT_CONTEXT_KEY_ID'),
|
|
66
|
+
nodeId, routing: ownership, relay,
|
|
67
|
+
admission: new ArtifactRequestAdmission({ redis: command, namespace }),
|
|
68
|
+
controlAdmission: new GatewayControlAdmission({ redis: command, namespace }),
|
|
69
|
+
resolveLease: sessionId => ownership.resolveLease(sessionId),
|
|
70
|
+
renewLease: lease => ownership.renew(lease, nodeId),
|
|
71
|
+
ready: () => clients.every(client => client.status === 'ready') && relay.isReady(),
|
|
72
|
+
onError: event => { if (event.kind === 'admission_rejected')
|
|
73
|
+
refuse(event.kind);
|
|
74
|
+
else
|
|
75
|
+
log({ event: 'artifact_gateway.request', ...event }); },
|
|
76
|
+
});
|
|
77
|
+
const port = Number(process.env.PORT ?? '8080');
|
|
78
|
+
if (!Number.isSafeInteger(port) || port < 1 || port > 65535)
|
|
79
|
+
throw new Error('Invalid gateway PORT');
|
|
80
|
+
// Every interface in its container; a laptop run (scripts/local-stack.sh) keeps it on loopback.
|
|
81
|
+
const host = process.env.ARTIFACT_NEXT_GATEWAY_HOST ?? '0.0.0.0';
|
|
82
|
+
if (isIP(host) === 0)
|
|
83
|
+
throw new Error('Invalid ARTIFACT_NEXT_GATEWAY_HOST: expected an IP address');
|
|
84
|
+
await new Promise((resolve, reject) => {
|
|
85
|
+
gateway.server.once('error', reject);
|
|
86
|
+
gateway.server.listen(port, host, resolve);
|
|
87
|
+
});
|
|
88
|
+
process.stdout.write(`${JSON.stringify({ event: 'artifact_gateway.ready', nodeId, port })}\n`);
|
|
89
|
+
const stop = async () => {
|
|
90
|
+
if (stopping)
|
|
91
|
+
return;
|
|
92
|
+
stopping = true;
|
|
93
|
+
const deadline = setTimeout(() => {
|
|
94
|
+
// Account for observations a forced exit discards; a graceful drain reports its own losses.
|
|
95
|
+
const lost = gateway.pendingUsage;
|
|
96
|
+
if (lost)
|
|
97
|
+
log({ event: 'artifact_gateway.request', kind: 'usage_lost_on_shutdown', count: lost });
|
|
98
|
+
gateway.server.closeAllConnections();
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}, 9_000);
|
|
101
|
+
deadline.unref();
|
|
102
|
+
await gateway.drain();
|
|
103
|
+
await relay.close();
|
|
104
|
+
for (const client of clients)
|
|
105
|
+
client.disconnect();
|
|
106
|
+
clearTimeout(deadline);
|
|
107
|
+
};
|
|
108
|
+
process.once('SIGTERM', () => { void stop(); });
|
|
109
|
+
process.once('SIGINT', () => { void stop(); });
|
|
110
|
+
}
|
|
111
|
+
void main().catch(() => {
|
|
112
|
+
process.stderr.write(`${JSON.stringify({ event: 'artifact_gateway.start_failed' })}\n`);
|
|
113
|
+
process.exit(1);
|
|
114
|
+
});
|
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>;
|