@sanity/runtime-cli 14.3.0 → 14.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 +31 -27
- package/dist/actions/blueprints/assets.d.ts +3 -2
- package/dist/actions/blueprints/assets.js +14 -3
- package/dist/actions/blueprints/blueprint.js +2 -2
- package/dist/actions/blueprints/resources.js +2 -1
- package/dist/actions/functions/logs.d.ts +0 -1
- package/dist/actions/functions/logs.js +0 -1
- package/dist/commands/blueprints/add.js +2 -1
- package/dist/commands/blueprints/deploy.d.ts +1 -0
- package/dist/commands/blueprints/deploy.js +10 -0
- package/dist/commands/functions/add.js +2 -1
- package/dist/commands/functions/build.d.ts +1 -0
- package/dist/commands/functions/build.js +7 -0
- package/dist/constants.d.ts +18 -1
- package/dist/constants.js +26 -9
- package/dist/cores/blueprints/deploy.d.ts +1 -0
- package/dist/cores/blueprints/deploy.js +2 -1
- package/dist/cores/functions/add.js +13 -13
- package/dist/cores/functions/build.d.ts +1 -0
- package/dist/cores/functions/build.js +2 -1
- package/dist/cores/functions/logs.js +3 -5
- package/dist/cores/functions/test.js +1 -1
- package/dist/server/app.js +6 -4
- package/dist/server/static/components/api-base.js +1 -1
- package/dist/server/static/components/filters.js +1 -1
- package/dist/server/static/components/function-list.js +2 -2
- package/dist/server/static/components/payload-panel.js +1 -1
- package/dist/server/static/components/run-panel.js +2 -2
- package/dist/utils/display/blueprints-formatting.js +12 -5
- package/dist/utils/find-function.js +4 -3
- package/dist/utils/functions/detect-native-modules.d.ts +7 -1
- package/dist/utils/functions/detect-native-modules.js +69 -1
- package/dist/utils/functions/prepare-asset.d.ts +3 -1
- package/dist/utils/functions/prepare-asset.js +28 -3
- package/dist/utils/functions/resolve-dependencies.d.ts +5 -2
- package/dist/utils/functions/resolve-dependencies.js +19 -12
- package/dist/utils/invoke-local.js +1 -1
- package/dist/utils/pnpm.d.ts +1 -0
- package/dist/utils/pnpm.js +9 -0
- package/dist/utils/transpile/transpile-function.d.ts +4 -2
- package/dist/utils/transpile/transpile-function.js +5 -8
- package/dist/utils/types.d.ts +8 -5
- package/dist/utils/types.js +8 -4
- package/dist/utils/validate/index.js +7 -4
- package/dist/utils/validate/resource.js +3 -2
- package/oclif.manifest.json +42 -7
- package/package.json +5 -4
|
@@ -74,7 +74,7 @@ async function streamLogs({ name, externalId, auth, log, }) {
|
|
|
74
74
|
}
|
|
75
75
|
async function getLogs({ name, externalId, auth, limit, json, utc, log, }) {
|
|
76
76
|
const spinner = log.ora(`Finding logs for function "${name}"`).start();
|
|
77
|
-
const { ok, error, logs
|
|
77
|
+
const { ok, error, logs } = await getLogsAction(externalId, { limit }, auth, log);
|
|
78
78
|
if (!ok) {
|
|
79
79
|
spinner.fail(`${styleText('red', 'Failed')} to retrieve logs`);
|
|
80
80
|
return { success: false, error: error || 'Unknown error' };
|
|
@@ -86,10 +86,8 @@ async function getLogs({ name, externalId, auth, limit, json, utc, log, }) {
|
|
|
86
86
|
}
|
|
87
87
|
spinner.succeed(`${formatTitle('Function', name)} Logs`);
|
|
88
88
|
if (!json) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
log(`Here are the last ${styleText('bold', filteredLogs.length.toString())} entries`);
|
|
92
|
-
}
|
|
89
|
+
const logLength = filteredLogs.length;
|
|
90
|
+
log(`Found ${styleText('bold', logLength.toString())} log ${logLength === 1 ? 'entry' : 'entries'} for function ${styleText('yellow', name)}`);
|
|
93
91
|
log('\n');
|
|
94
92
|
for (const { time, level, message } of filteredLogs) {
|
|
95
93
|
log(formatLog(time, level, message, utc));
|
|
@@ -135,7 +135,7 @@ export async function functionTestCore(options) {
|
|
|
135
135
|
after,
|
|
136
136
|
}
|
|
137
137
|
: {
|
|
138
|
-
event: '
|
|
138
|
+
event: 'scheduled',
|
|
139
139
|
};
|
|
140
140
|
const spinner = log.ora('Executing function...').start();
|
|
141
141
|
const { json, logs, error } = await testAction(resource, invokeOptions, contextOptions, {
|
package/dist/server/app.js
CHANGED
|
@@ -329,13 +329,13 @@ function parseInvokeRequest(body) {
|
|
|
329
329
|
}
|
|
330
330
|
const metadataEvent = metadata.event;
|
|
331
331
|
if (typeof metadataEvent !== 'string' ||
|
|
332
|
-
(!isEventType(metadataEvent) && metadataEvent !== '
|
|
333
|
-
throw new Error('Request body is not valid, `metadata.event` field is not one of `create`, `update`, `delete`, or `
|
|
332
|
+
(!isEventType(metadataEvent) && metadataEvent !== 'scheduled')) {
|
|
333
|
+
throw new Error('Request body is not valid, `metadata.event` field is not one of `create`, `update`, `delete`, or `scheduled`');
|
|
334
334
|
}
|
|
335
335
|
let before = null;
|
|
336
336
|
let after = null;
|
|
337
337
|
// Only GROQ-based events (create, update, delete) have before and after fields
|
|
338
|
-
if (metadataEvent !== '
|
|
338
|
+
if (metadataEvent !== 'scheduled') {
|
|
339
339
|
if (!('before' in metadata)) {
|
|
340
340
|
throw new Error('Request body is not valid, `metadata.before` field is missing');
|
|
341
341
|
}
|
|
@@ -374,7 +374,9 @@ function parseInvokeRequest(body) {
|
|
|
374
374
|
},
|
|
375
375
|
event,
|
|
376
376
|
},
|
|
377
|
-
metadata: metadataEvent === '
|
|
377
|
+
metadata: metadataEvent === 'scheduled'
|
|
378
|
+
? { event: metadataEvent }
|
|
379
|
+
: { event: metadataEvent, before, after },
|
|
378
380
|
};
|
|
379
381
|
}
|
|
380
382
|
export { app, buildApiUrl, parseDocumentUrl };
|
|
@@ -9,6 +9,6 @@ export class ApiBaseElement extends HTMLElement {
|
|
|
9
9
|
this.api = api
|
|
10
10
|
this.SANITY_FUNCTION_DOCUMENT = 'sanity.function.document'
|
|
11
11
|
this.SANITY_FUNCTION_MEDIA_LIBRARY_ASSET = 'sanity.function.media-library.asset'
|
|
12
|
-
this.
|
|
12
|
+
this.SANITY_FUNCTION_SCHEDULED = 'sanity.function.cron'
|
|
13
13
|
}
|
|
14
14
|
}
|
|
@@ -79,7 +79,7 @@ class FiltersComponent extends ApiBaseElement {
|
|
|
79
79
|
const mediaFunction = this.api.store.selectedFunctionType?.startsWith(
|
|
80
80
|
this.SANITY_FUNCTION_MEDIA_LIBRARY_ASSET,
|
|
81
81
|
)
|
|
82
|
-
const scheduleFunction = this.api.store.selectedFunctionType === this.
|
|
82
|
+
const scheduleFunction = this.api.store.selectedFunctionType === this.SANITY_FUNCTION_SCHEDULED
|
|
83
83
|
|
|
84
84
|
const container = this.shadowRoot.querySelector('fieldset')
|
|
85
85
|
container.innerHTML = this.buildFilters(docFunction, mediaFunction, scheduleFunction)
|
|
@@ -58,8 +58,8 @@ class FunctionList extends ApiBaseElement {
|
|
|
58
58
|
return 'Document'
|
|
59
59
|
case this.SANITY_FUNCTION_MEDIA_LIBRARY_ASSET:
|
|
60
60
|
return 'Media Library'
|
|
61
|
-
case this.
|
|
62
|
-
return '
|
|
61
|
+
case this.SANITY_FUNCTION_SCHEDULED:
|
|
62
|
+
return 'Scheduled'
|
|
63
63
|
default:
|
|
64
64
|
return type.split('.').pop().replaceAll('-', ' ')
|
|
65
65
|
}
|
|
@@ -79,7 +79,7 @@ class PayloadPanel extends ApiBaseElement {
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
updateCodeMirror = ({selectedFunctionType}) => {
|
|
82
|
-
if (selectedFunctionType === this.
|
|
82
|
+
if (selectedFunctionType === this.SANITY_FUNCTION_SCHEDULED) {
|
|
83
83
|
this.api.store.beforePayload.dispatch({
|
|
84
84
|
effects: editableCompartment.reconfigure(EditorView.editable.of(false)),
|
|
85
85
|
})
|
|
@@ -25,7 +25,7 @@ class RunPanel extends ApiBaseElement {
|
|
|
25
25
|
const docFunction = this.api.store.selectedFunctionType === this.SANITY_FUNCTION_DOCUMENT
|
|
26
26
|
const mediaFunction =
|
|
27
27
|
this.api.store.selectedFunctionType === this.SANITY_FUNCTION_MEDIA_LIBRARY_ASSET
|
|
28
|
-
const scheduleFunction = this.api.store.selectedFunctionType === this.
|
|
28
|
+
const scheduleFunction = this.api.store.selectedFunctionType === this.SANITY_FUNCTION_SCHEDULED
|
|
29
29
|
const docOrScheduleFunction = docFunction || scheduleFunction
|
|
30
30
|
|
|
31
31
|
this.api.store.result = {logs: '', time: 0}
|
|
@@ -58,7 +58,7 @@ class RunPanel extends ApiBaseElement {
|
|
|
58
58
|
token: this.api.store.withToken,
|
|
59
59
|
},
|
|
60
60
|
eventResourceType: scheduleFunction
|
|
61
|
-
? '
|
|
61
|
+
? 'scheduled'
|
|
62
62
|
: mediaFunction
|
|
63
63
|
? 'media-library'
|
|
64
64
|
: 'dataset',
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { treeify } from 'array-treeify';
|
|
2
|
-
import { SANITY_ACCESS_ROBOT, SANITY_ACCESS_ROLE, SANITY_FUNCTION_DOCUMENT, SANITY_FUNCTION_MEDIA_LIBRARY_ASSET,
|
|
2
|
+
import { SANITY_ACCESS_ROBOT, SANITY_ACCESS_ROLE, SANITY_FUNCTION_DOCUMENT, SANITY_FUNCTION_MEDIA_LIBRARY_ASSET, SANITY_FUNCTION_SCHEDULED, SANITY_PROJECT, SANITY_PROJECT_CORS, SANITY_PROJECT_DATASET, SANITY_PROJECT_WEBHOOK, } from '../../constants.js';
|
|
3
3
|
import { styleText } from '../style-text.js';
|
|
4
4
|
import { isCorsOriginResource, isDatasetResource, isRobotResource, isRoleResource, isWebhookResource, } from '../types.js';
|
|
5
5
|
import { formatDate, formatDuration } from './dates.js';
|
|
@@ -19,7 +19,7 @@ const RESOURCE_CATEGORIES = {
|
|
|
19
19
|
},
|
|
20
20
|
},
|
|
21
21
|
[SANITY_ACCESS_ROBOT]: {
|
|
22
|
-
label: '
|
|
22
|
+
label: 'Robot Tokens',
|
|
23
23
|
displayNameAttribute: 'label',
|
|
24
24
|
formatDetails(res) {
|
|
25
25
|
return isRobotResource(res) ? arrayifyRobot(res) : [];
|
|
@@ -27,7 +27,14 @@ const RESOURCE_CATEGORIES = {
|
|
|
27
27
|
},
|
|
28
28
|
[SANITY_FUNCTION_DOCUMENT]: functionCategory,
|
|
29
29
|
[SANITY_FUNCTION_MEDIA_LIBRARY_ASSET]: functionCategory,
|
|
30
|
-
[
|
|
30
|
+
[SANITY_FUNCTION_SCHEDULED]: functionCategory,
|
|
31
|
+
[SANITY_PROJECT]: {
|
|
32
|
+
label: 'Projects',
|
|
33
|
+
displayNameAttribute: 'displayName',
|
|
34
|
+
formatDetails(_res) {
|
|
35
|
+
return [];
|
|
36
|
+
},
|
|
37
|
+
},
|
|
31
38
|
[SANITY_PROJECT_CORS]: {
|
|
32
39
|
label: 'CORS Origins',
|
|
33
40
|
formatDetails(res) {
|
|
@@ -212,12 +219,12 @@ export function formatStacksListing(stacks, currentStackId) {
|
|
|
212
219
|
const IGNORED_PARAMS = {
|
|
213
220
|
[SANITY_FUNCTION_DOCUMENT]: new Set(['src']),
|
|
214
221
|
[SANITY_FUNCTION_MEDIA_LIBRARY_ASSET]: new Set(['src']),
|
|
215
|
-
[
|
|
222
|
+
[SANITY_FUNCTION_SCHEDULED]: new Set(['src']),
|
|
216
223
|
};
|
|
217
224
|
const ASSET_RESOURCE_TYPES = new Set([
|
|
218
225
|
SANITY_FUNCTION_DOCUMENT,
|
|
219
226
|
SANITY_FUNCTION_MEDIA_LIBRARY_ASSET,
|
|
220
|
-
|
|
227
|
+
SANITY_FUNCTION_SCHEDULED,
|
|
221
228
|
]);
|
|
222
229
|
function stringifyUnknown(val) {
|
|
223
230
|
if (val === null || val === undefined)
|
|
@@ -1,17 +1,18 @@
|
|
|
1
|
+
import { SANITY_FUNCTION_PREFIX } from '../constants.js';
|
|
1
2
|
export function getFunctionNames(resources) {
|
|
2
3
|
return (resources
|
|
3
|
-
?.filter((r) => r?.type?.startsWith(
|
|
4
|
+
?.filter((r) => r?.type?.startsWith(SANITY_FUNCTION_PREFIX))
|
|
4
5
|
.map((r) => r.name)
|
|
5
6
|
.filter((name) => typeof name === 'string') ?? []);
|
|
6
7
|
}
|
|
7
8
|
export function findFunctionInBlueprint(blueprint, name) {
|
|
8
|
-
const func = blueprint?.resources?.find((r) => r?.type?.startsWith(
|
|
9
|
+
const func = blueprint?.resources?.find((r) => r?.type?.startsWith(SANITY_FUNCTION_PREFIX) && r.name === name);
|
|
9
10
|
if (!func)
|
|
10
11
|
throw Error(`Unable to find function ${name}`);
|
|
11
12
|
return func;
|
|
12
13
|
}
|
|
13
14
|
export function findFunctionInStack(stack, name) {
|
|
14
|
-
const func = stack?.resources?.find((r) => r?.type?.startsWith(
|
|
15
|
+
const func = stack?.resources?.find((r) => r?.type?.startsWith(SANITY_FUNCTION_PREFIX) && r.name === name);
|
|
15
16
|
if (!func)
|
|
16
17
|
throw Error(`Unable to find function: "${name}"`);
|
|
17
18
|
if (!isDeployedResource(func))
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import type AdmZip from 'adm-zip';
|
|
2
|
+
import type { FunctionResource } from '../types.js';
|
|
2
3
|
/**
|
|
3
|
-
*
|
|
4
|
+
* Recurse node_modules for native modules.
|
|
4
5
|
* Native modules built on one platform may not work on another (e.g., macOS vs Linux)
|
|
5
6
|
*/
|
|
6
7
|
export declare const detectNativeModules: (zip: AdmZip) => string[];
|
|
8
|
+
/**
|
|
9
|
+
* Finds the dependencies of the function and compares to the workspace root to see if any native modules are included.
|
|
10
|
+
* Due to how PNPM with workspaces symlinks dependencies, we have to check the workspace root for native modules prior to the bundle step.
|
|
11
|
+
*/
|
|
12
|
+
export declare const detectNativeModulesForBundle: (resource: FunctionResource) => string[];
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { cwd } from 'node:process';
|
|
4
|
+
import { createPnpmRequire } from '../pnpm.js';
|
|
1
5
|
/**
|
|
2
6
|
* Patterns to identify native modules based on common file types and build configurations.
|
|
3
7
|
*/
|
|
4
8
|
const KNOWN_MODULE_PATTERNS = [/binding\.gyp$/, /\.node$/];
|
|
5
9
|
/**
|
|
6
|
-
*
|
|
10
|
+
* Recurse node_modules for native modules.
|
|
7
11
|
* Native modules built on one platform may not work on another (e.g., macOS vs Linux)
|
|
8
12
|
*/
|
|
9
13
|
export const detectNativeModules = (zip) => {
|
|
@@ -24,3 +28,67 @@ export const detectNativeModules = (zip) => {
|
|
|
24
28
|
throw new Error(`Failed to scan zip for native modules: ${message}`);
|
|
25
29
|
}
|
|
26
30
|
};
|
|
31
|
+
/**
|
|
32
|
+
* Recursively checks a directory for files that match known native module patterns.
|
|
33
|
+
*/
|
|
34
|
+
const hasNativeFiles = (directory) => {
|
|
35
|
+
for (const name of readdirSync(directory)) {
|
|
36
|
+
// don't recurse back into node_modules.
|
|
37
|
+
if (name === 'node_modules')
|
|
38
|
+
continue;
|
|
39
|
+
const fullPath = path.join(directory, name);
|
|
40
|
+
if (statSync(fullPath).isDirectory()) {
|
|
41
|
+
if (hasNativeFiles(fullPath))
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
else if (KNOWN_MODULE_PATTERNS.some((pattern) => pattern.test(name))) {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return false;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Finds the dependencies of the function and compares to the workspace root to see if any native modules are included.
|
|
52
|
+
* Due to how PNPM with workspaces symlinks dependencies, we have to check the workspace root for native modules prior to the bundle step.
|
|
53
|
+
*/
|
|
54
|
+
export const detectNativeModulesForBundle = (resource) => {
|
|
55
|
+
if (!resource.src)
|
|
56
|
+
throw new Error('Resource src is required');
|
|
57
|
+
const hasWorkspace = existsSync(path.join(cwd(), 'pnpm-workspace.yaml'));
|
|
58
|
+
if (!hasWorkspace) {
|
|
59
|
+
return [];
|
|
60
|
+
}
|
|
61
|
+
const sourcePath = path.resolve(cwd(), resource.src);
|
|
62
|
+
const stats = statSync(sourcePath);
|
|
63
|
+
const entryDir = stats.isFile() ? path.dirname(sourcePath) : sourcePath;
|
|
64
|
+
const hasPackageJson = existsSync(path.join(entryDir, 'package.json'));
|
|
65
|
+
if (!hasPackageJson) {
|
|
66
|
+
throw new Error('pnpm workspace detected but no package.json found in function src');
|
|
67
|
+
}
|
|
68
|
+
// gets package.json dependencies of the function. We use these function deps to compare against the workspace root modules.
|
|
69
|
+
const pkgJsonPath = path.join(entryDir, 'package.json');
|
|
70
|
+
// a function should always have deps, but this will just be a little safer.
|
|
71
|
+
const functionDeps = JSON.parse(readFileSync(pkgJsonPath, 'utf-8')).dependencies ?? {};
|
|
72
|
+
const workspaceModules = createPnpmRequire(cwd());
|
|
73
|
+
const nativeModules = [];
|
|
74
|
+
for (const depName of Object.keys(functionDeps)) {
|
|
75
|
+
try {
|
|
76
|
+
const resolved = workspaceModules.resolve(depName);
|
|
77
|
+
let currentDir = path.dirname(resolved);
|
|
78
|
+
while (currentDir !== cwd() && currentDir !== path.parse(currentDir).root) {
|
|
79
|
+
// look for node_module package.json to locate top level
|
|
80
|
+
if (existsSync(path.join(currentDir, 'package.json'))) {
|
|
81
|
+
if (hasNativeFiles(currentDir)) {
|
|
82
|
+
nativeModules.push(depName);
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
currentDir = path.dirname(currentDir);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
catch (_e) {
|
|
90
|
+
// If we fail to resolve, just skip
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return nativeModules;
|
|
94
|
+
};
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import type { CollectionFunction, FunctionResource } from '../types.js';
|
|
1
|
+
import type { CollectionFunction, FunctionResource, InstallerType } from '../types.js';
|
|
2
2
|
export declare function prepareAsset({ resource, }: {
|
|
3
3
|
resource: FunctionResource | CollectionFunction;
|
|
4
|
+
}, { installer }?: {
|
|
5
|
+
installer?: InstallerType;
|
|
4
6
|
}): Promise<{
|
|
5
7
|
success: boolean;
|
|
6
8
|
outputPath?: string;
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
+
import { rm, stat } from 'node:fs/promises';
|
|
1
2
|
import path from 'node:path';
|
|
2
3
|
import { cwd } from 'node:process';
|
|
3
4
|
import { MAX_ASSET_SIZE } from '../../constants.js';
|
|
4
5
|
import { transpileFunction } from '../transpile/transpile-function.js';
|
|
6
|
+
import { detectNativeModulesForBundle } from './detect-native-modules.js';
|
|
5
7
|
import { getFolderSize } from './getFolderSize.js';
|
|
6
8
|
import { resolveResourceDependencies } from './resolve-dependencies.js';
|
|
7
9
|
import { shouldAutoResolveDependencies } from './should-auto-resolve-deps.js';
|
|
8
10
|
import { shouldTranspileFunction } from './should-transpile.js';
|
|
9
|
-
export async function prepareAsset({ resource, }) {
|
|
11
|
+
export async function prepareAsset({ resource, }, { installer } = {}) {
|
|
10
12
|
if (!resource.src)
|
|
11
13
|
throw new Error('Resource src is required');
|
|
12
14
|
let functionPath = path.join(cwd(), resource.src);
|
|
@@ -15,7 +17,13 @@ export async function prepareAsset({ resource, }) {
|
|
|
15
17
|
const shouldTranspile = await shouldTranspileFunction(resource);
|
|
16
18
|
if (shouldTranspile) {
|
|
17
19
|
try {
|
|
18
|
-
const
|
|
20
|
+
const hasNativeModules = detectNativeModulesForBundle(resource);
|
|
21
|
+
if (hasNativeModules.length) {
|
|
22
|
+
const errorMsg = `Native modules detected:\n${hasNativeModules.join('\n')}\n\n` +
|
|
23
|
+
`Please replace with JavaScript alternatives.`;
|
|
24
|
+
throw new Error(errorMsg);
|
|
25
|
+
}
|
|
26
|
+
const result = await transpileFunction(resource, { installer });
|
|
19
27
|
functionPath = result.outputDir;
|
|
20
28
|
cleanup = result.cleanup;
|
|
21
29
|
wasBundled = result.bundled;
|
|
@@ -26,7 +34,8 @@ export async function prepareAsset({ resource, }) {
|
|
|
26
34
|
}
|
|
27
35
|
const shouldResolveDependencies = await shouldAutoResolveDependencies(resource);
|
|
28
36
|
if (shouldResolveDependencies && !wasBundled) {
|
|
29
|
-
await resolveResourceDependencies(resource, shouldTranspile);
|
|
37
|
+
await resolveResourceDependencies(resource, { transpiled: shouldTranspile, installer });
|
|
38
|
+
await removeArcAutoInstallPackageJson(functionPath);
|
|
30
39
|
}
|
|
31
40
|
try {
|
|
32
41
|
const size = getFolderSize(functionPath);
|
|
@@ -39,3 +48,19 @@ export async function prepareAsset({ resource, }) {
|
|
|
39
48
|
return { success: false, error: err instanceof Error ? err.message : `${err}` };
|
|
40
49
|
}
|
|
41
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Remove file that contains the current date to prevent the hash from changing in the final asset.
|
|
53
|
+
* @param functionPath The path to the built function
|
|
54
|
+
*/
|
|
55
|
+
async function removeArcAutoInstallPackageJson(functionPath) {
|
|
56
|
+
const arcAutoInstallPackageJson = path.join(functionPath, 'node_modules', '_arc-autoinstall', 'package.json');
|
|
57
|
+
try {
|
|
58
|
+
const fileStat = await stat(arcAutoInstallPackageJson);
|
|
59
|
+
if (fileStat.isFile()) {
|
|
60
|
+
await rm(arcAutoInstallPackageJson);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// ignore errors
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -1,2 +1,5 @@
|
|
|
1
|
-
import type { FunctionResource } from '../types.js';
|
|
2
|
-
export declare function resolveResourceDependencies(resource: FunctionResource, transpiled
|
|
1
|
+
import type { FunctionResource, InstallerType } from '../types.js';
|
|
2
|
+
export declare function resolveResourceDependencies(resource: FunctionResource, { transpiled, installer, }: {
|
|
3
|
+
transpiled: boolean;
|
|
4
|
+
installer?: InstallerType;
|
|
5
|
+
}): Promise<void>;
|
|
@@ -3,25 +3,22 @@ import { join } from 'node:path';
|
|
|
3
3
|
import hydrate from '@architect/hydrate';
|
|
4
4
|
import inventory from '@architect/inventory';
|
|
5
5
|
import { convertResourceToArcFormat } from './resource-to-arc.js';
|
|
6
|
-
export async function resolveResourceDependencies(resource, transpiled) {
|
|
6
|
+
export async function resolveResourceDependencies(resource, { transpiled, installer, }) {
|
|
7
7
|
const rawArc = await convertResourceToArcFormat(resource, transpiled);
|
|
8
8
|
const inv = await inventory({ rawArc });
|
|
9
9
|
const cwd = inv.inv._project.cwd;
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
};
|
|
17
|
-
if (existsSync(join(cwd, 'pnpm-lock.yaml'))) {
|
|
18
|
-
installOptions.pnpm = true;
|
|
10
|
+
let installType = 'npm';
|
|
11
|
+
if (installer) {
|
|
12
|
+
installType = installer;
|
|
13
|
+
}
|
|
14
|
+
else if (existsSync(join(cwd, 'pnpm-lock.yaml'))) {
|
|
15
|
+
installType = 'pnpm';
|
|
19
16
|
}
|
|
20
17
|
else if (existsSync(join(cwd, 'yarn.lock'))) {
|
|
21
|
-
|
|
18
|
+
installType = 'yarn';
|
|
22
19
|
}
|
|
23
20
|
try {
|
|
24
|
-
await hydrate.install(
|
|
21
|
+
await hydrate.install(toInstallerOptions(inv, installType));
|
|
25
22
|
}
|
|
26
23
|
catch (err) {
|
|
27
24
|
// This is a temporary fix.
|
|
@@ -32,3 +29,13 @@ export async function resolveResourceDependencies(resource, transpiled) {
|
|
|
32
29
|
}
|
|
33
30
|
}
|
|
34
31
|
}
|
|
32
|
+
function toInstallerOptions(inv, type) {
|
|
33
|
+
return {
|
|
34
|
+
inventory: inv,
|
|
35
|
+
hydrateShared: false,
|
|
36
|
+
quiet: true,
|
|
37
|
+
npm: type === 'npm',
|
|
38
|
+
pnpm: type === 'pnpm',
|
|
39
|
+
yarn: type === 'yarn',
|
|
40
|
+
};
|
|
41
|
+
}
|
|
@@ -84,7 +84,7 @@ export default async function invoke(resource, payload, context, options) {
|
|
|
84
84
|
}
|
|
85
85
|
const shouldResolveDependencies = await shouldAutoResolveDependencies(resource);
|
|
86
86
|
if (shouldResolveDependencies && existingPackageJson) {
|
|
87
|
-
await resolveResourceDependencies(resource, shouldTranspile);
|
|
87
|
+
await resolveResourceDependencies(resource, { transpiled: shouldTranspile });
|
|
88
88
|
}
|
|
89
89
|
if (!existingPackageJson) {
|
|
90
90
|
createTempPackageJson(functionPath);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const createPnpmRequire: (workspaceRoot: string) => NodeJS.Require;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
export const createPnpmRequire = (workspaceRoot) => {
|
|
4
|
+
const pnpmModulesPath = path.join(workspaceRoot, 'node_modules', '.pnpm', 'node_modules');
|
|
5
|
+
// createRequire gives us Node's full module resolution algorithm rooted at
|
|
6
|
+
// the pnpm virtual store — it reads package.json exports/main and returns
|
|
7
|
+
// the absolute path to the actual entry file, not just the directory.
|
|
8
|
+
return createRequire(path.join(pnpmModulesPath, '_virtual.js'));
|
|
9
|
+
};
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import type { FunctionResource } from '../types.js';
|
|
2
|
-
export declare function transpileFunction(resource: FunctionResource
|
|
1
|
+
import type { FunctionResource, InstallerType } from '../types.js';
|
|
2
|
+
export declare function transpileFunction(resource: FunctionResource, { installer }?: {
|
|
3
|
+
installer?: InstallerType;
|
|
4
|
+
}): Promise<{
|
|
3
5
|
type: string;
|
|
4
6
|
outputDir: string;
|
|
5
7
|
warnings: string[];
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
2
|
import { mkdir, readFile, rm, stat, writeFile } from 'node:fs/promises';
|
|
3
|
-
import { createRequire } from 'node:module';
|
|
4
3
|
import path from 'node:path';
|
|
5
4
|
import { performance } from 'node:perf_hooks';
|
|
6
5
|
import { cwd } from 'node:process';
|
|
@@ -8,9 +7,10 @@ import * as find from 'empathic/find';
|
|
|
8
7
|
import { build as viteBuild } from 'vite';
|
|
9
8
|
import tsConfigPaths from 'vite-tsconfig-paths';
|
|
10
9
|
import { findFunctionEntryPoint } from '../functions/find-entry-point.js';
|
|
10
|
+
import { createPnpmRequire } from '../pnpm.js';
|
|
11
11
|
import { cleanupSourceMaps } from './cleanup-source-maps.js';
|
|
12
12
|
import { verifyHandler } from './verify-handler.js';
|
|
13
|
-
export async function transpileFunction(resource) {
|
|
13
|
+
export async function transpileFunction(resource, { installer } = {}) {
|
|
14
14
|
if (!resource.src)
|
|
15
15
|
throw new Error('Resource src is required');
|
|
16
16
|
if (!resource.name)
|
|
@@ -35,7 +35,8 @@ export async function transpileFunction(resource) {
|
|
|
35
35
|
}
|
|
36
36
|
try {
|
|
37
37
|
const viteStart = performance.now();
|
|
38
|
-
|
|
38
|
+
// Always bundle the code if pnpm usage is detected or specified by the user
|
|
39
|
+
const bundle = existsSync(path.join(cwd(), 'pnpm-workspace.yaml')) || installer === 'pnpm';
|
|
39
40
|
const result = await viteBuild({
|
|
40
41
|
root: fnRootDir,
|
|
41
42
|
logLevel: 'silent',
|
|
@@ -140,11 +141,7 @@ function logCleanupFailure(err) {
|
|
|
140
141
|
* which isn't reachable via standard Node resolution.
|
|
141
142
|
*/
|
|
142
143
|
function pnpmResolvePlugin(workspaceRoot) {
|
|
143
|
-
const
|
|
144
|
-
// createRequire gives us Node's full module resolution algorithm rooted at
|
|
145
|
-
// the pnpm virtual store — it reads package.json exports/main and returns
|
|
146
|
-
// the absolute path to the actual entry file, not just the directory.
|
|
147
|
-
const pnpmRequire = createRequire(path.join(pnpmModulesPath, '_virtual.js'));
|
|
144
|
+
const pnpmRequire = createPnpmRequire(workspaceRoot);
|
|
148
145
|
return {
|
|
149
146
|
name: 'pnpm-resolve',
|
|
150
147
|
async resolveId(source, importer, options) {
|
package/dist/utils/types.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { type BlueprintCorsOriginResource, type BlueprintDatasetResource, type BlueprintDocumentWebhookResource, type BlueprintResource, type BlueprintRobotResource, type BlueprintRoleResource } from '@sanity/blueprints';
|
|
1
|
+
import { type BlueprintCorsOriginResource, type BlueprintDatasetResource, type BlueprintDocumentWebhookResource, type BlueprintProjectResource, type BlueprintResource, type BlueprintRobotResource, type BlueprintRoleResource } from '@sanity/blueprints';
|
|
2
2
|
import type { Blueprint } from '@sanity/blueprints-parser';
|
|
3
|
-
import { SANITY_FUNCTION_DOCUMENT, SANITY_FUNCTION_MEDIA_LIBRARY_ASSET,
|
|
3
|
+
import { SANITY_FUNCTION_DOCUMENT, SANITY_FUNCTION_MEDIA_LIBRARY_ASSET, SANITY_FUNCTION_SCHEDULED } from '../constants.js';
|
|
4
4
|
export type ScopeType = 'organization' | 'project';
|
|
5
5
|
/** Result utility type */
|
|
6
6
|
export type Result<T, E = string> = {
|
|
@@ -65,6 +65,7 @@ export declare function isScheduleFunctionResource<T extends BlueprintResource>(
|
|
|
65
65
|
export declare function isLocalFunctionCollection<T extends BlueprintResource>(r: T): r is T & FunctionsCollection;
|
|
66
66
|
export declare function isScheduleEvent(e: unknown): e is FunctionResourceScheduleEvent;
|
|
67
67
|
export declare function isCorsOriginResource(r: unknown): r is BlueprintCorsOriginResource;
|
|
68
|
+
export declare function isProjectResource(r: unknown): r is BlueprintProjectResource;
|
|
68
69
|
export declare function isRobotResource(r: unknown): r is BlueprintRobotResource;
|
|
69
70
|
export declare function isRoleResource(r: unknown): r is BlueprintRoleResource;
|
|
70
71
|
export declare function isDatasetResource(r: unknown): r is BlueprintDatasetResource;
|
|
@@ -91,11 +92,11 @@ interface FunctionResourceMediaLibraryAsset extends FunctionResourceBase {
|
|
|
91
92
|
event: GroqRuleMediaLibraryFunction;
|
|
92
93
|
}
|
|
93
94
|
interface FunctionResourceSchedule extends FunctionResourceBase {
|
|
94
|
-
type: typeof
|
|
95
|
+
type: typeof SANITY_FUNCTION_SCHEDULED;
|
|
95
96
|
event: FunctionResourceScheduleEvent;
|
|
96
97
|
}
|
|
97
98
|
export interface CollectionFunction {
|
|
98
|
-
type: typeof SANITY_FUNCTION_DOCUMENT | typeof
|
|
99
|
+
type: typeof SANITY_FUNCTION_DOCUMENT | typeof SANITY_FUNCTION_SCHEDULED | typeof SANITY_FUNCTION_MEDIA_LIBRARY_ASSET;
|
|
99
100
|
src: string;
|
|
100
101
|
name: string;
|
|
101
102
|
displayName?: string;
|
|
@@ -159,7 +160,7 @@ export interface InvokeGroqPayloadOptions {
|
|
|
159
160
|
}
|
|
160
161
|
export interface InvokeSchedulePayloadOptions {
|
|
161
162
|
payload?: Record<string, unknown>;
|
|
162
|
-
event: '
|
|
163
|
+
event: 'scheduled';
|
|
163
164
|
}
|
|
164
165
|
export type InvokePayloadOptions = InvokeGroqPayloadOptions | InvokeSchedulePayloadOptions;
|
|
165
166
|
export type InvokePayloadMetadata = Pick<InvokeGroqPayloadOptions, 'event' | 'before' | 'after'> | Pick<InvokeSchedulePayloadOptions, 'event'>;
|
|
@@ -254,4 +255,6 @@ export declare interface FetchConfig {
|
|
|
254
255
|
apiHost?: string;
|
|
255
256
|
apiVersion?: string;
|
|
256
257
|
}
|
|
258
|
+
export declare const INSTALLER_OPTIONS: readonly ["npm", "pnpm", "yarn"];
|
|
259
|
+
export type InstallerType = 'npm' | 'pnpm' | 'yarn';
|
|
257
260
|
export {};
|
package/dist/utils/types.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { validateCorsOrigin, validateDataset, validateDocumentWebhook, validateRobot, validateRole, } from '@sanity/blueprints';
|
|
2
|
-
import { SANITY_FUNCTION_DOCUMENT, SANITY_FUNCTION_MEDIA_LIBRARY_ASSET,
|
|
1
|
+
import { validateCorsOrigin, validateDataset, validateDocumentWebhook, validateProject, validateRobot, validateRole, } from '@sanity/blueprints';
|
|
2
|
+
import { SANITY_FUNCTION_DOCUMENT, SANITY_FUNCTION_MEDIA_LIBRARY_ASSET, SANITY_FUNCTION_PREFIX, SANITY_FUNCTION_SCHEDULED, } from '../constants.js';
|
|
3
3
|
// type narrowing with predicate functions
|
|
4
4
|
export function isLocalFunctionResource(r) {
|
|
5
|
-
return r.type.startsWith(
|
|
5
|
+
return r.type.startsWith(SANITY_FUNCTION_PREFIX);
|
|
6
6
|
}
|
|
7
7
|
export function isDocumentFunctionResource(r) {
|
|
8
8
|
return r.type === SANITY_FUNCTION_DOCUMENT;
|
|
@@ -11,7 +11,7 @@ export function isMediaLibraryAssetFunctionResource(r) {
|
|
|
11
11
|
return r.type === SANITY_FUNCTION_MEDIA_LIBRARY_ASSET;
|
|
12
12
|
}
|
|
13
13
|
export function isScheduleFunctionResource(r) {
|
|
14
|
-
return r.type ===
|
|
14
|
+
return r.type === SANITY_FUNCTION_SCHEDULED;
|
|
15
15
|
}
|
|
16
16
|
export function isLocalFunctionCollection(r) {
|
|
17
17
|
return r.type === 'sanity.experimental.functions-collection';
|
|
@@ -22,6 +22,9 @@ export function isScheduleEvent(e) {
|
|
|
22
22
|
export function isCorsOriginResource(r) {
|
|
23
23
|
return validateCorsOrigin(r).length === 0;
|
|
24
24
|
}
|
|
25
|
+
export function isProjectResource(r) {
|
|
26
|
+
return validateProject(r).length === 0;
|
|
27
|
+
}
|
|
25
28
|
export function isRobotResource(r) {
|
|
26
29
|
return validateRobot(r).length === 0;
|
|
27
30
|
}
|
|
@@ -54,3 +57,4 @@ export var BlueprintParserErrorType;
|
|
|
54
57
|
BlueprintParserErrorType["InvalidInput"] = "invalid_input";
|
|
55
58
|
BlueprintParserErrorType["MissingParameter"] = "missing_parameter";
|
|
56
59
|
})(BlueprintParserErrorType || (BlueprintParserErrorType = {}));
|
|
60
|
+
export const INSTALLER_OPTIONS = ['npm', 'pnpm', 'yarn'];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { validateCorsOrigin, validateDataset, validateDocumentFunction, validateDocumentWebhook, validateMediaLibraryAssetFunction, validateRole,
|
|
2
|
-
import { SANITY_ACCESS_ROLE, SANITY_FUNCTION_DOCUMENT, SANITY_FUNCTION_MEDIA_LIBRARY_ASSET,
|
|
1
|
+
import { validateCorsOrigin, validateDataset, validateDocumentFunction, validateDocumentWebhook, validateMediaLibraryAssetFunction, validateProject, validateRole, validateScheduledFunction, } from '@sanity/blueprints';
|
|
2
|
+
import { SANITY_ACCESS_ROLE, SANITY_FUNCTION_DOCUMENT, SANITY_FUNCTION_MEDIA_LIBRARY_ASSET, SANITY_FUNCTION_SCHEDULED, SANITY_PROJECT, SANITY_PROJECT_CORS, SANITY_PROJECT_DATASET, SANITY_PROJECT_WEBHOOK, } from '../../constants.js';
|
|
3
3
|
export * as validate from './resource.js';
|
|
4
4
|
const RESOURCE_VALIDATORS = {
|
|
5
5
|
[SANITY_ACCESS_ROLE]: {
|
|
@@ -11,8 +11,11 @@ const RESOURCE_VALIDATORS = {
|
|
|
11
11
|
[SANITY_FUNCTION_MEDIA_LIBRARY_ASSET]: {
|
|
12
12
|
validate: validateMediaLibraryAssetFunction,
|
|
13
13
|
},
|
|
14
|
-
[
|
|
15
|
-
validate:
|
|
14
|
+
[SANITY_FUNCTION_SCHEDULED]: {
|
|
15
|
+
validate: validateScheduledFunction,
|
|
16
|
+
},
|
|
17
|
+
[SANITY_PROJECT]: {
|
|
18
|
+
validate: validateProject,
|
|
16
19
|
},
|
|
17
20
|
[SANITY_PROJECT_CORS]: {
|
|
18
21
|
validate: validateCorsOrigin,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SANITY_FUNCTION_PREFIX } from '../../constants.js';
|
|
1
2
|
import { BlueprintParserErrorType, isDocumentFunctionResource, isMediaLibraryAssetFunctionResource, isScheduleEvent, } from '../types.js';
|
|
2
3
|
export function validateFunctionName(name) {
|
|
3
4
|
// must be 3+ characters, no special characters, no spaces, allow _ and -
|
|
@@ -14,9 +15,9 @@ export function validateFunctionResource(resource) {
|
|
|
14
15
|
type: BlueprintParserErrorType.InvalidProperty,
|
|
15
16
|
});
|
|
16
17
|
}
|
|
17
|
-
if (!resource.type.startsWith(
|
|
18
|
+
if (!resource.type.startsWith(SANITY_FUNCTION_PREFIX)) {
|
|
18
19
|
errors.push({
|
|
19
|
-
message: `${msgPrefix} Resource type must start with "
|
|
20
|
+
message: `${msgPrefix} Resource type must start with "${SANITY_FUNCTION_PREFIX}"`,
|
|
20
21
|
type: BlueprintParserErrorType.InvalidType,
|
|
21
22
|
});
|
|
22
23
|
}
|