@sanity/runtime-cli 17.15.0 → 17.16.0-app-functions.ge0a25ea
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 +19 -19
- package/dist/actions/blueprints/assets.d.ts +3 -3
- package/dist/actions/blueprints/assets.js +3 -3
- package/dist/actions/blueprints/blueprint.d.ts +20 -9
- package/dist/actions/blueprints/blueprint.js +25 -79
- package/dist/actions/blueprints/config.d.ts +1 -0
- package/dist/actions/blueprints/config.js +7 -1
- package/dist/actions/blueprints/pre-deploy.js +3 -3
- package/dist/actions/blueprints/process-assets.js +3 -2
- package/dist/actions/blueprints/stacks.d.ts +7 -2
- package/dist/actions/blueprints/stacks.js +10 -6
- package/dist/actions/blueprints/wait-for-operation.d.ts +1 -0
- package/dist/actions/blueprints/wait-for-operation.js +1 -1
- package/dist/baseCommands.js +3 -3
- package/dist/config.d.ts +1 -2
- package/dist/config.js +1 -2
- package/dist/cores/blueprints/deploy.d.ts +8 -0
- package/dist/cores/blueprints/deploy.js +12 -4
- package/dist/cores/blueprints/doctor.js +2 -2
- package/dist/cores/blueprints/init.js +11 -2
- package/dist/cores/functions/test.js +43 -9
- package/dist/functions/artifacts.d.ts +13 -0
- package/dist/functions/artifacts.js +26 -0
- package/dist/functions/build.d.ts +15 -0
- package/dist/functions/build.js +27 -0
- package/dist/functions/contractPlugin.d.ts +7 -0
- package/dist/functions/contractPlugin.js +32 -0
- package/dist/functions/contracts/schema.d.ts +22 -0
- package/dist/functions/contracts/schema.js +99 -0
- package/dist/functions/contracts/validation.d.ts +3 -0
- package/dist/functions/contracts/validation.js +66 -0
- package/dist/functions/contracts.d.ts +20 -0
- package/dist/functions/contracts.js +119 -0
- package/dist/functions/deploy.d.ts +15 -0
- package/dist/functions/deploy.js +60 -0
- package/dist/functions/stack.d.ts +18 -0
- package/dist/functions/stack.js +84 -0
- package/dist/server/app.js +4 -4
- package/dist/server/static/components/app.css +8 -1
- package/dist/utils/blueprints/group-resources.d.ts +2 -1
- package/dist/utils/blueprints/group-resources.js +3 -3
- package/dist/utils/blueprints/resolve-deployed-resources.d.ts +2 -2
- package/dist/utils/blueprints/resolve-deployed-resources.js +4 -4
- package/dist/utils/bundles/prepare-asset.d.ts +3 -3
- package/dist/utils/bundles/prepare-asset.js +8 -7
- package/dist/utils/functions/child-process-wrapper.js +1 -1
- package/dist/utils/functions/child-runtime/durable.js +99 -22
- package/dist/utils/functions/child-runtime/invoke.d.ts +1 -0
- package/dist/utils/functions/child-runtime/invoke.js +26 -9
- package/dist/utils/functions/child-runtime/step-history.d.ts +15 -0
- package/dist/utils/functions/child-runtime/step-history.js +52 -0
- package/dist/utils/functions/extract-durable-config.js +1 -4
- package/dist/utils/functions/invoke/local.js +27 -11
- package/dist/utils/functions/invoke/nested.js +2 -1
- package/dist/utils/functions/invoke/resolve-invoke-type.d.ts +2 -2
- package/dist/utils/functions/invoke/resolve-invoke-type.js +2 -2
- package/dist/utils/functions/transpile/function.d.ts +7 -1
- package/dist/utils/functions/transpile/function.js +15 -9
- package/dist/utils/types.d.ts +24 -13
- package/dist/utils/types.js +1 -1
- package/oclif.manifest.json +1 -1
- package/package.json +24 -3
- package/dist/actions/blueprints/resolve.d.ts +0 -51
- package/dist/actions/blueprints/resolve.js +0 -52
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import ts from 'typescript-compiler';
|
|
4
|
+
import { findFunctionEntryPoint } from '../utils/functions/find-entry-point.js';
|
|
5
|
+
import { contractType, schemaFromType } from './contracts/schema.js';
|
|
6
|
+
import { jsonGuard, validatorSource } from './contracts/validation.js';
|
|
7
|
+
/** Discover input and output types without loading or executing function code. */
|
|
8
|
+
export async function generateFunctionContracts(options) {
|
|
9
|
+
const entries = await Promise.all(options.functions.map(async (fn) => ({
|
|
10
|
+
...fn,
|
|
11
|
+
entryPoint: await findFunctionEntryPoint(path.resolve(options.workDir, fn.src), fn.name),
|
|
12
|
+
})));
|
|
13
|
+
const config = readProjectConfig(options.workDir);
|
|
14
|
+
const program = ts.createProgram(entries.map((fn) => fn.entryPoint), {
|
|
15
|
+
target: ts.ScriptTarget.ESNext,
|
|
16
|
+
module: ts.ModuleKind.ESNext,
|
|
17
|
+
moduleResolution: ts.ModuleResolutionKind.Bundler,
|
|
18
|
+
...config?.options,
|
|
19
|
+
allowJs: true,
|
|
20
|
+
checkJs: true,
|
|
21
|
+
noEmit: true,
|
|
22
|
+
strict: true,
|
|
23
|
+
strictNullChecks: true,
|
|
24
|
+
skipLibCheck: true,
|
|
25
|
+
composite: false,
|
|
26
|
+
incremental: false,
|
|
27
|
+
declaration: false,
|
|
28
|
+
declarationMap: false,
|
|
29
|
+
tsBuildInfoFile: undefined,
|
|
30
|
+
});
|
|
31
|
+
assertDiagnostics(ts.getPreEmitDiagnostics(program), options.workDir);
|
|
32
|
+
const checker = program.getTypeChecker();
|
|
33
|
+
return entries.map((fn) => readContract(checker, program, fn));
|
|
34
|
+
}
|
|
35
|
+
/** Check app consumers after regenerating their function registry. */
|
|
36
|
+
export function checkFunctionTypes(workDir) {
|
|
37
|
+
const config = readProjectConfig(workDir);
|
|
38
|
+
if (!config)
|
|
39
|
+
return;
|
|
40
|
+
const program = ts.createProgram({
|
|
41
|
+
rootNames: config.fileNames,
|
|
42
|
+
options: { ...config.options, noEmit: true, incremental: false, tsBuildInfoFile: undefined },
|
|
43
|
+
projectReferences: config.projectReferences,
|
|
44
|
+
});
|
|
45
|
+
assertDiagnostics(ts.getPreEmitDiagnostics(program), workDir);
|
|
46
|
+
}
|
|
47
|
+
function readProjectConfig(workDir) {
|
|
48
|
+
const configPath = ts.findConfigFile(workDir, ts.sys.fileExists);
|
|
49
|
+
if (!configPath)
|
|
50
|
+
return undefined;
|
|
51
|
+
const config = ts.readConfigFile(configPath, ts.sys.readFile);
|
|
52
|
+
if (config.error)
|
|
53
|
+
assertDiagnostics([config.error], workDir);
|
|
54
|
+
const parsed = ts.parseJsonConfigFileContent(config.config, ts.sys, path.dirname(configPath));
|
|
55
|
+
assertDiagnostics(parsed.errors, workDir);
|
|
56
|
+
return parsed;
|
|
57
|
+
}
|
|
58
|
+
function assertDiagnostics(diagnostics, workDir) {
|
|
59
|
+
const errors = diagnostics.filter((diagnostic) => diagnostic.category === ts.DiagnosticCategory.Error);
|
|
60
|
+
if (errors.length)
|
|
61
|
+
throw new Error(ts.formatDiagnostics(errors, {
|
|
62
|
+
getCanonicalFileName: (file) => file,
|
|
63
|
+
getCurrentDirectory: () => workDir,
|
|
64
|
+
getNewLine: () => '\n',
|
|
65
|
+
}));
|
|
66
|
+
}
|
|
67
|
+
function readContract(checker, program, fn) {
|
|
68
|
+
const source = program.getSourceFile(fn.entryPoint);
|
|
69
|
+
const module = source && checker.getSymbolAtLocation(source);
|
|
70
|
+
const exported = module && checker.getExportsOfModule(module).find((item) => item.getName() === 'handler');
|
|
71
|
+
if (!source || !exported)
|
|
72
|
+
throw new Error(`Function "${fn.name}" must export handler.`);
|
|
73
|
+
const signatures = checker.getTypeOfSymbolAtLocation(exported, source).getCallSignatures();
|
|
74
|
+
if (signatures.length !== 1)
|
|
75
|
+
throw new Error(`Function "${fn.name}" must have one handler signature.`);
|
|
76
|
+
const signature = signatures[0];
|
|
77
|
+
if (signature.parameters.length > 1)
|
|
78
|
+
throw new Error(`Function "${fn.name}" must accept a single FunctionArgs envelope.`);
|
|
79
|
+
let input = { kind: 'void' };
|
|
80
|
+
const parameter = signature.parameters[0];
|
|
81
|
+
if (parameter) {
|
|
82
|
+
const args = checker.getTypeOfSymbolAtLocation(parameter, source);
|
|
83
|
+
const event = args.getProperty('event');
|
|
84
|
+
const eventType = event && checker.getTypeOfSymbolAtLocation(event, source);
|
|
85
|
+
const data = eventType?.getProperty('data');
|
|
86
|
+
if (!data)
|
|
87
|
+
throw new Error(`Function "${fn.name}" must annotate its input with FunctionArgs<Input>.`);
|
|
88
|
+
input = schemaFromType(checker, checker.getTypeOfSymbolAtLocation(data, source), `${fn.name} input`, true);
|
|
89
|
+
}
|
|
90
|
+
const isObjectInput = (schema) => schema.kind === 'object' || (schema.kind === 'union' && schema.variants.every(isObjectInput));
|
|
91
|
+
if (input.kind !== 'void' && !isObjectInput(input))
|
|
92
|
+
throw new Error(`Function "${fn.name}" input must be a JSON object or omitted. Put scalar values inside an input object.`);
|
|
93
|
+
const returnType = checker.getReturnTypeOfSignature(signature);
|
|
94
|
+
const output = schemaFromType(checker, checker.getAwaitedType(returnType) ?? returnType, `${fn.name} output`, true);
|
|
95
|
+
return { ...fn, input, output };
|
|
96
|
+
}
|
|
97
|
+
const generatedHeader = '// Generated by Sanity CLI. Do not edit.\n';
|
|
98
|
+
/** Emit a standalone declaration registry and browser-safe validators. */
|
|
99
|
+
export async function writeFunctionContracts(workDir, contracts) {
|
|
100
|
+
const declarations = `${generatedHeader}import '@sanity/sdk'\n\ndeclare module '@sanity/sdk' {\n interface AppFunctionRegistry {\n${contracts.map((fn) => ` ${JSON.stringify(fn.name)}: {input: ${contractType(fn.input)}; output: ${contractType(fn.output)}}`).join('\n')}\n }\n}\n`;
|
|
101
|
+
const client = `${generatedHeader}${jsonGuard}\nexport const functionContracts = Object.fromEntries([\n${contracts.map((fn) => `[${JSON.stringify(fn.name)}, {hasInput: ${fn.input.kind !== 'void'}, validateInput: ${validatorSource(fn.input)}, validateOutput: ${validatorSource(fn.output)}}]`).join(',\n')}\n])\n`;
|
|
102
|
+
await writeGenerated(path.join(workDir, 'sanity.functions.d.ts'), declarations);
|
|
103
|
+
await writeGenerated(path.join(workDir, '.sanity', 'function-contracts.js'), client);
|
|
104
|
+
}
|
|
105
|
+
async function writeGenerated(file, content) {
|
|
106
|
+
try {
|
|
107
|
+
const previous = await readFile(file, 'utf8');
|
|
108
|
+
if (!previous.startsWith(generatedHeader))
|
|
109
|
+
throw new Error(`Refusing to overwrite non-generated file ${file}. Move it before generating function contracts.`);
|
|
110
|
+
if (previous === content)
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
if (!(error instanceof Error && 'code' in error && error.code === 'ENOENT'))
|
|
115
|
+
throw error;
|
|
116
|
+
}
|
|
117
|
+
await mkdir(path.dirname(file), { recursive: true });
|
|
118
|
+
await writeFile(file, content);
|
|
119
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { CoreResult } from '../cores/index.js';
|
|
2
|
+
/** Deploy already-built PubSub functions to an app-owned organization stack. */
|
|
3
|
+
export declare function deployFunctions(options: {
|
|
4
|
+
appId: string;
|
|
5
|
+
organizationId: string;
|
|
6
|
+
stackId: string;
|
|
7
|
+
workDir: string;
|
|
8
|
+
functions: {
|
|
9
|
+
name: string;
|
|
10
|
+
src: string;
|
|
11
|
+
}[];
|
|
12
|
+
token: string;
|
|
13
|
+
fetchFn: typeof fetch;
|
|
14
|
+
log: (message: string) => void;
|
|
15
|
+
}): Promise<CoreResult>;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { getStack } from '../actions/blueprints/stacks.js';
|
|
2
|
+
import { deployResourcesCore } from '../cores/blueprints/deploy.js';
|
|
3
|
+
import { Logger } from '../utils/logger.js';
|
|
4
|
+
import { readFunctionBuild } from './artifacts.js';
|
|
5
|
+
import { assertAppFunctionStack } from './stack.js';
|
|
6
|
+
/** Deploy already-built PubSub functions to an app-owned organization stack. */
|
|
7
|
+
export async function deployFunctions(options) {
|
|
8
|
+
const { organizationId, stackId, workDir, fetchFn } = options;
|
|
9
|
+
const auth = {
|
|
10
|
+
token: options.token,
|
|
11
|
+
scopeType: 'organization',
|
|
12
|
+
scopeId: organizationId,
|
|
13
|
+
};
|
|
14
|
+
const result = await getStack({ stackId, auth, fetchFn });
|
|
15
|
+
if (!result.ok)
|
|
16
|
+
return { success: false, error: result.error || 'Could not read app function stack' };
|
|
17
|
+
const stack = result.stack;
|
|
18
|
+
try {
|
|
19
|
+
assertAppFunctionStack(stack, options);
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
return { success: false, error: error instanceof Error ? error.message : String(error) };
|
|
23
|
+
}
|
|
24
|
+
if (stack.resources.some((resource) => resource.type !== 'sanity.function.pubsub')) {
|
|
25
|
+
return {
|
|
26
|
+
success: false,
|
|
27
|
+
error: 'The app function stack contains other resource types. Use a dedicated app function stack.',
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
const resources = [];
|
|
31
|
+
for (const fn of options.functions) {
|
|
32
|
+
let src;
|
|
33
|
+
try {
|
|
34
|
+
src = (await readFunctionBuild({ ...fn, workDir })).src;
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
return { success: false, error: error instanceof Error ? error.message : String(error) };
|
|
38
|
+
}
|
|
39
|
+
resources.push({
|
|
40
|
+
name: fn.name,
|
|
41
|
+
type: 'sanity.function.pubsub',
|
|
42
|
+
src,
|
|
43
|
+
transpile: false,
|
|
44
|
+
autoResolveDeps: false,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
return deployResourcesCore({
|
|
48
|
+
auth,
|
|
49
|
+
stackId,
|
|
50
|
+
scopeType: 'organization',
|
|
51
|
+
scopeId: organizationId,
|
|
52
|
+
deployedStack: stack,
|
|
53
|
+
resources,
|
|
54
|
+
workDir,
|
|
55
|
+
fetchFn,
|
|
56
|
+
bin: 'sanity',
|
|
57
|
+
flags: {},
|
|
58
|
+
log: Logger(options.log, { json: true }),
|
|
59
|
+
});
|
|
60
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Stack, StackSummary } from '../utils/types.js';
|
|
2
|
+
interface AppFunctionIdentity {
|
|
3
|
+
appId: string;
|
|
4
|
+
organizationId: string;
|
|
5
|
+
}
|
|
6
|
+
/** Shared ownership check for provisioning and deploying an app's function stack. */
|
|
7
|
+
export declare function assertAppFunctionStack(stack: Stack | StackSummary, { appId, organizationId }: AppFunctionIdentity): void;
|
|
8
|
+
/** Resolve or create a dedicated app stack using the Blueprint stack operations. */
|
|
9
|
+
export declare function ensureFunctionStack(options: AppFunctionIdentity & {
|
|
10
|
+
token: string;
|
|
11
|
+
fetchFn: typeof fetch;
|
|
12
|
+
stackId?: string;
|
|
13
|
+
dryRun?: boolean;
|
|
14
|
+
}): Promise<{
|
|
15
|
+
created: boolean;
|
|
16
|
+
id: string;
|
|
17
|
+
} | null>;
|
|
18
|
+
export { BLUEPRINTS_API_VERSION } from '../config.js';
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { createStack, getStack, listStacks } from '../actions/blueprints/stacks.js';
|
|
2
|
+
import { Logger } from '../utils/logger.js';
|
|
3
|
+
/** Shared ownership check for provisioning and deploying an app's function stack. */
|
|
4
|
+
export function assertAppFunctionStack(stack, { appId, organizationId }) {
|
|
5
|
+
if (!stack ||
|
|
6
|
+
typeof stack.id !== 'string' ||
|
|
7
|
+
!stack.id ||
|
|
8
|
+
stack.name !== appFunctionStackName(appId) ||
|
|
9
|
+
stack.scopeType !== 'organization' ||
|
|
10
|
+
stack.scopeId !== organizationId) {
|
|
11
|
+
throw new Error(`The function stack does not belong to app "${appId}" in organization "${organizationId}". Check .sanity/app-functions.json.`);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/** Resolve or create a dedicated app stack using the Blueprint stack operations. */
|
|
15
|
+
export async function ensureFunctionStack(options) {
|
|
16
|
+
const { appId, organizationId, token, fetchFn, stackId, dryRun = false } = options;
|
|
17
|
+
const auth = { token, scopeType: 'organization', scopeId: organizationId };
|
|
18
|
+
const name = appFunctionStackName(appId);
|
|
19
|
+
const logger = Logger(() => { }, { json: true });
|
|
20
|
+
const resolved = (stack, created) => {
|
|
21
|
+
assertAppFunctionStack(stack, options);
|
|
22
|
+
return { created, id: stack.id };
|
|
23
|
+
};
|
|
24
|
+
if (stackId) {
|
|
25
|
+
const result = await getStack({ stackId, auth, fetchFn });
|
|
26
|
+
if (result.ok)
|
|
27
|
+
return resolved(result.stack, false);
|
|
28
|
+
if (result.response?.status !== 404)
|
|
29
|
+
throw stackRequestError(result);
|
|
30
|
+
}
|
|
31
|
+
async function findStack() {
|
|
32
|
+
let cursor;
|
|
33
|
+
const cursors = new Set();
|
|
34
|
+
do {
|
|
35
|
+
const result = await listStacks(auth, logger, { fetchFn, cursor, limit: 500 });
|
|
36
|
+
if (!result.ok)
|
|
37
|
+
throw stackRequestError(result);
|
|
38
|
+
if (!Array.isArray(result.stacks))
|
|
39
|
+
throw new Error('Invalid Blueprint stack list response.');
|
|
40
|
+
const stack = result.stacks.find((item) => item?.name === name);
|
|
41
|
+
if (stack)
|
|
42
|
+
return stack;
|
|
43
|
+
if (result.response?.headers.get('x-has-more') !== 'true')
|
|
44
|
+
return undefined;
|
|
45
|
+
cursor = result.response.headers.get('x-cursor') ?? undefined;
|
|
46
|
+
if (!cursor || cursors.has(cursor))
|
|
47
|
+
throw new Error('Cannot read the next page of Blueprint stacks. Try again.');
|
|
48
|
+
cursors.add(cursor);
|
|
49
|
+
} while (cursor);
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
const existing = await findStack();
|
|
53
|
+
if (existing)
|
|
54
|
+
return resolved(existing, false);
|
|
55
|
+
if (dryRun)
|
|
56
|
+
return null;
|
|
57
|
+
const result = await createStack({
|
|
58
|
+
auth,
|
|
59
|
+
logger,
|
|
60
|
+
fetchFn,
|
|
61
|
+
stackMutation: {
|
|
62
|
+
name,
|
|
63
|
+
scopeType: auth.scopeType,
|
|
64
|
+
scopeId: auth.scopeId,
|
|
65
|
+
document: { resources: [] },
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
if (result.ok)
|
|
69
|
+
return resolved(result.stack, true);
|
|
70
|
+
if (result.response?.status === 409) {
|
|
71
|
+
const concurrent = await findStack();
|
|
72
|
+
if (concurrent)
|
|
73
|
+
return resolved(concurrent, false);
|
|
74
|
+
}
|
|
75
|
+
throw stackRequestError(result);
|
|
76
|
+
}
|
|
77
|
+
function appFunctionStackName(appId) {
|
|
78
|
+
return `app-${appId}`;
|
|
79
|
+
}
|
|
80
|
+
function stackRequestError(result) {
|
|
81
|
+
const status = result.response ? ` (${result.response.status})` : '';
|
|
82
|
+
return new Error(`Blueprint stack request failed${status}: ${result.error || result.response?.statusText || 'Unknown error'}`);
|
|
83
|
+
}
|
|
84
|
+
export { BLUEPRINTS_API_VERSION } from '../config.js';
|
package/dist/server/app.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { existsSync, readFileSync, watchFile } from 'node:fs';
|
|
2
2
|
import * as http from 'node:http';
|
|
3
3
|
import { dirname } from 'node:path';
|
|
4
|
+
import { resolveStackConfig } from '@sanity/blueprints/resolve';
|
|
4
5
|
import { default as mime } from 'mime-types';
|
|
5
6
|
import { WebSocket, WebSocketServer } from 'ws';
|
|
6
7
|
import { findBlueprintFile, loadAndParseBlueprint } from '../actions/blueprints/blueprint.js';
|
|
7
8
|
import { readConfigFile } from '../actions/blueprints/config.js';
|
|
8
|
-
import { resolveIds } from '../actions/blueprints/resolve.js';
|
|
9
9
|
import config from '../config.js';
|
|
10
10
|
import { isRecord } from '../utils/is-record.js';
|
|
11
|
-
import { isEventType,
|
|
11
|
+
import { isEventType, } from '../utils/types.js';
|
|
12
12
|
import { handleInvokeRequest } from './handlers/invoke.js';
|
|
13
13
|
const app = (host, port, validateResources, executionOptions) => {
|
|
14
14
|
// Send a JSON envelope to every connected UI client.
|
|
@@ -47,7 +47,7 @@ const app = (host, port, validateResources, executionOptions) => {
|
|
|
47
47
|
const blueprint = await loadAndParseBlueprint(undefined, { validateResources });
|
|
48
48
|
const { parsedBlueprint, fileInfo, module: blueprintModule } = blueprint;
|
|
49
49
|
const blueprintConfig = readConfigFile(fileInfo.blueprintFilePath);
|
|
50
|
-
const { projectId, organizationId } =
|
|
50
|
+
const { projectId, organizationId } = resolveStackConfig({
|
|
51
51
|
module: blueprintModule,
|
|
52
52
|
config: blueprintConfig,
|
|
53
53
|
});
|
|
@@ -75,7 +75,7 @@ const app = (host, port, validateResources, executionOptions) => {
|
|
|
75
75
|
const { data, func: functionName, metadata, } = parseInvokeRequest(Buffer.concat(body), dirname(fileInfo.blueprintFilePath));
|
|
76
76
|
const { context, event } = data;
|
|
77
77
|
// replace user token if required
|
|
78
|
-
if (
|
|
78
|
+
if ('clientOptions' in context) {
|
|
79
79
|
if (context.clientOptions.token) {
|
|
80
80
|
context.clientOptions.token = config.token || undefined;
|
|
81
81
|
}
|
|
@@ -191,7 +191,7 @@ body {
|
|
|
191
191
|
.border-right {
|
|
192
192
|
border-right-width: 1px;
|
|
193
193
|
border-right-style: solid;
|
|
194
|
-
border-
|
|
194
|
+
border-right-color: var(--card-border-color);
|
|
195
195
|
}
|
|
196
196
|
|
|
197
197
|
.hr-border {
|
|
@@ -413,6 +413,13 @@ ol[type='content'] li:hover:not(.selected:not(.selected)) {
|
|
|
413
413
|
border-right: none;
|
|
414
414
|
}
|
|
415
415
|
|
|
416
|
+
fieldset {
|
|
417
|
+
& :is(input:not([role='switch']), select, textarea) {
|
|
418
|
+
border: 1px solid light-dark(var(--gray-200), var(--gray-800)) !important;
|
|
419
|
+
border-radius: var(--radius-2);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
416
423
|
& :is(input:not([is='switch']), select, textarea) {
|
|
417
424
|
border: 1px solid light-dark(var(--gray-200), var(--gray-800)) !important;
|
|
418
425
|
border-radius: var(--radius-2);
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { BlueprintResource } from '@sanity/blueprints';
|
|
2
|
+
import type { ResolvedResource } from '../types.js';
|
|
2
3
|
export type GroupedBlueprintResource = {
|
|
3
4
|
id: string;
|
|
4
5
|
name: string;
|
|
5
6
|
type: string;
|
|
6
7
|
};
|
|
7
8
|
export type GroupedBlueprintResources = Record<string, GroupedBlueprintResource[]>;
|
|
8
|
-
export declare function groupBlueprintResources(blueprintResources: BlueprintResource[]): GroupedBlueprintResources;
|
|
9
|
+
export declare function groupBlueprintResources(blueprintResources: ResolvedResource<BlueprintResource>[]): GroupedBlueprintResources;
|
|
@@ -8,7 +8,8 @@ const BLUEPRINT_TYPE_TO_KEY = {
|
|
|
8
8
|
};
|
|
9
9
|
export function groupBlueprintResources(blueprintResources) {
|
|
10
10
|
const grouped = {};
|
|
11
|
-
for (const
|
|
11
|
+
for (const resolvedResource of blueprintResources) {
|
|
12
|
+
const resource = resolvedResource.resource;
|
|
12
13
|
if (resource.type === 'sanity.access.robot')
|
|
13
14
|
continue;
|
|
14
15
|
const key = (resource.type?.startsWith('sanity.function.')
|
|
@@ -18,9 +19,8 @@ export function groupBlueprintResources(blueprintResources) {
|
|
|
18
19
|
continue;
|
|
19
20
|
if (!grouped[key])
|
|
20
21
|
grouped[key] = [];
|
|
21
|
-
const externalId = resource.externalId;
|
|
22
22
|
grouped[key].push({
|
|
23
|
-
id: externalId || 'not-deployed',
|
|
23
|
+
id: resolvedResource.externalId || 'not-deployed',
|
|
24
24
|
name: resource.name,
|
|
25
25
|
type: resource.type,
|
|
26
26
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { BlueprintResource } from '@sanity/blueprints';
|
|
2
|
-
import type { AuthParams } from '../types.js';
|
|
2
|
+
import type { AuthParams, ResolvedResource } from '../types.js';
|
|
3
3
|
interface ResolveOptions {
|
|
4
4
|
resources: BlueprintResource[];
|
|
5
5
|
stackId?: string;
|
|
@@ -12,5 +12,5 @@ interface ResolveOptions {
|
|
|
12
12
|
* resources that aren't yet deployed are preserved without an externalId.
|
|
13
13
|
* Any failure to fetch the stack falls back to the raw local resources.
|
|
14
14
|
*/
|
|
15
|
-
export declare function resolveDeployedResources({ resources, stackId, auth, fetchFn, }: ResolveOptions): Promise<BlueprintResource[]>;
|
|
15
|
+
export declare function resolveDeployedResources({ resources, stackId, auth, fetchFn, }: ResolveOptions): Promise<ResolvedResource<BlueprintResource>[]>;
|
|
16
16
|
export {};
|
|
@@ -7,11 +7,11 @@ import { getStack } from '../../actions/blueprints/stacks.js';
|
|
|
7
7
|
*/
|
|
8
8
|
export async function resolveDeployedResources({ resources, stackId, auth, fetchFn, }) {
|
|
9
9
|
if (!stackId || !auth?.token || !auth.scopeType || !auth.scopeId)
|
|
10
|
-
return resources;
|
|
10
|
+
return resources.map((r) => ({ resource: r }));
|
|
11
11
|
try {
|
|
12
12
|
const response = await getStack({ stackId, auth, fetchFn });
|
|
13
13
|
if (!response.ok || !Array.isArray(response.stack?.resources))
|
|
14
|
-
return resources;
|
|
14
|
+
return resources.map((r) => ({ resource: r }));
|
|
15
15
|
const deployedByKey = new Map();
|
|
16
16
|
for (const deployed of response.stack.resources) {
|
|
17
17
|
if (deployed.externalId) {
|
|
@@ -20,10 +20,10 @@ export async function resolveDeployedResources({ resources, stackId, auth, fetch
|
|
|
20
20
|
}
|
|
21
21
|
return resources.map((resource) => {
|
|
22
22
|
const externalId = deployedByKey.get(`${resource.type}:${resource.name}`);
|
|
23
|
-
return
|
|
23
|
+
return { resource, externalId };
|
|
24
24
|
});
|
|
25
25
|
}
|
|
26
26
|
catch {
|
|
27
|
-
return resources;
|
|
27
|
+
return resources.map((r) => ({ resource: r }));
|
|
28
28
|
}
|
|
29
29
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { BlueprintApplicationResource, BlueprintInstallationConfigResource, BlueprintStudioResource } from '@sanity/blueprints';
|
|
2
2
|
import { type BuildStudioEventListener } from '@sanity/cli-build/_internal/build';
|
|
3
3
|
import type { Output } from '@sanity/cli-core';
|
|
4
|
-
import type { AssetPrepResult } from '../types.js';
|
|
4
|
+
import type { AssetPrepResult, ResolvedResource } from '../types.js';
|
|
5
5
|
export type BundleResource = BlueprintStudioResource | BlueprintApplicationResource | BlueprintInstallationConfigResource;
|
|
6
6
|
type BuildContext<R extends BundleResource> = {
|
|
7
7
|
resource: R;
|
|
@@ -28,9 +28,9 @@ export declare const BUNDLE_SPECS: {
|
|
|
28
28
|
build: typeof buildInstallationConfigBundle;
|
|
29
29
|
};
|
|
30
30
|
};
|
|
31
|
-
export declare function prepareBundleAsset<R extends BundleResource>({ blueprintFileDir,
|
|
31
|
+
export declare function prepareBundleAsset<R extends BundleResource>({ blueprintFileDir, resolvedResource, }: {
|
|
32
32
|
blueprintFileDir: string;
|
|
33
|
-
|
|
33
|
+
resolvedResource: ResolvedResource<R>;
|
|
34
34
|
}): Promise<AssetPrepResult>;
|
|
35
35
|
declare function buildStudioBundle({ resource, appId, workDir, outDir, output, eventListener, }: BuildContext<BlueprintStudioResource>): Promise<BuildResult>;
|
|
36
36
|
declare function buildApplicationBundle({ resource, appId, workDir, outDir, output, eventListener, }: BuildContext<BlueprintApplicationResource>): Promise<BuildResult>;
|
|
@@ -12,10 +12,11 @@ export const BUNDLE_SPECS = {
|
|
|
12
12
|
'sanity.application': { requiresAppId: true, build: buildApplicationBundle },
|
|
13
13
|
'sanity.installation.config': { requiresAppId: false, build: buildInstallationConfigBundle },
|
|
14
14
|
};
|
|
15
|
-
export async function prepareBundleAsset({ blueprintFileDir,
|
|
15
|
+
export async function prepareBundleAsset({ blueprintFileDir, resolvedResource, }) {
|
|
16
|
+
const resource = resolvedResource.resource;
|
|
16
17
|
// The lookup can't prove it yields the spec matching this R, so it's asserted once here.
|
|
17
18
|
const spec = BUNDLE_SPECS[resource.type];
|
|
18
|
-
const appId = getAppId(
|
|
19
|
+
const appId = getAppId(resolvedResource);
|
|
19
20
|
if (spec.requiresAppId && !appId) {
|
|
20
21
|
return {
|
|
21
22
|
success: false,
|
|
@@ -260,12 +261,12 @@ async function getInstalledSanityVersion(workDir) {
|
|
|
260
261
|
return undefined;
|
|
261
262
|
}
|
|
262
263
|
}
|
|
263
|
-
function getAppId(
|
|
264
|
-
if (
|
|
265
|
-
return
|
|
264
|
+
function getAppId(resolvedResource) {
|
|
265
|
+
if (resolvedResource.externalId) {
|
|
266
|
+
return resolvedResource.externalId;
|
|
266
267
|
}
|
|
267
|
-
else if (resource.lifecycle?.ownershipAction?.type === 'attach') {
|
|
268
|
-
return resource.lifecycle.ownershipAction.id;
|
|
268
|
+
else if (resolvedResource.resource.lifecycle?.ownershipAction?.type === 'attach') {
|
|
269
|
+
return resolvedResource.resource.lifecycle.ownershipAction.id;
|
|
269
270
|
}
|
|
270
271
|
return undefined;
|
|
271
272
|
}
|
|
@@ -86,7 +86,7 @@ process.on('message', async (data) => {
|
|
|
86
86
|
{
|
|
87
87
|
emit: (e) => process.send(JSON.stringify({type: 'step-event', event: e})),
|
|
88
88
|
wait: (ms) => new Promise((resolve) => setTimeout(resolve, noWait ? 0 : ms)),
|
|
89
|
-
invoke: invoker.
|
|
89
|
+
invoke: invoker.delegate,
|
|
90
90
|
},
|
|
91
91
|
)
|
|
92
92
|
} else {
|