@sanity/runtime-cli 17.13.1-next.g2772647 → 17.13.1-next.g4fa31d1

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.
@@ -7,15 +7,16 @@ import { createGzip } from 'node:zlib';
7
7
  import { buildApp, buildStudio, compareDependencyVersions, } from '@sanity/cli-build/_internal/build';
8
8
  import { getLocalPackageVersion } from '@sanity/cli-core';
9
9
  import { pack } from 'tar-fs';
10
- export const APP_BUILD_SPECS = {
11
- 'sanity.studio': { build: buildStudioBundle },
12
- 'sanity.application': { build: buildApplicationBundle },
10
+ export const BUNDLE_SPECS = {
11
+ 'sanity.studio': { requiresAppId: true, build: buildStudioBundle },
12
+ 'sanity.application': { requiresAppId: true, build: buildApplicationBundle },
13
+ 'sanity.installation.config': { requiresAppId: false, build: buildInstallationConfigBundle },
13
14
  };
14
- export async function prepareApplicationAsset({ blueprintFileDir, resource, }) {
15
+ export async function prepareBundleAsset({ blueprintFileDir, resource, }) {
15
16
  // The lookup can't prove it yields the spec matching this R, so it's asserted once here.
16
- const spec = APP_BUILD_SPECS[resource.type];
17
+ const spec = BUNDLE_SPECS[resource.type];
17
18
  const appId = getAppId(resource);
18
- if (!appId) {
19
+ if (spec.requiresAppId && !appId) {
19
20
  return {
20
21
  success: false,
21
22
  async cleanup() { },
@@ -25,6 +26,15 @@ export async function prepareApplicationAsset({ blueprintFileDir, resource, }) {
25
26
  let cleanup = async () => { };
26
27
  const assetPath = String(resource.root);
27
28
  const workDir = path.resolve(blueprintFileDir, assetPath);
29
+ // Defence in depth alongside blueprints-node's own check: an absolute path or one
30
+ // that climbs above the blueprint dir would bundle files outside the project.
31
+ if (path.isAbsolute(assetPath) || path.relative(blueprintFileDir, workDir).startsWith('..')) {
32
+ return {
33
+ success: false,
34
+ cleanup,
35
+ error: `Property 'root' on resource with type '${resource.type}' must be under the blueprint directory`,
36
+ };
37
+ }
28
38
  try {
29
39
  const workDirStat = await stat(workDir);
30
40
  if (!workDirStat.isDirectory()) {
@@ -44,7 +54,7 @@ export async function prepareApplicationAsset({ blueprintFileDir, resource, }) {
44
54
  };
45
55
  }
46
56
  try {
47
- const outDir = await mkdtemp(join(tmpdir(), `app-build-`));
57
+ const outDir = await mkdtemp(join(tmpdir(), `bundle-`));
48
58
  const outFile = `${outDir}.tar.gz`;
49
59
  cleanup = async () => {
50
60
  await rm(outDir, { force: true, recursive: true });
@@ -132,24 +142,45 @@ async function buildApplicationBundle({ resource, appId, workDir, outDir, output
132
142
  if (extraWindow) {
133
143
  throw new Error(`Application resource '${resource.name}' declares more than one window view; at most one is allowed`);
134
144
  }
135
- let mismatchedPackages;
136
145
  await buildApp({
146
+ ...sharedBuildAppOptions({ appId, workDir, outDir, output, eventListener }),
137
147
  appId,
138
148
  appTitle: resource.title,
149
+ entry: window?.src,
150
+ workbenchAppId: appId,
151
+ exposes: {
152
+ views: views.map(toWorkbenchView),
153
+ webWorkers: resource.webWorkers,
154
+ },
155
+ });
156
+ // a workbench app never auto-updates, so no dependency mismatches are compared
157
+ return {};
158
+ }
159
+ async function buildInstallationConfigBundle({ resource, workDir, outDir, output, eventListener, }) {
160
+ await buildApp({
161
+ ...sharedBuildAppOptions({ appId: undefined, workDir, outDir, output, eventListener }),
162
+ appId: undefined,
163
+ appTitle: undefined,
164
+ entry: undefined,
165
+ workbenchAppId: undefined,
166
+ exposes: { config: { appType: resource.appType, fields: resource.fields } },
167
+ });
168
+ return {};
169
+ }
170
+ // Options a workbench build (application or installation config) always sets the
171
+ // same way; each caller adds only what differs (identity, entry, exposes).
172
+ function sharedBuildAppOptions({ appId, workDir, outDir, output, eventListener, }) {
173
+ return {
139
174
  autoUpdatesEnabled: false,
140
175
  checkAppId() { },
141
176
  async compareDependencyVersions(packages) {
142
- const result = await compareDependencyVersions(packages, workDir, { appId });
143
- mismatchedPackages = result.mismatched.map((m) => m.pkg);
144
- return result;
177
+ return compareDependencyVersions(packages, workDir, { appId });
145
178
  },
146
179
  determineBasePath() {
147
180
  return '/';
148
181
  },
149
- entry: window?.src,
150
182
  isWorkbenchApp: true,
151
183
  isBlueprints: true,
152
- workbenchAppId: appId,
153
184
  minify: true,
154
185
  outDir,
155
186
  output,
@@ -161,12 +192,7 @@ async function buildApplicationBundle({ resource, appId, workDir, outDir, output
161
192
  vite: undefined,
162
193
  workDir,
163
194
  eventListener,
164
- exposes: {
165
- views: views.map(toWorkbenchView),
166
- webWorkers: resource.webWorkers,
167
- },
168
- });
169
- return { mismatchedPackages };
195
+ };
170
196
  }
171
197
  // blueprints-node emits `type` (window views as `app`); workbench-cli discriminates on `surface`.
172
198
  function toWorkbenchView(view) {
@@ -43,4 +43,19 @@ export interface BlueprintApplicationResource extends BlueprintResource<Blueprin
43
43
  views?: ApplicationView[];
44
44
  webWorkers?: WebWorker[];
45
45
  }
46
+ type InstallationConfigField = {
47
+ name: string;
48
+ title: string;
49
+ src: string;
50
+ public?: boolean;
51
+ };
52
+ export interface BlueprintInstallationConfigResource extends BlueprintResource {
53
+ type: 'sanity.installation.config';
54
+ appType: 'media-library';
55
+ /** Build directory, relative to the blueprint file; contains package.json. Field `src` resolves against it. */
56
+ root: string;
57
+ fields: InstallationConfigField[];
58
+ /** Stamped by the CLI before pre-deploy; the authored shape from blueprints-node has none. */
59
+ version?: string;
60
+ }
46
61
  export {};
@@ -1,6 +1,6 @@
1
1
  import { formatDateTime, formatElapsed } from '@sanity/cli-core/dates';
2
2
  import { treeify } from 'array-treeify';
3
- import { SANITY_ACCESS_ROBOT, SANITY_ACCESS_ROLE, SANITY_APPLICATION, SANITY_FUNCTION_DOCUMENT, SANITY_FUNCTION_DURABLE, SANITY_FUNCTION_MEDIA_LIBRARY_ASSET, SANITY_FUNCTION_PUBSUB, SANITY_FUNCTION_SCHEDULED, SANITY_FUNCTION_SYNC_TAG_INVALIDATE, SANITY_PROJECT, SANITY_PROJECT_CORS, SANITY_PROJECT_DATASET, SANITY_PROJECT_WEBHOOK, SANITY_STUDIO, } from '../../constants.js';
3
+ import { SANITY_ACCESS_ROBOT, SANITY_ACCESS_ROLE, SANITY_APPLICATION, SANITY_FUNCTION_DOCUMENT, SANITY_FUNCTION_DURABLE, SANITY_FUNCTION_MEDIA_LIBRARY_ASSET, SANITY_FUNCTION_PUBSUB, SANITY_FUNCTION_SCHEDULED, SANITY_FUNCTION_SYNC_TAG_INVALIDATE, SANITY_INSTALLATION_CONFIG, SANITY_PROJECT, SANITY_PROJECT_CORS, SANITY_PROJECT_DATASET, SANITY_PROJECT_WEBHOOK, SANITY_STUDIO, } from '../../constants.js';
4
4
  import { styleText } from '../style-text.js';
5
5
  import { isCorsOriginResource, isDatasetResource, isRobotResource, isRoleResource, isStudioResource, isWebhookResource, } from '../types.js';
6
6
  import { capitalize, niceId, renderSection } from './presenters.js';
@@ -69,6 +69,10 @@ const RESOURCE_CATEGORIES = {
69
69
  label: 'Applications',
70
70
  displayNameAttribute: 'title',
71
71
  },
72
+ [SANITY_INSTALLATION_CONFIG]: {
73
+ label: 'Installation configs',
74
+ displayNameAttribute: 'name',
75
+ },
72
76
  };
73
77
  const categoryByLabel = Object.values(RESOURCE_CATEGORIES).reduce((acc, curr) => {
74
78
  acc[curr.label] = curr;
@@ -0,0 +1,4 @@
1
+ export declare const REQUEST_ID_HEADER = "x-request-id";
2
+ /** Stack PUT/POST/DELETE success bodies also carry `requestId`; error bodies do not. */
3
+ export declare function getRequestId(response?: Response, body?: unknown): string | undefined;
4
+ export declare function withRequestId(message: string, requestId?: string): string;
@@ -0,0 +1,19 @@
1
+ export const REQUEST_ID_HEADER = 'x-request-id';
2
+ /** Stack PUT/POST/DELETE success bodies also carry `requestId`; error bodies do not. */
3
+ export function getRequestId(response, body) {
4
+ const fromHeader = response?.headers.get(REQUEST_ID_HEADER);
5
+ if (fromHeader)
6
+ return fromHeader;
7
+ if (body && typeof body === 'object' && 'requestId' in body) {
8
+ const fromBody = body.requestId;
9
+ if (typeof fromBody === 'string' && fromBody)
10
+ return fromBody;
11
+ }
12
+ return undefined;
13
+ }
14
+ export function withRequestId(message, requestId) {
15
+ if (!requestId)
16
+ return message;
17
+ const suffix = `(request ID ${requestId})`;
18
+ return message.endsWith('.') ? `${message.slice(0, -1)} ${suffix}.` : `${message} ${suffix}`;
19
+ }