@sequenceholdings/artifact-studio 0.3.1 → 0.5.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 +71 -0
- package/VERSION-PIN.md +9 -0
- package/dist/active-deploy-policy.d.ts +9 -3
- package/dist/active-deploy-policy.js +20 -8
- package/dist/artifact-env-bindings.d.ts +9 -0
- package/dist/artifact-env-bindings.js +32 -0
- package/dist/build.js +7 -0
- package/dist/cli.js +66 -13
- package/dist/deployment-validation.d.ts +2 -0
- package/dist/deployment-validation.js +2 -0
- package/dist/dev-link.js +29 -3
- package/dist/env-binding-scan.d.ts +10 -0
- package/dist/env-binding-scan.js +179 -0
- package/dist/env-bindings.d.ts +52 -0
- package/dist/env-bindings.js +180 -0
- package/dist/gateway/control.d.ts +33 -0
- package/dist/gateway/control.js +29 -0
- package/dist/gateway/hosted-proxy.d.ts +5 -0
- package/dist/gateway/hosted-proxy.js +4 -1
- package/dist/gateway/lattice-invoke.d.ts +48 -0
- package/dist/gateway/lattice-invoke.js +245 -0
- package/dist/gateway/lattice-upstream.d.ts +38 -0
- package/dist/gateway/lattice-upstream.js +136 -0
- package/dist/gateway/server.d.ts +3 -0
- package/dist/gateway/server.js +4 -5
- package/dist/gateway/session.d.ts +17 -2
- package/dist/gateway/session.js +27 -6
- package/dist/gateway-main.js +2 -0
- package/dist/loopback.d.ts +8 -0
- package/dist/loopback.js +13 -0
- package/dist/manifest-custom-roles.d.ts +11 -1
- package/dist/manifest-custom-roles.js +13 -5
- package/dist/manifest-next.d.ts +55 -0
- package/dist/manifest-next.js +35 -2
- package/dist/manifest-static.d.ts +35 -0
- package/dist/manifest-static.js +12 -0
- package/dist/manifest.d.ts +48 -0
- package/dist/next/protocol.js +2 -1
- package/dist/next-build.d.ts +2 -1
- package/dist/next-build.js +12 -7
- package/dist/next-dev.js +2 -1
- package/dist/next-project.js +2 -1
- package/dist/offline/cache.d.ts +4 -0
- package/dist/offline/cache.js +7 -0
- package/dist/offline/connector.d.ts +26 -0
- package/dist/offline/connector.js +118 -0
- package/dist/offline/crypto.d.ts +5 -0
- package/dist/offline/crypto.js +24 -0
- package/dist/offline/index.d.ts +8 -0
- package/dist/offline/index.js +7 -0
- package/dist/offline/indexed-db.d.ts +10 -0
- package/dist/offline/indexed-db.js +104 -0
- package/dist/offline/outbox.d.ts +7 -0
- package/dist/offline/outbox.js +46 -0
- package/dist/offline/replay.d.ts +25 -0
- package/dist/offline/replay.js +192 -0
- package/dist/offline/secure-store.d.ts +6 -0
- package/dist/offline/secure-store.js +51 -0
- package/dist/offline/types.d.ts +162 -0
- package/dist/offline/types.js +1 -0
- package/dist/orm-json-scalars.d.ts +21 -0
- package/dist/orm-json-scalars.js +21 -0
- package/dist/project.js +3 -3
- package/dist/role-definitions-notice.d.ts +7 -0
- package/dist/role-definitions-notice.js +19 -0
- package/dist/runtime-contracts.d.ts +7 -7
- package/dist/runtime-contracts.js +2 -1
- package/dist/sandbox-lint.js +1 -1
- package/dist/sdk-orm.d.ts +24 -0
- package/dist/sdk-orm.js +28 -0
- package/dist/sdk.d.ts +127 -0
- package/dist/sdk.js +1 -0
- package/dist/server/broker-contract.d.ts +191 -5
- package/dist/server/broker-contract.js +142 -1
- package/dist/server/client.d.ts +60 -12
- package/dist/server/client.js +143 -20
- package/dist/server/errors.d.ts +26 -0
- package/dist/server/errors.js +29 -0
- package/dist/server/managed-server-path.d.ts +6 -0
- package/dist/server/managed-server-path.js +14 -0
- package/dist/server/processes.d.ts +290 -0
- package/dist/server/processes.js +56 -0
- package/dist/server/request-context.d.ts +6 -0
- package/dist/server/request-context.js +12 -2
- package/dist/server/runtime-config.js +2 -1
- package/dist/server.d.ts +5 -1
- package/dist/server.js +1 -0
- package/dist/source-resolver.d.ts +9 -2
- package/dist/source-resolver.js +55 -8
- package/dist/templates/nextjs/AGENTS.md +10 -0
- package/dist/templates/nextjs/CLAUDE.md +1 -0
- package/dist/templates/nextjs/README.md +88 -0
- package/dist/templates/react-vite/AGENTS.md +8 -88
- package/dist/templates/react-vite/CLAUDE.md +0 -6
- package/dist/templates/react-vite/package.json +1 -1
- package/dist/templates/react-vite/src/App.tsx +37 -14
- package/dist/templates/react-vite/src/styles.css +10 -0
- package/dist/tunnel/client.js +10 -3
- package/dist/tunnel/protocol.js +2 -1
- package/package.json +7 -3
- package/templates/nextjs/AGENTS.md +10 -0
- package/templates/nextjs/CLAUDE.md +1 -0
- package/templates/nextjs/README.md +88 -0
- package/templates/react-vite/AGENTS.md +8 -88
- package/templates/react-vite/CLAUDE.md +0 -6
- package/templates/react-vite/package.json +1 -1
- package/templates/react-vite/src/App.tsx +37 -14
- package/templates/react-vite/src/styles.css +10 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import type { SequenceApiFetchResponse, SequenceOfflineNetwork, SequenceOfflineRequest } from '../sdk.js';
|
|
2
|
+
export type OfflineStoreName = 'cache' | 'outbox' | 'accesslog';
|
|
3
|
+
export interface OfflineKvStore {
|
|
4
|
+
get(input: {
|
|
5
|
+
store: OfflineStoreName;
|
|
6
|
+
key: string;
|
|
7
|
+
}): Promise<unknown>;
|
|
8
|
+
set(input: {
|
|
9
|
+
store: OfflineStoreName;
|
|
10
|
+
key: string;
|
|
11
|
+
value: unknown;
|
|
12
|
+
}): Promise<void>;
|
|
13
|
+
delete(input: {
|
|
14
|
+
store: OfflineStoreName;
|
|
15
|
+
key: string;
|
|
16
|
+
}): Promise<void>;
|
|
17
|
+
keys(input: {
|
|
18
|
+
store: OfflineStoreName;
|
|
19
|
+
}): Promise<string[]>;
|
|
20
|
+
}
|
|
21
|
+
export interface OfflineEncryptedRecord {
|
|
22
|
+
iv: number[];
|
|
23
|
+
ciphertext: number[];
|
|
24
|
+
}
|
|
25
|
+
export interface OfflineCrypto {
|
|
26
|
+
encrypt(input: {
|
|
27
|
+
value: unknown;
|
|
28
|
+
}): Promise<OfflineEncryptedRecord>;
|
|
29
|
+
decrypt(input: {
|
|
30
|
+
record: OfflineEncryptedRecord;
|
|
31
|
+
}): Promise<unknown>;
|
|
32
|
+
}
|
|
33
|
+
export interface EncryptedOfflineStore {
|
|
34
|
+
get<T = unknown>(input: {
|
|
35
|
+
store: OfflineStoreName;
|
|
36
|
+
key: string;
|
|
37
|
+
}): Promise<T | undefined>;
|
|
38
|
+
set(input: {
|
|
39
|
+
store: OfflineStoreName;
|
|
40
|
+
key: string;
|
|
41
|
+
value: unknown;
|
|
42
|
+
}): Promise<void>;
|
|
43
|
+
delete(input: {
|
|
44
|
+
store: OfflineStoreName;
|
|
45
|
+
key: string;
|
|
46
|
+
}): Promise<void>;
|
|
47
|
+
keys(input: {
|
|
48
|
+
store: OfflineStoreName;
|
|
49
|
+
}): Promise<string[]>;
|
|
50
|
+
clear(input: {
|
|
51
|
+
store: OfflineStoreName;
|
|
52
|
+
}): Promise<void>;
|
|
53
|
+
}
|
|
54
|
+
export type OfflineFailureKind = 'transient' | 'invalid' | 'auth' | 'conflict';
|
|
55
|
+
export type OfflineOutboxStatus = 'pending' | 'inflight' | 'failed' | 'conflict';
|
|
56
|
+
export interface OfflineConflictField {
|
|
57
|
+
key: string;
|
|
58
|
+
mine: unknown;
|
|
59
|
+
theirs: unknown;
|
|
60
|
+
}
|
|
61
|
+
export interface OfflineConflictInfo {
|
|
62
|
+
fields: OfflineConflictField[];
|
|
63
|
+
observedAt?: string;
|
|
64
|
+
serverUpdatedAt?: string;
|
|
65
|
+
}
|
|
66
|
+
export interface OfflineOutboxEntry {
|
|
67
|
+
id: string;
|
|
68
|
+
sequence: number;
|
|
69
|
+
request: SequenceOfflineRequest;
|
|
70
|
+
entity: string;
|
|
71
|
+
capturedAt: string;
|
|
72
|
+
attempts: number;
|
|
73
|
+
status: OfflineOutboxStatus;
|
|
74
|
+
observed?: Record<string, unknown>;
|
|
75
|
+
optimisticId?: string;
|
|
76
|
+
failureKind?: OfflineFailureKind;
|
|
77
|
+
lastError?: string;
|
|
78
|
+
conflict?: OfflineConflictInfo;
|
|
79
|
+
force?: boolean;
|
|
80
|
+
}
|
|
81
|
+
export interface OfflineOutbox {
|
|
82
|
+
enqueue(input: {
|
|
83
|
+
id?: string;
|
|
84
|
+
request: SequenceOfflineRequest;
|
|
85
|
+
entity: string;
|
|
86
|
+
observed?: Record<string, unknown>;
|
|
87
|
+
optimisticId?: string;
|
|
88
|
+
}): Promise<OfflineOutboxEntry>;
|
|
89
|
+
list(): Promise<OfflineOutboxEntry[]>;
|
|
90
|
+
update(input: {
|
|
91
|
+
id: string;
|
|
92
|
+
patch: Partial<OfflineOutboxEntry>;
|
|
93
|
+
}): Promise<void>;
|
|
94
|
+
remove(input: {
|
|
95
|
+
id: string;
|
|
96
|
+
}): Promise<void>;
|
|
97
|
+
}
|
|
98
|
+
export interface OfflineReadDescriptor {
|
|
99
|
+
key: string;
|
|
100
|
+
ttlMs: number;
|
|
101
|
+
entity: string;
|
|
102
|
+
}
|
|
103
|
+
export interface OfflineReadPolicy {
|
|
104
|
+
match(request: SequenceOfflineRequest): OfflineReadDescriptor | null;
|
|
105
|
+
}
|
|
106
|
+
export interface OfflineCacheEntry {
|
|
107
|
+
response: SequenceApiFetchResponse;
|
|
108
|
+
cachedAt: number;
|
|
109
|
+
expiresAt: number;
|
|
110
|
+
entity: string;
|
|
111
|
+
}
|
|
112
|
+
export interface OfflineCache {
|
|
113
|
+
read(input: {
|
|
114
|
+
key: string;
|
|
115
|
+
}): Promise<OfflineCacheEntry | undefined>;
|
|
116
|
+
write(input: {
|
|
117
|
+
key: string;
|
|
118
|
+
entry: OfflineCacheEntry;
|
|
119
|
+
}): Promise<void>;
|
|
120
|
+
remove(input: {
|
|
121
|
+
key: string;
|
|
122
|
+
}): Promise<void>;
|
|
123
|
+
}
|
|
124
|
+
export interface OfflineMutationDescriptor {
|
|
125
|
+
entity: string;
|
|
126
|
+
label(entry: OfflineOutboxEntry): string;
|
|
127
|
+
capture?(input: {
|
|
128
|
+
request: SequenceOfflineRequest;
|
|
129
|
+
}): Promise<Record<string, unknown> | undefined>;
|
|
130
|
+
optimisticApply?(input: {
|
|
131
|
+
entry: OfflineOutboxEntry;
|
|
132
|
+
}): Promise<void> | void;
|
|
133
|
+
optimisticResponse?(input: {
|
|
134
|
+
entry: OfflineOutboxEntry;
|
|
135
|
+
}): SequenceApiFetchResponse;
|
|
136
|
+
fetchCurrent?(input: {
|
|
137
|
+
entry: OfflineOutboxEntry;
|
|
138
|
+
network: SequenceOfflineNetwork;
|
|
139
|
+
}): Promise<unknown>;
|
|
140
|
+
detectConflict?(input: {
|
|
141
|
+
entry: OfflineOutboxEntry;
|
|
142
|
+
current: unknown;
|
|
143
|
+
}): OfflineConflictInfo | null;
|
|
144
|
+
reconcile?(input: {
|
|
145
|
+
entry: OfflineOutboxEntry;
|
|
146
|
+
response: SequenceApiFetchResponse;
|
|
147
|
+
}): Promise<void> | void;
|
|
148
|
+
rollback?(input: {
|
|
149
|
+
entry: OfflineOutboxEntry;
|
|
150
|
+
}): Promise<void> | void;
|
|
151
|
+
}
|
|
152
|
+
export interface OfflineMutationPolicy {
|
|
153
|
+
match(request: SequenceOfflineRequest): OfflineMutationDescriptor | null;
|
|
154
|
+
}
|
|
155
|
+
export interface OfflineUnresolvedWrite {
|
|
156
|
+
id: string;
|
|
157
|
+
label: string;
|
|
158
|
+
capturedAt: string;
|
|
159
|
+
kind: OfflineFailureKind;
|
|
160
|
+
error?: string;
|
|
161
|
+
conflict?: OfflineConflictInfo;
|
|
162
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ORM data plane's opt-in exact-number wire form (orm-data-plane/src/protocol.ts).
|
|
3
|
+
* By default numeric and bigint values come back as JSON numbers, which JavaScript
|
|
4
|
+
* rounds past 15-17 significant digits. With `lossless-v1` they come back as strings
|
|
5
|
+
* ("250000.00", "9007199254740993"), and a successful response acknowledges the form
|
|
6
|
+
* with the same header. Variables may carry numeric and bigint values as decimal
|
|
7
|
+
* strings in either form.
|
|
8
|
+
*/
|
|
9
|
+
export declare const ORM_JSON_SCALARS_HEADER = "x-sequence-orm-json-scalars";
|
|
10
|
+
export declare const ORM_LOSSLESS_JSON_SCALARS = "lossless-v1";
|
|
11
|
+
export type ArtifactOrmJsonScalars = typeof ORM_LOSSLESS_JSON_SCALARS;
|
|
12
|
+
/**
|
|
13
|
+
* An executor that asked for exact numbers refuses a successful result the platform
|
|
14
|
+
* did not acknowledge, because its numbers may already be rounded. Failed responses
|
|
15
|
+
* never acknowledge and keep their own error.
|
|
16
|
+
*/
|
|
17
|
+
export declare function assertJsonScalarsAcknowledged({ requested, acknowledged, status }: {
|
|
18
|
+
requested: ArtifactOrmJsonScalars | undefined;
|
|
19
|
+
acknowledged: string | null | undefined;
|
|
20
|
+
status: number;
|
|
21
|
+
}): void;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { OrmOperationError } from '@sequenceholdings/orm/client';
|
|
2
|
+
/**
|
|
3
|
+
* The ORM data plane's opt-in exact-number wire form (orm-data-plane/src/protocol.ts).
|
|
4
|
+
* By default numeric and bigint values come back as JSON numbers, which JavaScript
|
|
5
|
+
* rounds past 15-17 significant digits. With `lossless-v1` they come back as strings
|
|
6
|
+
* ("250000.00", "9007199254740993"), and a successful response acknowledges the form
|
|
7
|
+
* with the same header. Variables may carry numeric and bigint values as decimal
|
|
8
|
+
* strings in either form.
|
|
9
|
+
*/
|
|
10
|
+
export const ORM_JSON_SCALARS_HEADER = 'x-sequence-orm-json-scalars';
|
|
11
|
+
export const ORM_LOSSLESS_JSON_SCALARS = 'lossless-v1';
|
|
12
|
+
/**
|
|
13
|
+
* An executor that asked for exact numbers refuses a successful result the platform
|
|
14
|
+
* did not acknowledge, because its numbers may already be rounded. Failed responses
|
|
15
|
+
* never acknowledge and keep their own error.
|
|
16
|
+
*/
|
|
17
|
+
export function assertJsonScalarsAcknowledged({ requested, acknowledged, status }) {
|
|
18
|
+
if (requested === undefined || acknowledged === requested)
|
|
19
|
+
return;
|
|
20
|
+
throw new OrmOperationError(`Atlas did not confirm ${requested} numbers for this ORM response, so its numeric and bigint values may be rounded; update Atlas before relying on exact numbers`, status, 'LOSSLESS_TRANSPORT_UNAVAILABLE');
|
|
21
|
+
}
|
package/dist/project.js
CHANGED
|
@@ -9,7 +9,7 @@ import { readNextSourceSnapshot } from './next-source.js';
|
|
|
9
9
|
import { normalizeArtifactPath } from './paths.js';
|
|
10
10
|
const IGNORED_DIRS = new Set(['node_modules', '.git', 'dist', '.next', '.artifact-studio']);
|
|
11
11
|
const TEXT_EXTENSIONS = new Set([
|
|
12
|
-
'.ts', '.tsx', '.js', '.jsx', '.json', '.css', '.html', '.md', '.yml', '.yaml', '.svg',
|
|
12
|
+
'.ts', '.tsx', '.mts', '.cts', '.js', '.jsx', '.mjs', '.cjs', '.json', '.css', '.html', '.md', '.yml', '.yaml', '.svg',
|
|
13
13
|
]);
|
|
14
14
|
export function hasArtifactStudioManifest(rootDir) {
|
|
15
15
|
return existsSync(join(resolve(rootDir), 'artifact.bundle.yml'));
|
|
@@ -83,9 +83,9 @@ function isTextFile(path) {
|
|
|
83
83
|
TEXT_EXTENSIONS.has(ext));
|
|
84
84
|
}
|
|
85
85
|
function inferContentType(path) {
|
|
86
|
-
if (
|
|
86
|
+
if (/\.(?:tsx?|mts|cts)$/.test(path))
|
|
87
87
|
return 'text/typescript';
|
|
88
|
-
if (
|
|
88
|
+
if (/\.(?:jsx?|mjs|cjs)$/.test(path))
|
|
89
89
|
return 'text/javascript';
|
|
90
90
|
if (path.endsWith('.css'))
|
|
91
91
|
return 'text/css';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capabilities are code, roles are data: roles are created in Atlas, and
|
|
3
|
+
* `capabilities.roles.definitions` here is a deprecated one-time seed (a
|
|
4
|
+
* deploy creates any that don't exist yet and never updates them). Atlas only
|
|
5
|
+
* logs the drift, so tell the author.
|
|
6
|
+
*/
|
|
7
|
+
export declare function warnOnRoleDefinitions(manifest: unknown, warn?: (line: string) => void): void;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capabilities are code, roles are data: roles are created in Atlas, and
|
|
3
|
+
* `capabilities.roles.definitions` here is a deprecated one-time seed (a
|
|
4
|
+
* deploy creates any that don't exist yet and never updates them). Atlas only
|
|
5
|
+
* logs the drift, so tell the author.
|
|
6
|
+
*/
|
|
7
|
+
export function warnOnRoleDefinitions(manifest, warn = console.warn) {
|
|
8
|
+
// A non-null, non-array object is safe to index by key; TS can't narrow `object` to a record itself.
|
|
9
|
+
const record = (value) => typeof value === 'object' && value !== null && !Array.isArray(value) ? value : null;
|
|
10
|
+
const definitions = record(record(record(record(manifest)?.capabilities)?.roles)?.definitions);
|
|
11
|
+
if (!definitions)
|
|
12
|
+
return;
|
|
13
|
+
const keys = Object.keys(definitions).sort();
|
|
14
|
+
if (keys.length === 0)
|
|
15
|
+
return;
|
|
16
|
+
warn(`[seq-studio] artifact.bundle.yml defines roles (${keys.join(', ')}). Roles in YAML are a deprecated one-time seed: ` +
|
|
17
|
+
`a deploy creates any that don't exist yet and never updates them after. Create and edit roles in Atlas ` +
|
|
18
|
+
`(Permissions → Roles); keep capabilities in YAML.`);
|
|
19
|
+
}
|
|
@@ -72,9 +72,9 @@ export declare const artifactOperationSchema: z.ZodDiscriminatedUnion<[z.ZodObje
|
|
|
72
72
|
projectId: z.ZodString;
|
|
73
73
|
environment: z.ZodString;
|
|
74
74
|
kind: z.ZodEnum<{
|
|
75
|
-
preview: "preview";
|
|
76
75
|
build: "build";
|
|
77
76
|
deploy: "deploy";
|
|
77
|
+
preview: "preview";
|
|
78
78
|
rollback: "rollback";
|
|
79
79
|
suspend: "suspend";
|
|
80
80
|
delete: "delete";
|
|
@@ -92,9 +92,9 @@ export declare const artifactOperationSchema: z.ZodDiscriminatedUnion<[z.ZodObje
|
|
|
92
92
|
projectId: z.ZodString;
|
|
93
93
|
environment: z.ZodString;
|
|
94
94
|
kind: z.ZodEnum<{
|
|
95
|
-
preview: "preview";
|
|
96
95
|
build: "build";
|
|
97
96
|
deploy: "deploy";
|
|
97
|
+
preview: "preview";
|
|
98
98
|
rollback: "rollback";
|
|
99
99
|
suspend: "suspend";
|
|
100
100
|
delete: "delete";
|
|
@@ -109,9 +109,9 @@ export declare const artifactOperationSchema: z.ZodDiscriminatedUnion<[z.ZodObje
|
|
|
109
109
|
projectId: z.ZodString;
|
|
110
110
|
environment: z.ZodString;
|
|
111
111
|
kind: z.ZodEnum<{
|
|
112
|
-
preview: "preview";
|
|
113
112
|
build: "build";
|
|
114
113
|
deploy: "deploy";
|
|
114
|
+
preview: "preview";
|
|
115
115
|
rollback: "rollback";
|
|
116
116
|
suspend: "suspend";
|
|
117
117
|
delete: "delete";
|
|
@@ -130,9 +130,9 @@ export declare const artifactOperationSchema: z.ZodDiscriminatedUnion<[z.ZodObje
|
|
|
130
130
|
projectId: z.ZodString;
|
|
131
131
|
environment: z.ZodString;
|
|
132
132
|
kind: z.ZodEnum<{
|
|
133
|
-
preview: "preview";
|
|
134
133
|
build: "build";
|
|
135
134
|
deploy: "deploy";
|
|
135
|
+
preview: "preview";
|
|
136
136
|
rollback: "rollback";
|
|
137
137
|
suspend: "suspend";
|
|
138
138
|
delete: "delete";
|
|
@@ -147,9 +147,9 @@ export declare const artifactOperationSchema: z.ZodDiscriminatedUnion<[z.ZodObje
|
|
|
147
147
|
projectId: z.ZodString;
|
|
148
148
|
environment: z.ZodString;
|
|
149
149
|
kind: z.ZodEnum<{
|
|
150
|
-
preview: "preview";
|
|
151
150
|
build: "build";
|
|
152
151
|
deploy: "deploy";
|
|
152
|
+
preview: "preview";
|
|
153
153
|
rollback: "rollback";
|
|
154
154
|
suspend: "suspend";
|
|
155
155
|
delete: "delete";
|
|
@@ -164,9 +164,9 @@ export declare const artifactRuntimeSessionSchema: z.ZodObject<{
|
|
|
164
164
|
projectId: z.ZodString;
|
|
165
165
|
environment: z.ZodString;
|
|
166
166
|
mode: z.ZodEnum<{
|
|
167
|
+
local: "local";
|
|
167
168
|
preview: "preview";
|
|
168
169
|
published: "published";
|
|
169
|
-
local: "local";
|
|
170
170
|
}>;
|
|
171
171
|
origin: z.ZodString;
|
|
172
172
|
parentOrigin: z.ZodString;
|
|
@@ -187,12 +187,12 @@ export declare const artifactLocalDevSessionSchema: z.ZodObject<{
|
|
|
187
187
|
sourceRevision: z.ZodOptional<z.ZodString>;
|
|
188
188
|
revision: z.ZodNumber;
|
|
189
189
|
state: z.ZodEnum<{
|
|
190
|
+
offline: "offline";
|
|
190
191
|
failed: "failed";
|
|
191
192
|
starting: "starting";
|
|
192
193
|
compiling: "compiling";
|
|
193
194
|
ready: "ready";
|
|
194
195
|
reconnecting: "reconnecting";
|
|
195
|
-
offline: "offline";
|
|
196
196
|
revoked: "revoked";
|
|
197
197
|
expired: "expired";
|
|
198
198
|
}>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { ARTIFACT_NEXT_RUNTIME, artifactNextSecretNameSchema } from './manifest-next.js';
|
|
3
3
|
import { MAX_STUDIO_PATH_LENGTH } from './paths.js';
|
|
4
|
+
import { isLoopbackHostname } from './loopback.js';
|
|
4
5
|
export { resolveArtifactNextImages } from './next-image-config.js';
|
|
5
6
|
/** Shared by local collection, repository collection and the bounded source-upload API. */
|
|
6
7
|
export const ARTIFACT_NEXT_SOURCE_LIMITS = Object.freeze({
|
|
@@ -42,7 +43,7 @@ export const artifactSourcePathSchema = z.string().min(1).max(MAX_STUDIO_PATH_LE
|
|
|
42
43
|
export const artifactRuntimeOriginSchema = z.string().max(2048).refine((value) => {
|
|
43
44
|
try {
|
|
44
45
|
const url = new URL(value);
|
|
45
|
-
return url.protocol === 'https:' && url.origin === value && !url.hostname.includes('*');
|
|
46
|
+
return (url.protocol === 'https:' || (url.protocol === 'http:' && isLoopbackHostname(url.hostname))) && url.origin === value && !url.hostname.includes('*');
|
|
46
47
|
}
|
|
47
48
|
catch {
|
|
48
49
|
return false;
|
package/dist/sandbox-lint.js
CHANGED
|
@@ -42,7 +42,7 @@ const RULES = [
|
|
|
42
42
|
message: "Direct fetch() to an external origin is blocked (CSP connect-src 'self') — call Atlas routes via seq.api.fetch.",
|
|
43
43
|
},
|
|
44
44
|
];
|
|
45
|
-
const LINTABLE_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx', '.css', '.html']);
|
|
45
|
+
const LINTABLE_EXTENSIONS = new Set(['.ts', '.tsx', '.mts', '.cts', '.js', '.jsx', '.mjs', '.cjs', '.css', '.html']);
|
|
46
46
|
function extensionOf(path) {
|
|
47
47
|
return path.includes('.') ? path.slice(path.lastIndexOf('.')) : '';
|
|
48
48
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type OperationExecutor } from '@sequenceholdings/orm/client';
|
|
2
|
+
import { type ArtifactOrmJsonScalars } from './orm-json-scalars.js';
|
|
3
|
+
import { type SequenceApi } from './sdk.js';
|
|
4
|
+
export interface ArtifactOrmExecutorConfig {
|
|
5
|
+
namespace: string;
|
|
6
|
+
/** The generated operations.manifest.json, validated before any host call. */
|
|
7
|
+
manifest: unknown;
|
|
8
|
+
/** Optional action-scoped host client (for example, withTraceAction's client). */
|
|
9
|
+
sdk?: {
|
|
10
|
+
api: Pick<SequenceApi, 'fetch'>;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* `lossless-v1` returns numeric and bigint values as strings instead of JSON numbers.
|
|
14
|
+
* The executor refuses any successful result the host does not acknowledge in that form.
|
|
15
|
+
*/
|
|
16
|
+
jsonScalars?: ArtifactOrmJsonScalars;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Execute persisted operations as the signed-in Artifact user. The host owns
|
|
20
|
+
* authentication; this helper never obtains or exposes browser credentials.
|
|
21
|
+
* Declare the namespace path in capabilities.api.write (queries also use POST).
|
|
22
|
+
* Namespace grants, capabilities, row policies, and write checks still apply.
|
|
23
|
+
*/
|
|
24
|
+
export declare function createArtifactOrmExecutor({ namespace, manifest, sdk, jsonScalars }: ArtifactOrmExecutorConfig): OperationExecutor;
|
package/dist/sdk-orm.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { createOperationExecutorWithTransport, OrmClientError } from '@sequenceholdings/orm/client';
|
|
2
|
+
import { assertJsonScalarsAcknowledged, ORM_JSON_SCALARS_HEADER } from './orm-json-scalars.js';
|
|
3
|
+
import { seq } from './sdk.js';
|
|
4
|
+
/**
|
|
5
|
+
* Execute persisted operations as the signed-in Artifact user. The host owns
|
|
6
|
+
* authentication; this helper never obtains or exposes browser credentials.
|
|
7
|
+
* Declare the namespace path in capabilities.api.write (queries also use POST).
|
|
8
|
+
* Namespace grants, capabilities, row policies, and write checks still apply.
|
|
9
|
+
*/
|
|
10
|
+
export function createArtifactOrmExecutor({ namespace, manifest, sdk = seq, jsonScalars }) {
|
|
11
|
+
if (!sdk || typeof sdk.api?.fetch !== 'function') {
|
|
12
|
+
throw new OrmClientError('Artifact ORM requires the authenticated Artifact Studio host; open the deployed artifact or its Preview');
|
|
13
|
+
}
|
|
14
|
+
return createOperationExecutorWithTransport({
|
|
15
|
+
namespace,
|
|
16
|
+
manifest,
|
|
17
|
+
transport: async ({ namespace: target, op, variables, idempotencyKey }) => {
|
|
18
|
+
const response = await sdk.api.fetch(`/api/data/v2/${target}`, {
|
|
19
|
+
method: 'POST',
|
|
20
|
+
body: { op, variables, ...(idempotencyKey === undefined ? {} : { idempotencyKey }) },
|
|
21
|
+
...(jsonScalars ? { headers: { [ORM_JSON_SCALARS_HEADER]: jsonScalars } } : {}),
|
|
22
|
+
});
|
|
23
|
+
if (response.ok)
|
|
24
|
+
assertJsonScalarsAcknowledged({ requested: jsonScalars, acknowledged: response.headers?.[ORM_JSON_SCALARS_HEADER], status: response.status });
|
|
25
|
+
return response;
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
}
|
package/dist/sdk.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { type StartStorageUpload, type StorageUploadSession } from '@sequenceholdings/storage';
|
|
2
|
+
export { createArtifactOrmExecutor, type ArtifactOrmExecutorConfig } from './sdk-orm.js';
|
|
3
|
+
export type { ArtifactOrmJsonScalars } from './orm-json-scalars.js';
|
|
2
4
|
export interface SequenceStorageOptions {
|
|
3
5
|
signal?: AbortSignal;
|
|
4
6
|
timeoutMs?: number;
|
|
@@ -47,6 +49,54 @@ export interface SequenceApi {
|
|
|
47
49
|
patch<TBody = unknown>(path: string, body?: unknown): Promise<TBody>;
|
|
48
50
|
delete<TBody = unknown>(path: string, body?: unknown): Promise<TBody>;
|
|
49
51
|
}
|
|
52
|
+
export type SequenceOfflineRequestMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
53
|
+
export interface SequenceOfflineRequest {
|
|
54
|
+
method: SequenceOfflineRequestMethod;
|
|
55
|
+
path: string;
|
|
56
|
+
body?: unknown;
|
|
57
|
+
headers?: Record<string, string>;
|
|
58
|
+
}
|
|
59
|
+
export interface SequenceOfflineConnectorExecution<TBody = unknown> {
|
|
60
|
+
request: SequenceOfflineRequest;
|
|
61
|
+
/**
|
|
62
|
+
* Invoke the authenticated Artifact Studio bridge. Authentication remains
|
|
63
|
+
* host-owned and is never exposed to the connector or durable storage.
|
|
64
|
+
*/
|
|
65
|
+
network: (request: SequenceOfflineRequest) => Promise<SequenceApiFetchResponse<TBody>>;
|
|
66
|
+
}
|
|
67
|
+
export interface SequenceOfflineNetwork {
|
|
68
|
+
execute<TBody = unknown>(request: SequenceOfflineRequest): Promise<SequenceApiFetchResponse<TBody>>;
|
|
69
|
+
}
|
|
70
|
+
export interface SequenceOfflineRequestConnector {
|
|
71
|
+
execute<TBody = unknown>(execution: SequenceOfflineConnectorExecution<TBody>): Promise<SequenceApiFetchResponse<TBody>>;
|
|
72
|
+
/**
|
|
73
|
+
* Receives a capability-gated bridge executor for durable outbox replay.
|
|
74
|
+
* It carries no bearer token and remains subject to the active manifest.
|
|
75
|
+
*/
|
|
76
|
+
attach?(context: {
|
|
77
|
+
network: SequenceOfflineNetwork;
|
|
78
|
+
}): void | (() => void);
|
|
79
|
+
}
|
|
80
|
+
export interface SequenceOffline {
|
|
81
|
+
/**
|
|
82
|
+
* Install the artifact's request policy beneath seq.api. Returns a disposer
|
|
83
|
+
* that restores the network-only default when it still owns registration.
|
|
84
|
+
*/
|
|
85
|
+
registerConnector(registration: {
|
|
86
|
+
connector: SequenceOfflineRequestConnector;
|
|
87
|
+
}): () => void;
|
|
88
|
+
/** Cache the current artifact shell on this device after the user's opt-in. */
|
|
89
|
+
enroll(options?: {
|
|
90
|
+
maxAgeHours?: number;
|
|
91
|
+
}): Promise<void>;
|
|
92
|
+
/** Remove this artifact's cached shell. App-owned data should be wiped too. */
|
|
93
|
+
remove(): Promise<void>;
|
|
94
|
+
status(): Promise<{
|
|
95
|
+
enabled: boolean;
|
|
96
|
+
enrolled: boolean;
|
|
97
|
+
expiresAt?: number;
|
|
98
|
+
}>;
|
|
99
|
+
}
|
|
50
100
|
export interface SequenceFunctions {
|
|
51
101
|
invoke<TOutput = unknown>(id: string, params?: Record<string, unknown>, traceAction?: {
|
|
52
102
|
id: string;
|
|
@@ -134,6 +184,7 @@ export interface SequenceHost {
|
|
|
134
184
|
export interface SequenceArtifactSdk {
|
|
135
185
|
storage: SequenceStorage;
|
|
136
186
|
api: SequenceApi;
|
|
187
|
+
offline: SequenceOffline;
|
|
137
188
|
ai: {
|
|
138
189
|
fetch(path: string, options?: SequenceApiFetchOptions): Promise<Response>;
|
|
139
190
|
};
|
|
@@ -210,6 +261,73 @@ export declare function createArtifactStorageClient(sdk?: SequenceArtifactSdk):
|
|
|
210
261
|
versionId: string | null;
|
|
211
262
|
maxBytes: number;
|
|
212
263
|
}>;
|
|
264
|
+
startBatch: (items: StartStorageUpload[]) => Promise<{
|
|
265
|
+
items: {
|
|
266
|
+
id: string;
|
|
267
|
+
fileId: string;
|
|
268
|
+
uploadUrl: string | null;
|
|
269
|
+
expiresAt: string;
|
|
270
|
+
state: "pending" | "complete" | "aborted";
|
|
271
|
+
versionId: string | null;
|
|
272
|
+
maxBytes: number;
|
|
273
|
+
}[];
|
|
274
|
+
}>;
|
|
275
|
+
list: () => Promise<{
|
|
276
|
+
items: {
|
|
277
|
+
id: string;
|
|
278
|
+
fileId: string;
|
|
279
|
+
name: string;
|
|
280
|
+
state: "pending" | "complete" | "aborted";
|
|
281
|
+
expiresAt: string;
|
|
282
|
+
maxBytes: number;
|
|
283
|
+
expectedSize: number | null;
|
|
284
|
+
versionId: string | null;
|
|
285
|
+
}[];
|
|
286
|
+
limits: {
|
|
287
|
+
perPrincipal: number;
|
|
288
|
+
perWorkspace: number;
|
|
289
|
+
};
|
|
290
|
+
}>;
|
|
291
|
+
reconcile: (input?: {
|
|
292
|
+
abortIncomplete?: boolean;
|
|
293
|
+
}) => Promise<{
|
|
294
|
+
completed: {
|
|
295
|
+
id: string;
|
|
296
|
+
fileId: string;
|
|
297
|
+
name: string;
|
|
298
|
+
state: "pending" | "complete" | "aborted";
|
|
299
|
+
expiresAt: string;
|
|
300
|
+
maxBytes: number;
|
|
301
|
+
expectedSize: number | null;
|
|
302
|
+
versionId: string | null;
|
|
303
|
+
code: string;
|
|
304
|
+
message: string;
|
|
305
|
+
}[];
|
|
306
|
+
aborted: {
|
|
307
|
+
id: string;
|
|
308
|
+
fileId: string;
|
|
309
|
+
name: string;
|
|
310
|
+
state: "pending" | "complete" | "aborted";
|
|
311
|
+
expiresAt: string;
|
|
312
|
+
maxBytes: number;
|
|
313
|
+
expectedSize: number | null;
|
|
314
|
+
versionId: string | null;
|
|
315
|
+
code: string;
|
|
316
|
+
message: string;
|
|
317
|
+
}[];
|
|
318
|
+
pending: {
|
|
319
|
+
id: string;
|
|
320
|
+
fileId: string;
|
|
321
|
+
name: string;
|
|
322
|
+
state: "pending" | "complete" | "aborted";
|
|
323
|
+
expiresAt: string;
|
|
324
|
+
maxBytes: number;
|
|
325
|
+
expectedSize: number | null;
|
|
326
|
+
versionId: string | null;
|
|
327
|
+
code: string;
|
|
328
|
+
message: string;
|
|
329
|
+
}[];
|
|
330
|
+
}>;
|
|
213
331
|
};
|
|
214
332
|
get: () => Promise<{
|
|
215
333
|
id: string;
|
|
@@ -488,6 +606,15 @@ export declare function createArtifactStorageClient(sdk?: SequenceArtifactSdk):
|
|
|
488
606
|
capabilities: () => Promise<{
|
|
489
607
|
canProvision: boolean;
|
|
490
608
|
uploadMethods: string[];
|
|
609
|
+
pendingUploads: {
|
|
610
|
+
perPrincipal: number;
|
|
611
|
+
perWorkspace: number;
|
|
612
|
+
};
|
|
613
|
+
uploadBatchLimit: number;
|
|
614
|
+
importConcurrency: {
|
|
615
|
+
default: number;
|
|
616
|
+
max: number;
|
|
617
|
+
};
|
|
491
618
|
}>;
|
|
492
619
|
list: (options?: import("@sequenceholdings/storage").StorageListOptions) => Promise<{
|
|
493
620
|
items: {
|