@sequenceholdings/artifact-studio 0.2.5 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -0
- package/VERSION-PIN.md +6 -2
- package/dist/atlas-ui-css-plugin.d.ts +11 -7
- package/dist/atlas-ui-css-plugin.js +38 -10
- package/dist/build.js +10 -6
- package/dist/cli.d.ts +0 -1
- package/dist/cli.js +3 -27
- package/dist/manifest.d.ts +6 -6
- package/dist/manifest.js +3 -2
- package/dist/sdk.d.ts +420 -1
- package/dist/sdk.js +35 -0
- package/dist/templates/react-vite/artifact.bundle.yml +0 -1
- package/package.json +3 -2
- package/templates/react-vite/artifact.bundle.yml +0 -1
package/README.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Artifact Studio SDK
|
|
2
|
+
|
|
3
|
+
`@sequenceholdings/artifact-studio` exposes the host-provided `seq` client. Runtime
|
|
4
|
+
requests use the current user's normal permissions.
|
|
5
|
+
|
|
6
|
+
Artifact Studio is a workflow-agnostic platform primitive. The host, SDK, and
|
|
7
|
+
deployer provide generic application capabilities; each deployed artifact owns
|
|
8
|
+
its product behavior. Primitive code must not branch on an artifact ID or import
|
|
9
|
+
an application's services. See [Platform primitive boundaries](../../../docs/platform/platform-primitives.md).
|
|
10
|
+
|
|
11
|
+
## Grouping calls into a trace
|
|
12
|
+
|
|
13
|
+
The Artifact host supplies tracing context automatically for supported platform
|
|
14
|
+
requests. Use an explicit action scope to correlate several calls from one UI
|
|
15
|
+
action and supply a readable intent key:
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { withTraceAction } from '@sequenceholdings/artifact-studio'
|
|
19
|
+
|
|
20
|
+
await withTraceAction(
|
|
21
|
+
{ key: 'application.refresh' },
|
|
22
|
+
async ({ api, functions }) => {
|
|
23
|
+
await functions.invoke('my-function', { refresh: true })
|
|
24
|
+
await api.get('/api/my-application/summary')
|
|
25
|
+
},
|
|
26
|
+
)
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Use a stable operation label, never an identifier, customer value or payload. Each
|
|
30
|
+
scope gets its own correlation ID, so concurrent actions remain separate. The
|
|
31
|
+
helper scopes JSON `api.fetch/get/post/put/patch/delete` and `functions.invoke`;
|
|
32
|
+
streaming and file-transfer helpers retain their ordinary host tracing behavior.
|
|
33
|
+
Tracing does not grant permissions, change service-account behavior or guarantee
|
|
34
|
+
that every transport is instrumented. Traces availability depends on the deployed
|
|
35
|
+
platform version and your app/resource permissions.
|
package/VERSION-PIN.md
CHANGED
|
@@ -53,8 +53,12 @@ never claims `no-op` for a deploy that would actually rebuild.
|
|
|
53
53
|
|
|
54
54
|
`tokens.css` is the canonical stylesheet and includes semantic roles plus the
|
|
55
55
|
Tailwind utility bridge. The legacy `tokens.v2.css` and `theme.v2.css` paths are
|
|
56
|
-
temporary forwarding aliases; the compiler normalizes their
|
|
57
|
-
canonical layer and reports a migration warning
|
|
56
|
+
temporary forwarding aliases in current packages; the compiler normalizes their
|
|
57
|
+
source imports to one canonical layer and reports a migration warning only after
|
|
58
|
+
checking that both selected files are forwarding aliases. Older packages carry
|
|
59
|
+
independent styles in those files, so their imports and provenance entries are
|
|
60
|
+
preserved. This prevents CRM spacing and corner utilities from disappearing when
|
|
61
|
+
rebuilding with a split-sheet package. An explicit
|
|
58
62
|
`seq-studio artifact dev --link <atlas-ui-package-dir>` preview additionally
|
|
59
63
|
reports retired numbered black/white opacity utilities and uncertain dynamic
|
|
60
64
|
class construction. Legacy stylesheet aliases normalize to canonical tokens;
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import type { Plugin } from 'vite';
|
|
2
2
|
export interface AtlasUiResolvedTokenSelection {
|
|
3
|
-
specifier:
|
|
3
|
+
specifier: string;
|
|
4
4
|
injectedBy: string | null;
|
|
5
5
|
}
|
|
6
|
+
/** Only collapse aliases when the selected package makes them forwarding sheets.
|
|
7
|
+
* Older releases define independent semantic tokens and the Tailwind bridge there.
|
|
8
|
+
*/
|
|
9
|
+
export declare function hasUnifiedAtlasUiTokens(atlasUiDir: string): boolean;
|
|
6
10
|
/**
|
|
7
|
-
*
|
|
8
|
-
* expands
|
|
11
|
+
* For packages with forwarding aliases, collapse imports before Tailwind
|
|
12
|
+
* expands them. The aliases remain package-compatible, but are no longer allowed
|
|
9
13
|
* to create duplicate token layers in an Artifact Studio bundle (DES-437).
|
|
10
14
|
*/
|
|
11
15
|
export declare function normalizeAtlasUiTokenImports(code: string): {
|
|
@@ -13,10 +17,10 @@ export declare function normalizeAtlasUiTokenImports(code: string): {
|
|
|
13
17
|
deprecatedImports: string[];
|
|
14
18
|
};
|
|
15
19
|
/**
|
|
16
|
-
* Evidence mirrors the
|
|
17
|
-
*
|
|
20
|
+
* Evidence mirrors the selected package: split sheets remain separate entries;
|
|
21
|
+
* forwarding aliases are normalized to the canonical entry point.
|
|
18
22
|
*/
|
|
19
|
-
export declare function collectAtlasUiResolvedTokenSelections({ cssModules, }: {
|
|
23
|
+
export declare function collectAtlasUiResolvedTokenSelections({ cssModules, atlasUiDir, }: {
|
|
20
24
|
cssModules: readonly {
|
|
21
25
|
id: string;
|
|
22
26
|
code: string;
|
|
@@ -25,7 +29,7 @@ export declare function collectAtlasUiResolvedTokenSelections({ cssModules, }: {
|
|
|
25
29
|
atlasUiDist: string;
|
|
26
30
|
formRendererDist: string | null;
|
|
27
31
|
}): AtlasUiResolvedTokenSelection[];
|
|
28
|
-
export declare function atlasUiCssResolverPlugin({ atlasUiDir
|
|
32
|
+
export declare function atlasUiCssResolverPlugin({ atlasUiDir, atlasUiDist, formRendererDist, onTransform, }: {
|
|
29
33
|
atlasUiDir: string;
|
|
30
34
|
atlasUiDist: string;
|
|
31
35
|
formRendererDist: string | null;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync } from 'node:fs';
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
3
|
import { dirname, resolve } from 'node:path';
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
@@ -19,14 +19,26 @@ function resolveTwAnimateCssPath() {
|
|
|
19
19
|
}
|
|
20
20
|
const CANONICAL_TOKEN_IMPORT = '@import "@sequenceholdings/atlas-ui/tokens.css";';
|
|
21
21
|
const ATLAS_UI_TOKEN_IMPORT = /@import\s+(?:url\(\s*)?(?:["'][^"']*?@sequenceholdings\/atlas-ui\/|[^"'()\s;]*@sequenceholdings\/atlas-ui\/)(tokens\.css|tokens\.v2\.css|theme\.v2\.css)(?:["'])?\s*\)?[^;\n]*;/gi;
|
|
22
|
+
/** Only collapse aliases when the selected package makes them forwarding sheets.
|
|
23
|
+
* Older releases define independent semantic tokens and the Tailwind bridge there.
|
|
24
|
+
*/
|
|
25
|
+
export function hasUnifiedAtlasUiTokens(atlasUiDir) {
|
|
26
|
+
return ['tokens.v2.css', 'theme.v2.css'].every((sheet) => {
|
|
27
|
+
const path = resolve(atlasUiDir, sheet);
|
|
28
|
+
if (!existsSync(path))
|
|
29
|
+
return false;
|
|
30
|
+
const content = readFileSync(path, 'utf8').replace(/\/\*[\s\S]*?\*\//g, '').trim();
|
|
31
|
+
return /^@import\s+["']@sequenceholdings\/atlas-ui\/tokens\.css["'];$/.test(content);
|
|
32
|
+
});
|
|
33
|
+
}
|
|
22
34
|
function atlasUiTokenImportSheets(code) {
|
|
23
35
|
return [...code.matchAll(ATLAS_UI_TOKEN_IMPORT)]
|
|
24
36
|
.map((match) => match[1]?.toLowerCase())
|
|
25
37
|
.filter((sheet) => sheet !== undefined);
|
|
26
38
|
}
|
|
27
39
|
/**
|
|
28
|
-
*
|
|
29
|
-
* expands
|
|
40
|
+
* For packages with forwarding aliases, collapse imports before Tailwind
|
|
41
|
+
* expands them. The aliases remain package-compatible, but are no longer allowed
|
|
30
42
|
* to create duplicate token layers in an Artifact Studio bundle (DES-437).
|
|
31
43
|
*/
|
|
32
44
|
export function normalizeAtlasUiTokenImports(code) {
|
|
@@ -43,16 +55,23 @@ export function normalizeAtlasUiTokenImports(code) {
|
|
|
43
55
|
};
|
|
44
56
|
}
|
|
45
57
|
/**
|
|
46
|
-
* Evidence mirrors the
|
|
47
|
-
*
|
|
58
|
+
* Evidence mirrors the selected package: split sheets remain separate entries;
|
|
59
|
+
* forwarding aliases are normalized to the canonical entry point.
|
|
48
60
|
*/
|
|
49
|
-
export function collectAtlasUiResolvedTokenSelections({ cssModules, }) {
|
|
61
|
+
export function collectAtlasUiResolvedTokenSelections({ cssModules, atlasUiDir, }) {
|
|
50
62
|
for (const { code } of cssModules) {
|
|
51
63
|
if (!code.includes('@import "tailwindcss"'))
|
|
52
64
|
continue;
|
|
65
|
+
const sheets = atlasUiTokenImportSheets(code);
|
|
66
|
+
if (!hasUnifiedAtlasUiTokens(atlasUiDir)) {
|
|
67
|
+
return [...new Set(['tokens.css', ...sheets])].map((sheet) => ({
|
|
68
|
+
specifier: `@sequenceholdings/atlas-ui/${sheet}`,
|
|
69
|
+
injectedBy: sheets.includes(sheet) ? null : 'atlas-ui-css-resolver',
|
|
70
|
+
}));
|
|
71
|
+
}
|
|
53
72
|
return [{
|
|
54
73
|
specifier: '@sequenceholdings/atlas-ui/tokens.css',
|
|
55
|
-
injectedBy:
|
|
74
|
+
injectedBy: sheets.includes('tokens.css') ? null : 'atlas-ui-css-resolver',
|
|
56
75
|
}];
|
|
57
76
|
}
|
|
58
77
|
return [];
|
|
@@ -70,7 +89,8 @@ function insertAfterImports(code, lines) {
|
|
|
70
89
|
const end = lastImport.index + lastImport[0].length;
|
|
71
90
|
return `${code.slice(0, end)}${block}${code.slice(end)}`;
|
|
72
91
|
}
|
|
73
|
-
export function atlasUiCssResolverPlugin({ atlasUiDir
|
|
92
|
+
export function atlasUiCssResolverPlugin({ atlasUiDir, atlasUiDist, formRendererDist, onTransform, }) {
|
|
93
|
+
const unifiedTokens = hasUnifiedAtlasUiTokens(atlasUiDir);
|
|
74
94
|
return {
|
|
75
95
|
name: 'atlas-ui-css-resolver',
|
|
76
96
|
enforce: 'pre',
|
|
@@ -88,8 +108,16 @@ export function atlasUiCssResolverPlugin({ atlasUiDir: _atlasUiDir, atlasUiDist,
|
|
|
88
108
|
if (formRendererDist) {
|
|
89
109
|
sources.push(`@source ${JSON.stringify(formRendererDist)};`);
|
|
90
110
|
}
|
|
91
|
-
|
|
92
|
-
|
|
111
|
+
if (unifiedTokens) {
|
|
112
|
+
result = normalizeAtlasUiTokenImports(result).code;
|
|
113
|
+
}
|
|
114
|
+
else if (!atlasUiTokenImportSheets(result).includes('tokens.css')) {
|
|
115
|
+
const firstTokenImport = result.match(ATLAS_UI_TOKEN_IMPORT)?.[0];
|
|
116
|
+
// The old base must precede the semantic overrides in split packages.
|
|
117
|
+
result = firstTokenImport
|
|
118
|
+
? result.replace(firstTokenImport, `${CANONICAL_TOKEN_IMPORT}\n${firstTokenImport}`)
|
|
119
|
+
: insertAfterImports(result, [CANONICAL_TOKEN_IMPORT]);
|
|
120
|
+
}
|
|
93
121
|
result = insertAfterImports(result, sources);
|
|
94
122
|
}
|
|
95
123
|
if (result.includes('@import "tw-animate-css"')) {
|
package/dist/build.js
CHANGED
|
@@ -3,7 +3,7 @@ import { mkdir, writeFile } from 'node:fs/promises';
|
|
|
3
3
|
import { createRequire } from 'node:module';
|
|
4
4
|
import { dirname, relative, resolve } from 'node:path';
|
|
5
5
|
import { build as viteBuild } from 'vite';
|
|
6
|
-
import { atlasUiCssResolverPlugin, collectAtlasUiResolvedTokenSelections } from './atlas-ui-css-plugin.js';
|
|
6
|
+
import { atlasUiCssResolverPlugin, collectAtlasUiResolvedTokenSelections, hasUnifiedAtlasUiTokens } from './atlas-ui-css-plugin.js';
|
|
7
7
|
import { validateAtlasUiTokenStylesheetContract } from './atlas-ui-package-contract.js';
|
|
8
8
|
import { createObservedBuildEvidence } from './build-provenance.js';
|
|
9
9
|
import { collectResolvedStylesheets } from './resolved-stylesheets.js';
|
|
@@ -31,6 +31,7 @@ const reactRouterDomDir = dirname(cliRequire.resolve('react-router-dom/package.j
|
|
|
31
31
|
const sonnerDir = dirname(cliRequire.resolve('sonner'));
|
|
32
32
|
const atlasUiDir = dirname(cliRequire.resolve('@sequenceholdings/atlas-ui/package.json'));
|
|
33
33
|
const atlasUiDist = resolve(atlasUiDir, 'dist');
|
|
34
|
+
const artifactSdkEntry = cliRequire.resolve('@sequenceholdings/artifact-studio');
|
|
34
35
|
/**
|
|
35
36
|
* Released Atlas UI 2.0.1 exports its canonical sheet from dist, while the
|
|
36
37
|
* candidate layout puts the generated entry at package root so it can be read
|
|
@@ -75,6 +76,7 @@ function createPeerResolveAlias({ atlasUiPackageDir = atlasUiDir } = {}) {
|
|
|
75
76
|
'@tanstack/react-query': reactQueryDir,
|
|
76
77
|
'react-router-dom': reactRouterDomDir,
|
|
77
78
|
sonner: sonnerDir,
|
|
79
|
+
'@sequenceholdings/storage': cliRequire.resolve('@sequenceholdings/storage'),
|
|
78
80
|
// Candidate Atlas UI exposes canonical tokens at the package root; released
|
|
79
81
|
// 2.0.1 keeps them in dist. Preserve explicit alias resolution so legacy
|
|
80
82
|
// source reaches the normalizer instead of falling through to the bare alias.
|
|
@@ -102,7 +104,9 @@ export async function buildArtifactStudioProject(rootDir, { candidateTokenCompat
|
|
|
102
104
|
const selectedAtlasUiTokens = resolveAtlasUiTokensPath(selectedAtlasUiDir);
|
|
103
105
|
const source = await readArtifactStudioSource(rootDir);
|
|
104
106
|
const diagnostics = [
|
|
105
|
-
...
|
|
107
|
+
...(hasUnifiedAtlasUiTokens(selectedAtlasUiDir)
|
|
108
|
+
? collectDeprecatedTokenImportDiagnostics({ files: source.files })
|
|
109
|
+
: []),
|
|
106
110
|
...(candidateTokenCompatibility
|
|
107
111
|
? collectCandidateTokenCompatibilityDiagnostics({ files: source.files })
|
|
108
112
|
: []),
|
|
@@ -116,7 +120,7 @@ export async function buildArtifactStudioProject(rootDir, { candidateTokenCompat
|
|
|
116
120
|
plugins: [
|
|
117
121
|
rejectOutsideSourcePlugin({
|
|
118
122
|
root,
|
|
119
|
-
trustedExternalRoots: Object.values(peerResolveAlias).map((target) => dirname(target)),
|
|
123
|
+
trustedExternalRoots: [...Object.values(peerResolveAlias).map((target) => dirname(target)), dirname(artifactSdkEntry)],
|
|
120
124
|
}),
|
|
121
125
|
rejectIgnoredSourcePlugin(root, source.ignorePatterns),
|
|
122
126
|
artifactSdkVirtualModule(),
|
|
@@ -267,8 +271,8 @@ function artifactSdkVirtualModule() {
|
|
|
267
271
|
// host-injected `window.seq`, so a tree that happens to be `pnpm install`ed
|
|
268
272
|
// must produce the same bundle as a bare tree (e.g. one materialized from a
|
|
269
273
|
// git repo) — otherwise the same source yields different bytes depending on
|
|
270
|
-
// whether node_modules exists.
|
|
271
|
-
//
|
|
274
|
+
// whether node_modules exists. Use the CLI's SDK entry so runtime helpers
|
|
275
|
+
// stay in sync with the types instead of maintaining a partial export stub.
|
|
272
276
|
enforce: 'pre',
|
|
273
277
|
resolveId(id) {
|
|
274
278
|
return virtualIds.has(id) ? resolved : undefined;
|
|
@@ -276,7 +280,7 @@ function artifactSdkVirtualModule() {
|
|
|
276
280
|
load(id) {
|
|
277
281
|
if (id !== resolved)
|
|
278
282
|
return undefined;
|
|
279
|
-
return
|
|
283
|
+
return `export * from ${JSON.stringify(artifactSdkEntry)}; export { default } from ${JSON.stringify(artifactSdkEntry)};`;
|
|
280
284
|
},
|
|
281
285
|
};
|
|
282
286
|
}
|
package/dist/cli.d.ts
CHANGED
package/dist/cli.js
CHANGED
|
@@ -254,8 +254,7 @@ async function listCommand(args) {
|
|
|
254
254
|
: project.activeDeploymentId
|
|
255
255
|
? 'deployed'
|
|
256
256
|
: 'not deployed';
|
|
257
|
-
|
|
258
|
-
console.log(` ${project.slug} id=${project.id} [${active}]${visibility} ${project.title}`);
|
|
257
|
+
console.log(` ${project.slug} id=${project.id} [${active}] ${project.title}`);
|
|
259
258
|
}
|
|
260
259
|
return 0;
|
|
261
260
|
}
|
|
@@ -300,8 +299,6 @@ async function showCommand(args) {
|
|
|
300
299
|
console.log(` title: ${project.title}`);
|
|
301
300
|
if (project.description)
|
|
302
301
|
console.log(` description: ${project.description}`);
|
|
303
|
-
if (project.visibility)
|
|
304
|
-
console.log(` visibility: ${project.visibility}`);
|
|
305
302
|
if (project.appEligible !== undefined)
|
|
306
303
|
console.log(` appEligible: ${project.appEligible ? 'yes' : 'no'}`);
|
|
307
304
|
if (project.createdBy)
|
|
@@ -499,7 +496,6 @@ async function deployCommand(args) {
|
|
|
499
496
|
linkConfigDir: spec.kind === 'local' ? source.dir : null,
|
|
500
497
|
allowCreate: args.flags['no-create'] !== true,
|
|
501
498
|
explicitProjectId,
|
|
502
|
-
envName: env.name,
|
|
503
499
|
});
|
|
504
500
|
const deployment = await uploadDeployment({ env: env.url, token, projectId: project.id, result, channel: 'active', provenance: source.provenance });
|
|
505
501
|
console.log(`[seq-studio] deployed ${project.title} ${deployment.version}`);
|
|
@@ -589,9 +585,7 @@ async function devCommand(args) {
|
|
|
589
585
|
// Resolve per push so the token refreshes across a long watch session.
|
|
590
586
|
const token = await getRequiredToken(args);
|
|
591
587
|
if (!projectId) {
|
|
592
|
-
|
|
593
|
-
// visibility too (and notes drift, once, when the project pre-exists).
|
|
594
|
-
const project = await ensureRemoteProject({ env: env.url, token, result, linkConfigDir: dir, envName: env.name });
|
|
588
|
+
const project = await ensureRemoteProject({ env: env.url, token, result, linkConfigDir: dir });
|
|
595
589
|
projectId = project.id;
|
|
596
590
|
}
|
|
597
591
|
const deployment = await uploadDeployment({
|
|
@@ -771,21 +765,7 @@ export function activeDeploymentMatchesSkipPin({ active, sourceHash, peerVersion
|
|
|
771
765
|
return (active.cliVersion === peerVersions.cliVersion &&
|
|
772
766
|
active.atlasUiVersion === peerVersions.atlasUiVersion);
|
|
773
767
|
}
|
|
774
|
-
|
|
775
|
-
* Deploys never change a live project's visibility — the Studio sharing UI /
|
|
776
|
-
* PATCH own that. The manifest's targets.<env>.visibility is honored at
|
|
777
|
-
* project CREATION only; on drift, say so instead of silently ignoring the
|
|
778
|
-
* field (which is how it read as decorative for a year).
|
|
779
|
-
*/
|
|
780
|
-
function warnOnVisibilityDrift({ project, declared, envName }) {
|
|
781
|
-
if (!declared || !envName || !project.visibility || declared === project.visibility)
|
|
782
|
-
return;
|
|
783
|
-
console.warn(`[seq-studio] note: artifact.bundle.yml targets.${envName}.visibility is "${declared}" but the project is ` +
|
|
784
|
-
`"${project.visibility}" on this environment — deploys never change visibility. Update it in Studio sharing ` +
|
|
785
|
-
`(or PATCH /api/artifact-studio/projects/${project.id}) if the manifest is what you intend.`);
|
|
786
|
-
}
|
|
787
|
-
async function ensureRemoteProject({ env, token, result, linkConfigDir, allowCreate = true, explicitProjectId = null, envName = null }) {
|
|
788
|
-
const declaredVisibility = envName ? result.manifest.targets?.[envName]?.visibility : undefined;
|
|
768
|
+
async function ensureRemoteProject({ env, token, result, linkConfigDir, allowCreate = true, explicitProjectId = null }) {
|
|
789
769
|
if (explicitProjectId) {
|
|
790
770
|
const data = await getJsonOr404({ baseUrl: env, token, path: `/api/artifact-studio/projects/${explicitProjectId}` });
|
|
791
771
|
if (!data)
|
|
@@ -794,14 +774,12 @@ async function ensureRemoteProject({ env, token, result, linkConfigDir, allowCre
|
|
|
794
774
|
throw new Error(`--project ${explicitProjectId} resolves to slug "${data.project.slug}" but this source's ` +
|
|
795
775
|
`artifact.bundle.yml declares project_id "${result.manifest.artifact.project_id}" — refusing to deploy over a different project`);
|
|
796
776
|
}
|
|
797
|
-
warnOnVisibilityDrift({ project: data.project, declared: declaredVisibility, envName });
|
|
798
777
|
return data.project;
|
|
799
778
|
}
|
|
800
779
|
const config = linkConfigDir ? await readLocalConfig(linkConfigDir) : {};
|
|
801
780
|
if (config.projectId) {
|
|
802
781
|
const data = await getJsonOr404({ baseUrl: env, token, path: `/api/artifact-studio/projects/${config.projectId}` });
|
|
803
782
|
if (data) {
|
|
804
|
-
warnOnVisibilityDrift({ project: data.project, declared: declaredVisibility, envName });
|
|
805
783
|
return data.project;
|
|
806
784
|
}
|
|
807
785
|
console.warn(`[seq-studio] cached projectId ${config.projectId} not found on ${env} — relinking by slug`);
|
|
@@ -810,7 +788,6 @@ async function ensureRemoteProject({ env, token, result, linkConfigDir, allowCre
|
|
|
810
788
|
if (existing) {
|
|
811
789
|
if (linkConfigDir)
|
|
812
790
|
await writeLocalConfig({ ...config, projectId: existing.id, projectSlug: existing.slug }, linkConfigDir);
|
|
813
|
-
warnOnVisibilityDrift({ project: existing, declared: declaredVisibility, envName });
|
|
814
791
|
return existing;
|
|
815
792
|
}
|
|
816
793
|
if (!allowCreate) {
|
|
@@ -826,7 +803,6 @@ async function ensureRemoteProject({ env, token, result, linkConfigDir, allowCre
|
|
|
826
803
|
title: result.manifest.artifact.title,
|
|
827
804
|
slug: result.manifest.artifact.project_id,
|
|
828
805
|
description: result.manifest.artifact.description ?? null,
|
|
829
|
-
...(declaredVisibility ? { visibility: declaredVisibility } : {}),
|
|
830
806
|
},
|
|
831
807
|
});
|
|
832
808
|
if (linkConfigDir)
|
package/dist/manifest.d.ts
CHANGED
|
@@ -29,6 +29,11 @@ export declare const artifactStudioManifestSchema: z.ZodObject<{
|
|
|
29
29
|
react: z.ZodDefault<z.ZodUnion<readonly [z.ZodLiteral<18>, z.ZodLiteral<19>]>>;
|
|
30
30
|
}, z.core.$strip>>;
|
|
31
31
|
capabilities: z.ZodDefault<z.ZodObject<{
|
|
32
|
+
storage: z.ZodOptional<z.ZodRecord<z.ZodUUID, z.ZodEnum<{
|
|
33
|
+
viewer: "viewer";
|
|
34
|
+
editor: "editor";
|
|
35
|
+
admin: "admin";
|
|
36
|
+
}>>>;
|
|
32
37
|
data: z.ZodDefault<z.ZodObject<{
|
|
33
38
|
read: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
34
39
|
write: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
@@ -57,12 +62,7 @@ export declare const artifactStudioManifestSchema: z.ZodObject<{
|
|
|
57
62
|
}, z.core.$strip>>;
|
|
58
63
|
targets: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
59
64
|
url: z.ZodString;
|
|
60
|
-
|
|
61
|
-
published: "published";
|
|
62
|
-
private: "private";
|
|
63
|
-
shared: "shared";
|
|
64
|
-
}>>;
|
|
65
|
-
}, z.core.$strip>>>;
|
|
65
|
+
}, z.core.$strict>>>;
|
|
66
66
|
sidebarBadge: z.ZodOptional<z.ZodObject<{
|
|
67
67
|
endpoint: z.ZodString;
|
|
68
68
|
tone: z.ZodOptional<z.ZodEnum<{
|
package/dist/manifest.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { storageScopesSchema } from '@sequenceholdings/storage';
|
|
1
2
|
import { z } from 'zod';
|
|
2
3
|
import { capabilityPinsSchema, customRolesBlockSchema, validateCapabilityPins, } from './manifest-custom-roles.js';
|
|
3
4
|
import { normalizeArtifactPath } from './paths.js';
|
|
@@ -56,6 +57,7 @@ export const artifactStudioManifestSchema = z.object({
|
|
|
56
57
|
react: z.union([z.literal(18), z.literal(19)]).default(18),
|
|
57
58
|
}).default({ sdk: 'sequence', react: 18 }),
|
|
58
59
|
capabilities: z.object({
|
|
60
|
+
storage: storageScopesSchema.optional(),
|
|
59
61
|
data: z.object({
|
|
60
62
|
read: z.array(z.string().min(1)).default([]),
|
|
61
63
|
write: z.array(z.string().min(1)).default([]),
|
|
@@ -96,8 +98,7 @@ export const artifactStudioManifestSchema = z.object({
|
|
|
96
98
|
}),
|
|
97
99
|
targets: z.record(z.string().min(1), z.object({
|
|
98
100
|
url: z.string().url(),
|
|
99
|
-
|
|
100
|
-
})).default({}),
|
|
101
|
+
}).strict()).default({}),
|
|
101
102
|
sidebarBadge: sidebarBadgeSchema.optional(),
|
|
102
103
|
}).superRefine((manifest, ctx) => {
|
|
103
104
|
for (const message of validateCapabilityPins({
|
package/dist/sdk.d.ts
CHANGED
|
@@ -1,4 +1,22 @@
|
|
|
1
|
+
import { type StartStorageUpload, type StorageUploadSession } from '@sequenceholdings/storage';
|
|
2
|
+
export interface SequenceStorageOptions {
|
|
3
|
+
signal?: AbortSignal;
|
|
4
|
+
timeoutMs?: number;
|
|
5
|
+
onProgress?: (progress: {
|
|
6
|
+
uploadId: string;
|
|
7
|
+
fileId: string;
|
|
8
|
+
uploadedBytes: number;
|
|
9
|
+
}) => void;
|
|
10
|
+
}
|
|
11
|
+
export interface SequenceStorage {
|
|
12
|
+
upload(workspaceId: string, input: StartStorageUpload, file: Blob, options?: SequenceStorageOptions): Promise<StorageUploadSession>;
|
|
13
|
+
resume(workspaceId: string, uploadId: string, file: Blob, options?: SequenceStorageOptions): Promise<StorageUploadSession>;
|
|
14
|
+
}
|
|
1
15
|
export interface SequenceApiFetchOptions {
|
|
16
|
+
traceAction?: {
|
|
17
|
+
id: string;
|
|
18
|
+
key: string;
|
|
19
|
+
};
|
|
2
20
|
method?: string;
|
|
3
21
|
body?: unknown;
|
|
4
22
|
headers?: Record<string, string>;
|
|
@@ -7,6 +25,8 @@ export interface SequenceApiFetchResponse<TBody = unknown> {
|
|
|
7
25
|
ok: boolean;
|
|
8
26
|
status: number;
|
|
9
27
|
body: TBody;
|
|
28
|
+
/** Allowlisted download headers for binary responses; keys are lowercase. */
|
|
29
|
+
headers?: Record<string, string>;
|
|
10
30
|
}
|
|
11
31
|
export interface SequenceApiUploadOptions {
|
|
12
32
|
timeoutMs?: number;
|
|
@@ -28,7 +48,10 @@ export interface SequenceApi {
|
|
|
28
48
|
delete<TBody = unknown>(path: string, body?: unknown): Promise<TBody>;
|
|
29
49
|
}
|
|
30
50
|
export interface SequenceFunctions {
|
|
31
|
-
invoke<TOutput = unknown>(id: string, params?: Record<string, unknown
|
|
51
|
+
invoke<TOutput = unknown>(id: string, params?: Record<string, unknown>, traceAction?: {
|
|
52
|
+
id: string;
|
|
53
|
+
key: string;
|
|
54
|
+
}): Promise<TOutput>;
|
|
32
55
|
}
|
|
33
56
|
export interface SequenceUser {
|
|
34
57
|
current(): Promise<{
|
|
@@ -109,6 +132,7 @@ export interface SequenceHost {
|
|
|
109
132
|
}): void;
|
|
110
133
|
}
|
|
111
134
|
export interface SequenceArtifactSdk {
|
|
135
|
+
storage: SequenceStorage;
|
|
112
136
|
api: SequenceApi;
|
|
113
137
|
ai: {
|
|
114
138
|
fetch(path: string, options?: SequenceApiFetchOptions): Promise<Response>;
|
|
@@ -128,3 +152,398 @@ declare global {
|
|
|
128
152
|
}
|
|
129
153
|
export declare const seq: SequenceArtifactSdk;
|
|
130
154
|
export default seq;
|
|
155
|
+
/** Typed metadata API plus direct browser-host transfers for sandboxed Artifacts. */
|
|
156
|
+
export declare function createArtifactStorageClient(sdk?: SequenceArtifactSdk): {
|
|
157
|
+
workspace(id: string): {
|
|
158
|
+
uploads: {
|
|
159
|
+
put: (input: StartStorageUpload, file: Blob, options?: SequenceStorageOptions) => Promise<{
|
|
160
|
+
id: string;
|
|
161
|
+
fileId: string;
|
|
162
|
+
uploadUrl: string | null;
|
|
163
|
+
expiresAt: string;
|
|
164
|
+
state: "aborted" | "pending" | "complete";
|
|
165
|
+
versionId: string | null;
|
|
166
|
+
maxBytes: number;
|
|
167
|
+
}>;
|
|
168
|
+
resume: (uploadId: string, file: Blob, options?: SequenceStorageOptions) => Promise<{
|
|
169
|
+
id: string;
|
|
170
|
+
fileId: string;
|
|
171
|
+
uploadUrl: string | null;
|
|
172
|
+
expiresAt: string;
|
|
173
|
+
state: "aborted" | "pending" | "complete";
|
|
174
|
+
versionId: string | null;
|
|
175
|
+
maxBytes: number;
|
|
176
|
+
}>;
|
|
177
|
+
start: (input: StartStorageUpload) => Promise<{
|
|
178
|
+
id: string;
|
|
179
|
+
fileId: string;
|
|
180
|
+
uploadUrl: string | null;
|
|
181
|
+
expiresAt: string;
|
|
182
|
+
state: "pending" | "complete" | "aborted";
|
|
183
|
+
versionId: string | null;
|
|
184
|
+
maxBytes: number;
|
|
185
|
+
}>;
|
|
186
|
+
get: (id: string) => Promise<{
|
|
187
|
+
id: string;
|
|
188
|
+
fileId: string;
|
|
189
|
+
uploadUrl: string | null;
|
|
190
|
+
expiresAt: string;
|
|
191
|
+
state: "pending" | "complete" | "aborted";
|
|
192
|
+
versionId: string | null;
|
|
193
|
+
maxBytes: number;
|
|
194
|
+
}>;
|
|
195
|
+
complete: (id: string, input?: import("@sequenceholdings/storage").CompleteStorageUpload) => Promise<{
|
|
196
|
+
id: string;
|
|
197
|
+
fileId: string;
|
|
198
|
+
uploadUrl: string | null;
|
|
199
|
+
expiresAt: string;
|
|
200
|
+
state: "pending" | "complete" | "aborted";
|
|
201
|
+
versionId: string | null;
|
|
202
|
+
maxBytes: number;
|
|
203
|
+
}>;
|
|
204
|
+
abort: (id: string) => Promise<{
|
|
205
|
+
id: string;
|
|
206
|
+
fileId: string;
|
|
207
|
+
uploadUrl: string | null;
|
|
208
|
+
expiresAt: string;
|
|
209
|
+
state: "pending" | "complete" | "aborted";
|
|
210
|
+
versionId: string | null;
|
|
211
|
+
maxBytes: number;
|
|
212
|
+
}>;
|
|
213
|
+
};
|
|
214
|
+
get: () => Promise<{
|
|
215
|
+
id: string;
|
|
216
|
+
name: string;
|
|
217
|
+
description: string;
|
|
218
|
+
environment: string;
|
|
219
|
+
state: "active" | "trashed" | "purging";
|
|
220
|
+
revision: number;
|
|
221
|
+
createdAt: string;
|
|
222
|
+
role: "viewer" | "editor" | "admin" | "owner";
|
|
223
|
+
quotaBytes: number;
|
|
224
|
+
usedBytes: number;
|
|
225
|
+
reservedBytes: number;
|
|
226
|
+
}>;
|
|
227
|
+
delete: (expectedRevision: number) => Promise<{
|
|
228
|
+
id: string;
|
|
229
|
+
workspaceId: string;
|
|
230
|
+
targetId: string | null;
|
|
231
|
+
kind: "purge" | "delete_version";
|
|
232
|
+
status: "queued" | "running" | "succeeded" | "failed";
|
|
233
|
+
completed: number;
|
|
234
|
+
total: number;
|
|
235
|
+
error: string | null;
|
|
236
|
+
createdAt: string;
|
|
237
|
+
updatedAt: string;
|
|
238
|
+
canRetry: boolean;
|
|
239
|
+
}>;
|
|
240
|
+
list: (options?: import("@sequenceholdings/storage").StorageListOptions & {
|
|
241
|
+
parentId?: string | null;
|
|
242
|
+
trash?: boolean;
|
|
243
|
+
}) => Promise<{
|
|
244
|
+
items: {
|
|
245
|
+
id: string;
|
|
246
|
+
workspaceId: string;
|
|
247
|
+
parentId: string | null;
|
|
248
|
+
name: string;
|
|
249
|
+
kind: "file" | "folder";
|
|
250
|
+
state: "active" | "trashed" | "purging";
|
|
251
|
+
restricted: boolean;
|
|
252
|
+
revision: number;
|
|
253
|
+
currentVersionId: string | null;
|
|
254
|
+
metadata: Record<string, string>;
|
|
255
|
+
createdAt: string;
|
|
256
|
+
updatedAt: string;
|
|
257
|
+
role: "viewer" | "editor" | "admin";
|
|
258
|
+
size: number | null;
|
|
259
|
+
contentType: string | null;
|
|
260
|
+
}[];
|
|
261
|
+
nextCursor: string | null;
|
|
262
|
+
}>;
|
|
263
|
+
getNode: (id: string) => Promise<{
|
|
264
|
+
id: string;
|
|
265
|
+
workspaceId: string;
|
|
266
|
+
parentId: string | null;
|
|
267
|
+
name: string;
|
|
268
|
+
kind: "file" | "folder";
|
|
269
|
+
state: "active" | "trashed" | "purging";
|
|
270
|
+
restricted: boolean;
|
|
271
|
+
revision: number;
|
|
272
|
+
currentVersionId: string | null;
|
|
273
|
+
metadata: Record<string, string>;
|
|
274
|
+
createdAt: string;
|
|
275
|
+
updatedAt: string;
|
|
276
|
+
role: "viewer" | "editor" | "admin";
|
|
277
|
+
size: number | null;
|
|
278
|
+
contentType: string | null;
|
|
279
|
+
}>;
|
|
280
|
+
createFolder: (name: string, parentId?: string | null, { idempotencyKey, }?: {
|
|
281
|
+
idempotencyKey?: string;
|
|
282
|
+
}) => Promise<{
|
|
283
|
+
id: string;
|
|
284
|
+
workspaceId: string;
|
|
285
|
+
parentId: string | null;
|
|
286
|
+
name: string;
|
|
287
|
+
kind: "file" | "folder";
|
|
288
|
+
state: "active" | "trashed" | "purging";
|
|
289
|
+
restricted: boolean;
|
|
290
|
+
revision: number;
|
|
291
|
+
currentVersionId: string | null;
|
|
292
|
+
metadata: Record<string, string>;
|
|
293
|
+
createdAt: string;
|
|
294
|
+
updatedAt: string;
|
|
295
|
+
role: "viewer" | "editor" | "admin";
|
|
296
|
+
size: number | null;
|
|
297
|
+
contentType: string | null;
|
|
298
|
+
}>;
|
|
299
|
+
update: (id: string, input: import("zod").input<typeof import("@sequenceholdings/storage").updateNodeSchema>) => Promise<{
|
|
300
|
+
id: string;
|
|
301
|
+
workspaceId: string;
|
|
302
|
+
parentId: string | null;
|
|
303
|
+
name: string;
|
|
304
|
+
kind: "file" | "folder";
|
|
305
|
+
state: "active" | "trashed" | "purging";
|
|
306
|
+
restricted: boolean;
|
|
307
|
+
revision: number;
|
|
308
|
+
currentVersionId: string | null;
|
|
309
|
+
metadata: Record<string, string>;
|
|
310
|
+
createdAt: string;
|
|
311
|
+
updatedAt: string;
|
|
312
|
+
role: "viewer" | "editor" | "admin";
|
|
313
|
+
size: number | null;
|
|
314
|
+
contentType: string | null;
|
|
315
|
+
}>;
|
|
316
|
+
trash: (id: string, expectedRevision: number) => Promise<{
|
|
317
|
+
id: string;
|
|
318
|
+
workspaceId: string;
|
|
319
|
+
parentId: string | null;
|
|
320
|
+
name: string;
|
|
321
|
+
kind: "file" | "folder";
|
|
322
|
+
state: "active" | "trashed" | "purging";
|
|
323
|
+
restricted: boolean;
|
|
324
|
+
revision: number;
|
|
325
|
+
currentVersionId: string | null;
|
|
326
|
+
metadata: Record<string, string>;
|
|
327
|
+
createdAt: string;
|
|
328
|
+
updatedAt: string;
|
|
329
|
+
role: "viewer" | "editor" | "admin";
|
|
330
|
+
size: number | null;
|
|
331
|
+
contentType: string | null;
|
|
332
|
+
}>;
|
|
333
|
+
restore: (id: string, expectedRevision: number) => Promise<{
|
|
334
|
+
id: string;
|
|
335
|
+
workspaceId: string;
|
|
336
|
+
parentId: string | null;
|
|
337
|
+
name: string;
|
|
338
|
+
kind: "file" | "folder";
|
|
339
|
+
state: "active" | "trashed" | "purging";
|
|
340
|
+
restricted: boolean;
|
|
341
|
+
revision: number;
|
|
342
|
+
currentVersionId: string | null;
|
|
343
|
+
metadata: Record<string, string>;
|
|
344
|
+
createdAt: string;
|
|
345
|
+
updatedAt: string;
|
|
346
|
+
role: "viewer" | "editor" | "admin";
|
|
347
|
+
size: number | null;
|
|
348
|
+
contentType: string | null;
|
|
349
|
+
}>;
|
|
350
|
+
purge: (id: string, expectedRevision: number) => Promise<{
|
|
351
|
+
id: string;
|
|
352
|
+
workspaceId: string;
|
|
353
|
+
targetId: string | null;
|
|
354
|
+
kind: "purge" | "delete_version";
|
|
355
|
+
status: "queued" | "running" | "succeeded" | "failed";
|
|
356
|
+
completed: number;
|
|
357
|
+
total: number;
|
|
358
|
+
error: string | null;
|
|
359
|
+
createdAt: string;
|
|
360
|
+
updatedAt: string;
|
|
361
|
+
canRetry: boolean;
|
|
362
|
+
}>;
|
|
363
|
+
download: (id: string, options?: {
|
|
364
|
+
versionId?: string;
|
|
365
|
+
preview?: boolean;
|
|
366
|
+
}) => Promise<{
|
|
367
|
+
url: string;
|
|
368
|
+
expiresAt: string;
|
|
369
|
+
preview: boolean;
|
|
370
|
+
}>;
|
|
371
|
+
versions: (id: string, options?: {
|
|
372
|
+
before?: number;
|
|
373
|
+
limit?: number;
|
|
374
|
+
}) => Promise<{
|
|
375
|
+
items: {
|
|
376
|
+
id: string;
|
|
377
|
+
fileId: string;
|
|
378
|
+
revision: number;
|
|
379
|
+
size: number;
|
|
380
|
+
checksum: string;
|
|
381
|
+
contentType: string;
|
|
382
|
+
createdAt: string;
|
|
383
|
+
createdBy: string;
|
|
384
|
+
state: "active" | "purging";
|
|
385
|
+
}[];
|
|
386
|
+
nextRevision: number | null;
|
|
387
|
+
}>;
|
|
388
|
+
restoreVersion: (id: string, versionId: string, expectedRevision: number) => Promise<{
|
|
389
|
+
id: string;
|
|
390
|
+
fileId: string;
|
|
391
|
+
revision: number;
|
|
392
|
+
size: number;
|
|
393
|
+
checksum: string;
|
|
394
|
+
contentType: string;
|
|
395
|
+
createdAt: string;
|
|
396
|
+
createdBy: string;
|
|
397
|
+
state: "active" | "purging";
|
|
398
|
+
}>;
|
|
399
|
+
deleteVersion: (id: string, versionId: string, expectedRevision: number) => Promise<{
|
|
400
|
+
id: string;
|
|
401
|
+
workspaceId: string;
|
|
402
|
+
targetId: string | null;
|
|
403
|
+
kind: "purge" | "delete_version";
|
|
404
|
+
status: "queued" | "running" | "succeeded" | "failed";
|
|
405
|
+
completed: number;
|
|
406
|
+
total: number;
|
|
407
|
+
error: string | null;
|
|
408
|
+
createdAt: string;
|
|
409
|
+
updatedAt: string;
|
|
410
|
+
canRetry: boolean;
|
|
411
|
+
}>;
|
|
412
|
+
access: (id?: string) => Promise<{
|
|
413
|
+
revision: number;
|
|
414
|
+
restricted: boolean;
|
|
415
|
+
grants: {
|
|
416
|
+
type: "user" | "group" | "organization" | "service_account";
|
|
417
|
+
id: string;
|
|
418
|
+
role: "viewer" | "editor" | "admin";
|
|
419
|
+
}[];
|
|
420
|
+
owner: string | null;
|
|
421
|
+
inherited: boolean;
|
|
422
|
+
workspaceAdminOverride: true;
|
|
423
|
+
displayNames: Record<string, string>;
|
|
424
|
+
}>;
|
|
425
|
+
setAccess: (input: import("zod").input<typeof import("@sequenceholdings/storage").updateStorageAccessSchema>, id?: string) => Promise<{
|
|
426
|
+
revision: number;
|
|
427
|
+
restricted: boolean;
|
|
428
|
+
grants: {
|
|
429
|
+
type: "user" | "group" | "organization" | "service_account";
|
|
430
|
+
id: string;
|
|
431
|
+
role: "viewer" | "editor" | "admin";
|
|
432
|
+
}[];
|
|
433
|
+
owner: string | null;
|
|
434
|
+
inherited: boolean;
|
|
435
|
+
workspaceAdminOverride: true;
|
|
436
|
+
displayNames: Record<string, string>;
|
|
437
|
+
}>;
|
|
438
|
+
transferOwner: (principal: {
|
|
439
|
+
type: "user" | "service_account";
|
|
440
|
+
id: string;
|
|
441
|
+
}, expectedRevision: number) => Promise<{
|
|
442
|
+
revision: number;
|
|
443
|
+
restricted: boolean;
|
|
444
|
+
grants: {
|
|
445
|
+
type: "user" | "group" | "organization" | "service_account";
|
|
446
|
+
id: string;
|
|
447
|
+
role: "viewer" | "editor" | "admin";
|
|
448
|
+
}[];
|
|
449
|
+
owner: string | null;
|
|
450
|
+
inherited: boolean;
|
|
451
|
+
workspaceAdminOverride: true;
|
|
452
|
+
displayNames: Record<string, string>;
|
|
453
|
+
}>;
|
|
454
|
+
operations: (options?: {
|
|
455
|
+
before?: string;
|
|
456
|
+
limit?: number;
|
|
457
|
+
scope?: import("@sequenceholdings/storage").StorageOperationScope;
|
|
458
|
+
}) => Promise<{
|
|
459
|
+
items: {
|
|
460
|
+
id: string;
|
|
461
|
+
workspaceId: string;
|
|
462
|
+
targetId: string | null;
|
|
463
|
+
kind: "purge" | "delete_version";
|
|
464
|
+
status: "queued" | "running" | "succeeded" | "failed";
|
|
465
|
+
completed: number;
|
|
466
|
+
total: number;
|
|
467
|
+
error: string | null;
|
|
468
|
+
createdAt: string;
|
|
469
|
+
updatedAt: string;
|
|
470
|
+
canRetry: boolean;
|
|
471
|
+
}[];
|
|
472
|
+
nextCursor: string | null;
|
|
473
|
+
}>;
|
|
474
|
+
retryOperation: (id: string) => Promise<{
|
|
475
|
+
id: string;
|
|
476
|
+
workspaceId: string;
|
|
477
|
+
targetId: string | null;
|
|
478
|
+
kind: "purge" | "delete_version";
|
|
479
|
+
status: "queued" | "running" | "succeeded" | "failed";
|
|
480
|
+
completed: number;
|
|
481
|
+
total: number;
|
|
482
|
+
error: string | null;
|
|
483
|
+
createdAt: string;
|
|
484
|
+
updatedAt: string;
|
|
485
|
+
canRetry: boolean;
|
|
486
|
+
}>;
|
|
487
|
+
};
|
|
488
|
+
capabilities: () => Promise<{
|
|
489
|
+
canProvision: boolean;
|
|
490
|
+
uploadMethods: string[];
|
|
491
|
+
}>;
|
|
492
|
+
list: (options?: import("@sequenceholdings/storage").StorageListOptions) => Promise<{
|
|
493
|
+
items: {
|
|
494
|
+
id: string;
|
|
495
|
+
name: string;
|
|
496
|
+
description: string;
|
|
497
|
+
environment: string;
|
|
498
|
+
state: "active" | "trashed" | "purging";
|
|
499
|
+
revision: number;
|
|
500
|
+
createdAt: string;
|
|
501
|
+
role: "viewer" | "editor" | "admin" | "owner";
|
|
502
|
+
quotaBytes: number;
|
|
503
|
+
usedBytes: number;
|
|
504
|
+
reservedBytes: number;
|
|
505
|
+
}[];
|
|
506
|
+
nextCursor: string | null;
|
|
507
|
+
}>;
|
|
508
|
+
shared: (options?: import("@sequenceholdings/storage").StorageListOptions & {
|
|
509
|
+
trash?: boolean;
|
|
510
|
+
}) => Promise<{
|
|
511
|
+
items: {
|
|
512
|
+
id: string;
|
|
513
|
+
workspaceId: string;
|
|
514
|
+
parentId: string | null;
|
|
515
|
+
name: string;
|
|
516
|
+
kind: "file" | "folder";
|
|
517
|
+
state: "active" | "trashed" | "purging";
|
|
518
|
+
restricted: boolean;
|
|
519
|
+
revision: number;
|
|
520
|
+
currentVersionId: string | null;
|
|
521
|
+
metadata: Record<string, string>;
|
|
522
|
+
createdAt: string;
|
|
523
|
+
updatedAt: string;
|
|
524
|
+
role: "viewer" | "editor" | "admin";
|
|
525
|
+
size: number | null;
|
|
526
|
+
contentType: string | null;
|
|
527
|
+
}[];
|
|
528
|
+
nextCursor: string | null;
|
|
529
|
+
}>;
|
|
530
|
+
apply: (manifest: import("@sequenceholdings/storage").WorkspaceManifest) => Promise<{
|
|
531
|
+
id: string;
|
|
532
|
+
name: string;
|
|
533
|
+
description: string;
|
|
534
|
+
environment: string;
|
|
535
|
+
state: "active" | "trashed" | "purging";
|
|
536
|
+
revision: number;
|
|
537
|
+
createdAt: string;
|
|
538
|
+
role: "viewer" | "editor" | "admin" | "owner";
|
|
539
|
+
quotaBytes: number;
|
|
540
|
+
usedBytes: number;
|
|
541
|
+
reservedBytes: number;
|
|
542
|
+
}>;
|
|
543
|
+
};
|
|
544
|
+
/** Explicit scope keeps concurrent browser actions isolated. The key is a safe
|
|
545
|
+
* author label, never a payload or an authentication claim. */
|
|
546
|
+
export declare function withTraceAction<T>({ key, sdk }: {
|
|
547
|
+
key: string;
|
|
548
|
+
sdk?: SequenceArtifactSdk;
|
|
549
|
+
}, run: (client: Pick<SequenceArtifactSdk, 'api' | 'functions'>) => Promise<T>): Promise<T>;
|
package/dist/sdk.js
CHANGED
|
@@ -1,4 +1,39 @@
|
|
|
1
|
+
import { storageClient } from '@sequenceholdings/storage';
|
|
1
2
|
export const seq = typeof window !== 'undefined'
|
|
2
3
|
? window.seq
|
|
3
4
|
: undefined;
|
|
4
5
|
export default seq;
|
|
6
|
+
/** Typed metadata API plus direct browser-host transfers for sandboxed Artifacts. */
|
|
7
|
+
export function createArtifactStorageClient(sdk = seq) {
|
|
8
|
+
const client = storageClient({ async request(method, path, body) {
|
|
9
|
+
const response = await sdk.api.fetch(path, { method, body });
|
|
10
|
+
if (!response.ok)
|
|
11
|
+
throw new Error(`Storage request failed (${response.status})`);
|
|
12
|
+
return response.body;
|
|
13
|
+
} });
|
|
14
|
+
return { ...client, workspace(id) {
|
|
15
|
+
const workspace = client.workspace(id);
|
|
16
|
+
return { ...workspace, uploads: { ...workspace.uploads,
|
|
17
|
+
put: (input, file, options) => sdk.storage.upload(id, input, file, options),
|
|
18
|
+
resume: (uploadId, file, options) => sdk.storage.resume(id, uploadId, file, options),
|
|
19
|
+
} };
|
|
20
|
+
} };
|
|
21
|
+
}
|
|
22
|
+
/** Explicit scope keeps concurrent browser actions isolated. The key is a safe
|
|
23
|
+
* author label, never a payload or an authentication claim. */
|
|
24
|
+
export function withTraceAction({ key, sdk = seq }, run) {
|
|
25
|
+
if (!/^[a-zA-Z0-9_.:/-]{1,120}$/.test(key))
|
|
26
|
+
throw new Error('Use a bounded operation key for tracing');
|
|
27
|
+
const traceAction = { id: crypto.randomUUID(), key };
|
|
28
|
+
const fetch = (path, options) => sdk.api.fetch(path, { ...options, traceAction });
|
|
29
|
+
const request = async (method, path, body) => {
|
|
30
|
+
const response = await fetch(path, { method, body });
|
|
31
|
+
if (!response.ok)
|
|
32
|
+
throw new Error(`API request failed (${response.status})`);
|
|
33
|
+
return response.body;
|
|
34
|
+
};
|
|
35
|
+
return run({ api: { ...sdk.api, fetch,
|
|
36
|
+
get: (path) => request('GET', path), post: (path, body) => request('POST', path, body),
|
|
37
|
+
put: (path, body) => request('PUT', path, body), patch: (path, body) => request('PATCH', path, body), delete: (path, body) => request('DELETE', path, body),
|
|
38
|
+
}, functions: { invoke: (id, params) => sdk.functions.invoke(id, params, traceAction) } });
|
|
39
|
+
}
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"publishConfig": {
|
|
10
10
|
"access": "public"
|
|
11
11
|
},
|
|
12
|
-
"version": "0.2.
|
|
12
|
+
"version": "0.2.6",
|
|
13
13
|
"description": "SDK types and CLI library for building Artifact Studio apps. Driven via `seq-studio artifact <sub>` (@sequenceholdings/studio-cli), which runs the `./cli` runCli export.",
|
|
14
14
|
"type": "module",
|
|
15
15
|
"exports": {
|
|
@@ -85,7 +85,8 @@
|
|
|
85
85
|
"tw-animate-css": "^1.4.0",
|
|
86
86
|
"vite": "8.0.16",
|
|
87
87
|
"zod": "^4.1.13",
|
|
88
|
-
"@sequenceholdings/lattice-form-renderer": "0.1.3"
|
|
88
|
+
"@sequenceholdings/lattice-form-renderer": "0.1.3",
|
|
89
|
+
"@sequenceholdings/storage": "0.1.0"
|
|
89
90
|
},
|
|
90
91
|
"devDependencies": {
|
|
91
92
|
"@types/js-yaml": "^4.0.9",
|