@sanity/runtime-cli 17.12.0 → 17.13.0-next.g06897b0

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.
Files changed (74) hide show
  1. package/README.md +21 -21
  2. package/dist/actions/blueprints/assets.d.ts +23 -20
  3. package/dist/actions/blueprints/assets.js +18 -87
  4. package/dist/actions/blueprints/logs-polling.d.ts +3 -5
  5. package/dist/actions/blueprints/logs-polling.js +4 -9
  6. package/dist/actions/blueprints/logs.d.ts +1 -2
  7. package/dist/actions/blueprints/logs.js +1 -3
  8. package/dist/actions/blueprints/operations-state.d.ts +10 -0
  9. package/dist/actions/blueprints/operations-state.js +42 -0
  10. package/dist/actions/blueprints/operations.d.ts +2 -9
  11. package/dist/actions/blueprints/operations.js +1 -15
  12. package/dist/actions/blueprints/pre-deploy.d.ts +19 -11
  13. package/dist/actions/blueprints/pre-deploy.js +95 -27
  14. package/dist/actions/blueprints/process-assets.d.ts +13 -11
  15. package/dist/actions/blueprints/process-assets.js +9 -7
  16. package/dist/actions/blueprints/stacks.d.ts +4 -4
  17. package/dist/actions/blueprints/stacks.js +2 -4
  18. package/dist/actions/blueprints/wait-for-operation.d.ts +1 -10
  19. package/dist/actions/blueprints/wait-for-operation.js +56 -101
  20. package/dist/baseCommands.js +6 -1
  21. package/dist/commands/blueprints/deploy.js +2 -0
  22. package/dist/commands/functions/dev.js +1 -1
  23. package/dist/config.d.ts +1 -0
  24. package/dist/config.js +6 -0
  25. package/dist/constants.d.ts +1 -0
  26. package/dist/constants.js +1 -0
  27. package/dist/cores/blueprints/config.js +1 -1
  28. package/dist/cores/blueprints/deploy.d.ts +2 -0
  29. package/dist/cores/blueprints/deploy.js +38 -7
  30. package/dist/cores/blueprints/destroy.js +4 -2
  31. package/dist/cores/blueprints/doctor.js +2 -2
  32. package/dist/cores/blueprints/init.js +3 -1
  33. package/dist/cores/blueprints/logs.js +11 -5
  34. package/dist/cores/blueprints/mint-deploy-token.js +2 -1
  35. package/dist/cores/functions/build.js +4 -5
  36. package/dist/cores/functions/test.js +3 -1
  37. package/dist/cores/index.js +3 -1
  38. package/dist/server/app.d.ts +2 -1
  39. package/dist/server/app.js +4 -1
  40. package/dist/server/handlers/invoke.js +1 -1
  41. package/dist/server/static/vendor/vendor.bundle.js +21791 -25197
  42. package/dist/server/static/web-socket.js +4 -2
  43. package/dist/utils/blueprints/deploy-metadata.d.ts +10 -0
  44. package/dist/utils/blueprints/deploy-metadata.js +17 -0
  45. package/dist/utils/blueprints/resolve-deployed-resources.d.ts +2 -3
  46. package/dist/utils/blueprints/resolve-deployed-resources.js +2 -2
  47. package/dist/utils/blueprints/wait-for.d.ts +60 -0
  48. package/dist/utils/blueprints/wait-for.js +115 -0
  49. package/dist/utils/display/blueprints-formatting.d.ts +1 -0
  50. package/dist/utils/display/blueprints-formatting.js +17 -3
  51. package/dist/utils/display/dates.d.ts +0 -2
  52. package/dist/utils/display/dates.js +0 -25
  53. package/dist/utils/display/functions-formatting.js +1 -1
  54. package/dist/utils/display/prompt.js +5 -2
  55. package/dist/utils/display/resources-formatting.d.ts +6 -0
  56. package/dist/utils/display/resources-formatting.js +17 -0
  57. package/dist/utils/functions/child-runtime/durable.js +2 -2
  58. package/dist/utils/functions/dependency-info.d.ts +30 -0
  59. package/dist/utils/functions/dependency-info.js +57 -0
  60. package/dist/utils/functions/invoke/assert-console.d.ts +5 -0
  61. package/dist/utils/functions/invoke/assert-console.js +60 -0
  62. package/dist/utils/functions/invoke/local.js +9 -0
  63. package/dist/utils/functions/is-folder-larger-than.d.ts +5 -0
  64. package/dist/utils/functions/is-folder-larger-than.js +38 -0
  65. package/dist/utils/functions/prepare-asset.d.ts +2 -0
  66. package/dist/utils/functions/prepare-asset.js +59 -23
  67. package/dist/utils/studios/prepare-asset.js +36 -9
  68. package/dist/utils/types.d.ts +35 -4
  69. package/dist/utils/types.js +0 -3
  70. package/oclif.manifest.json +2 -2
  71. package/package.json +17 -19
  72. package/skills/functions-dev.md +1 -1
  73. package/dist/utils/functions/get-folder-size.d.ts +0 -5
  74. package/dist/utils/functions/get-folder-size.js +0 -24
@@ -0,0 +1,38 @@
1
+ import { readdirSync, statSync } from 'node:fs';
2
+ import path from 'node:path';
3
+ /**
4
+ * Recursively validates the total size of a folder, including all nested files and subdirectories.
5
+ */
6
+ export function isFolderLargerThan(folder, maxBytes) {
7
+ if (!statSync(folder).isDirectory()) {
8
+ throw new Error('Provided path is not a directory');
9
+ }
10
+ function recurse(f, bytesSoFar) {
11
+ let totalBytes = bytesSoFar;
12
+ const fileEntries = readdirSync(f);
13
+ for (const file of fileEntries) {
14
+ const filePath = path.join(f.toString(), file);
15
+ const stats = statSync(filePath);
16
+ if (stats.isFile()) {
17
+ totalBytes += stats.size;
18
+ }
19
+ else if (stats.isDirectory()) {
20
+ const result = recurse(filePath, totalBytes);
21
+ totalBytes = result.totalBytes;
22
+ }
23
+ // return early if we have already exceeded the maximum
24
+ if (totalBytes > maxBytes) {
25
+ return {
26
+ totalBytes,
27
+ exceededMax: true,
28
+ };
29
+ }
30
+ }
31
+ return {
32
+ totalBytes,
33
+ exceededMax: false,
34
+ };
35
+ }
36
+ const result = recurse(folder, 0);
37
+ return result.exceededMax;
38
+ }
@@ -1,6 +1,8 @@
1
+ import AdmZip from 'adm-zip';
1
2
  import type { AssetPrepResult, FunctionResource, InstallerType } from '../types.js';
2
3
  export declare function prepareAsset({ blueprintFileDir, resource, installer, }: {
3
4
  blueprintFileDir: string;
4
5
  resource: FunctionResource;
5
6
  installer?: InstallerType;
6
7
  }): Promise<AssetPrepResult>;
8
+ export declare function pathToZip(path: string): Promise<AdmZip>;
@@ -1,9 +1,11 @@
1
- import { existsSync } from 'node:fs';
2
- import { rm, stat } from 'node:fs/promises';
1
+ import fs, { existsSync } from 'node:fs';
2
+ import { stat } from 'node:fs/promises';
3
3
  import path from 'node:path';
4
- import { MAX_ASSET_SIZE } from '../../constants.js';
4
+ import AdmZip from 'adm-zip';
5
+ import { CONVERT_BYTES_TO_MB, MAX_ASSET_SIZE } from '../../constants.js';
6
+ import { getDependencyInfo, takeAutoInstallManifest, } from './dependency-info.js';
5
7
  import { detectNativeModules, errorMessage } from './detect-native-modules.js';
6
- import { getFolderSize } from './get-folder-size.js';
8
+ import { isFolderLargerThan } from './is-folder-larger-than.js';
7
9
  import { resolveResourceDependencies } from './resolve-dependencies.js';
8
10
  import { shouldAutoResolveDependencies } from './should-auto-resolve-deps.js';
9
11
  import { shouldTranspileFunction } from './should-transpile.js';
@@ -15,6 +17,8 @@ export async function prepareAsset({ blueprintFileDir, resource, installer, }) {
15
17
  let functionPath = resolvedFunctionPath;
16
18
  let cleanup = async () => { };
17
19
  let wasBundled = false;
20
+ let dependenciesInstalled = false;
21
+ let autoInstall;
18
22
  const shouldTranspile = await shouldTranspileFunction(resource, functionPath);
19
23
  if (shouldTranspile.transpile) {
20
24
  try {
@@ -41,7 +45,8 @@ export async function prepareAsset({ blueprintFileDir, resource, installer, }) {
41
45
  transpiled: shouldTranspile.transpile,
42
46
  installer,
43
47
  });
44
- await removeArcAutoInstallPackageJson(functionPath);
48
+ autoInstall = await takeAutoInstallManifest(functionPath);
49
+ dependenciesInstalled = true;
45
50
  }
46
51
  if (existsSync(path.join(functionPath, 'node_modules'))) {
47
52
  const modulesPath = path.join(functionPath, 'node_modules');
@@ -51,29 +56,60 @@ export async function prepareAsset({ blueprintFileDir, resource, installer, }) {
51
56
  }
52
57
  }
53
58
  try {
54
- const size = getFolderSize(functionPath);
55
- if (size > MAX_ASSET_SIZE) {
59
+ // First check the size before trying to create the zip file
60
+ const result = isFolderLargerThan(functionPath, MAX_ASSET_SIZE);
61
+ if (result) {
56
62
  throw new Error('Resource is larger than max asset size of 200 MB.');
57
63
  }
58
- return { success: true, outputPath: functionPath, cleanup };
64
+ const zip = await pathToZip(functionPath);
65
+ // Add up the uncompressed sizes of all entries to get a more accurate size of the asset being uploaded
66
+ const zipEntrySize = zip
67
+ .getEntries()
68
+ .map((entry) => entry.header.size)
69
+ .reduce((a, b) => a + b, 0);
70
+ if (zipEntrySize > MAX_ASSET_SIZE) {
71
+ throw new Error(`Asset size of ${(zipEntrySize / CONVERT_BYTES_TO_MB).toFixed(2)} MB exceeds the maximum allowed size of 200 MB.`);
72
+ }
73
+ return {
74
+ success: true,
75
+ outputBuffer: await zip.toBufferPromise(),
76
+ fileExtension: 'zip',
77
+ assetParameter: 'src',
78
+ cleanup,
79
+ metadata: {
80
+ transpiled: shouldTranspile.transpile,
81
+ bundled: wasBundled,
82
+ dependenciesInstalled,
83
+ ...(await getDependencyInfo({
84
+ sourceDir: resolvedFunctionPath,
85
+ autoInstall,
86
+ bundled: wasBundled,
87
+ })),
88
+ assetBytes: zipEntrySize,
89
+ },
90
+ };
59
91
  }
60
92
  catch (err) {
61
93
  return { success: false, cleanup, error: err instanceof Error ? err.message : `${err}` };
62
94
  }
63
95
  }
64
- /**
65
- * Remove file that contains the current date to prevent the hash from changing in the final asset.
66
- * @param functionPath The path to the built function
67
- */
68
- async function removeArcAutoInstallPackageJson(functionPath) {
69
- const arcAutoInstallPackageJson = path.join(functionPath, 'node_modules', '_arc-autoinstall', 'package.json');
70
- try {
71
- const fileStat = await stat(arcAutoInstallPackageJson);
72
- if (fileStat.isFile()) {
73
- await rm(arcAutoInstallPackageJson);
74
- }
75
- }
76
- catch {
77
- // ignore errors
78
- }
96
+ export async function pathToZip(path) {
97
+ const stats = await stat(path);
98
+ const zip = new AdmZip(undefined, {
99
+ fs: {
100
+ ...fs,
101
+ // @ts-expect-error monkey patching to return matching mtime for builds
102
+ statSync(path, options) {
103
+ const s = fs.statSync(path, options);
104
+ if (s)
105
+ s.mtime = new Date(0);
106
+ return s;
107
+ },
108
+ },
109
+ });
110
+ if (stats.isDirectory())
111
+ zip.addLocalFolder(path);
112
+ else
113
+ zip.addLocalFile(path, '', 'index.js');
114
+ return zip;
79
115
  }
@@ -1,16 +1,16 @@
1
1
  import { createWriteStream } from 'node:fs';
2
- import { mkdtemp, rm, stat } from 'node:fs/promises';
2
+ import { mkdtemp, readFile, rm, stat } from 'node:fs/promises';
3
3
  import { tmpdir } from 'node:os';
4
4
  import path, { basename, dirname, join } from 'node:path';
5
5
  import { pipeline } from 'node:stream/promises';
6
6
  import { createGzip } from 'node:zlib';
7
7
  import { buildStudio, compareDependencyVersions } from '@sanity/cli-build/_internal/build';
8
+ import { getLocalPackageVersion } from '@sanity/cli-core';
8
9
  import { pack } from 'tar-fs';
9
10
  export async function prepareStudioAsset({ blueprintFileDir, resource, }) {
10
- // validate resource has an externalId
11
- if (!('externalId' in resource) ||
12
- typeof resource.externalId !== 'string' ||
13
- !resource.externalId) {
11
+ // validate resource has an appId
12
+ const appId = getAppId(resource);
13
+ if (!appId) {
14
14
  return {
15
15
  success: false,
16
16
  async cleanup() {
@@ -23,6 +23,8 @@ export async function prepareStudioAsset({ blueprintFileDir, resource, }) {
23
23
  // no op
24
24
  };
25
25
  const studioPath = path.resolve(blueprintFileDir, resource.src);
26
+ // set only when auto-updates is on, because the build compares versions only then
27
+ let mismatchedPackages;
26
28
  try {
27
29
  const studioPathStat = await stat(studioPath);
28
30
  if (!studioPathStat.isDirectory()) {
@@ -48,14 +50,15 @@ export async function prepareStudioAsset({ blueprintFileDir, resource, }) {
48
50
  await rm(outDir, { force: true, recursive: true });
49
51
  await rm(outFile, { force: true });
50
52
  };
51
- const appId = resource.externalId;
52
53
  try {
53
54
  await buildStudio({
54
55
  appId,
55
56
  autoUpdatesEnabled: resource.autoUpdates.enabled,
56
57
  checkAppId() { },
57
58
  async compareDependencyVersions(packages) {
58
- return compareDependencyVersions(packages, studioPath, { appId });
59
+ const result = await compareDependencyVersions(packages, studioPath, { appId });
60
+ mismatchedPackages = result.mismatched.map((m) => m.pkg);
61
+ return result;
59
62
  },
60
63
  determineBasePath() {
61
64
  return resource.basePath ?? '/';
@@ -105,11 +108,18 @@ export async function prepareStudioAsset({ blueprintFileDir, resource, }) {
105
108
  }
106
109
  // tar/gzip/write the built output
107
110
  await pipeline(pack(dirname(outDir), { entries: [basename(outDir)] }), createGzip(), createWriteStream(outFile));
111
+ const buffer = await readFile(outFile);
108
112
  return {
109
113
  success: true,
110
114
  cleanup,
111
- // studio must be pre built (e.g. with `npm run build`) and tarred/gzipped
112
- outputPath: outFile,
115
+ outputBuffer: buffer,
116
+ fileExtension: 'tar.gz',
117
+ assetParameter: 'src',
118
+ metadata: {
119
+ sanityVersion: await getInstalledSanityVersion(studioPath),
120
+ mismatchedPackages,
121
+ assetBytes: buffer.length,
122
+ },
113
123
  };
114
124
  }
115
125
  catch (error) {
@@ -137,3 +147,20 @@ function createSilentOutput() {
137
147
  },
138
148
  };
139
149
  }
150
+ async function getInstalledSanityVersion(studioPath) {
151
+ try {
152
+ return (await getLocalPackageVersion('sanity', studioPath)) ?? undefined;
153
+ }
154
+ catch {
155
+ return undefined;
156
+ }
157
+ }
158
+ function getAppId(resource) {
159
+ if ('externalId' in resource && typeof resource.externalId === 'string' && resource.externalId) {
160
+ return resource.externalId;
161
+ }
162
+ else if (resource.lifecycle?.ownershipAction?.type === 'attach') {
163
+ return resource.lifecycle.ownershipAction.id;
164
+ }
165
+ return undefined;
166
+ }
@@ -20,8 +20,6 @@ export type FunctionResource = FunctionGroqResource | BlueprintPubSubFunctionRes
20
20
  export type EventFunctionResource = Exclude<FunctionResource, BlueprintPubSubFunctionResource>;
21
21
  /** The `event` of any function resource that declares one */
22
22
  export type FunctionResourceEvent = EventFunctionResource['event'];
23
- /** Resource types that carry a deployable asset (have src: string) */
24
- export type AssetResource = FunctionResource | BlueprintStudioResource;
25
23
  /** @internal */
26
24
  export interface AuthParams {
27
25
  token: string;
@@ -52,7 +50,6 @@ export declare function isRoleResource(r: unknown): r is BlueprintRoleResource;
52
50
  export declare function isDatasetResource(r: unknown): r is BlueprintDatasetResource;
53
51
  export declare function isStudioResource(r: BlueprintResource): r is BlueprintStudioResource;
54
52
  export declare function isDurableFunctionResource(r: unknown): r is BlueprintDurableFunctionResource;
55
- export declare function isAssetResource(r: BlueprintResource): r is AssetResource;
56
53
  export declare function isWebhookResource(r: unknown): r is BlueprintDocumentWebhookResource;
57
54
  interface StackBase {
58
55
  id: string;
@@ -90,7 +87,22 @@ export interface StackMutation {
90
87
  };
91
88
  userMessage?: string;
92
89
  preDeploy?: boolean;
90
+ metadata?: DeployMetadata;
93
91
  }
92
+ /**
93
+ * Facts about one deploy, stored with the Blueprint on the server.
94
+ * The API accepts it on stack update and never returns it.
95
+ */
96
+ export interface DeployMetadata {
97
+ /** The environment that ran the deploy */
98
+ client?: Record<string, unknown>;
99
+ /** One entry per resource that had an asset prepared, plus what was learned while preparing it */
100
+ resources?: ResourceMetadata[];
101
+ }
102
+ export type ResourceMetadata = {
103
+ name: string;
104
+ type: string;
105
+ } & Record<string, unknown>;
94
106
  /** @internal */
95
107
  export interface BuildPayloadOptions {
96
108
  data?: string;
@@ -301,13 +313,32 @@ export declare const INSTALLER_OPTIONS: readonly ['npm', 'pnpm', 'yarn'];
301
313
  export type InstallerType = 'npm' | 'pnpm' | 'yarn';
302
314
  export type AssetPrepResult = {
303
315
  success: true;
304
- outputPath: string;
316
+ outputBuffer: Buffer;
317
+ fileExtension: string;
318
+ assetParameter: string;
305
319
  cleanup: () => Promise<void>;
320
+ /** Facts learned about the resource while preparing its asset. Sent as deploy metadata. */
321
+ metadata?: Record<string, unknown>;
306
322
  } | {
307
323
  success: false;
308
324
  cleanup: () => Promise<void>;
309
325
  error: string;
310
326
  };
327
+ export type PrepAssetFn<R extends BlueprintResource> = (options: {
328
+ resource: R;
329
+ blueprintFileDir: string;
330
+ installer?: InstallerType;
331
+ }) => Promise<AssetPrepResult>;
332
+ export type PreDeployConfig = {
333
+ /** The resource type this config applies to or a function that returns true if this config applies to the given type. */
334
+ resourceType: string | ((type: string) => boolean);
335
+ /** Function that takes a resource and returns a buffer to be sent to blueprints. */
336
+ prepareAsset?: PrepAssetFn<BlueprintResource>;
337
+ };
338
+ export type AssetResource<R extends BlueprintResource> = {
339
+ resource: R;
340
+ prepareAsset: PrepAssetFn<R>;
341
+ };
311
342
  export type Warning = {
312
343
  code: string;
313
344
  suggestions: string[];
@@ -31,9 +31,6 @@ export function isStudioResource(r) {
31
31
  export function isDurableFunctionResource(r) {
32
32
  return validateDurableFunction(r).length === 0;
33
33
  }
34
- export function isAssetResource(r) {
35
- return isLocalFunctionResource(r) || isStudioResource(r);
36
- }
37
34
  export function isWebhookResource(r) {
38
35
  return validateDocumentWebhook(r).length === 0;
39
36
  }
@@ -2042,7 +2042,7 @@
2042
2042
  "functions:dev": {
2043
2043
  "aliases": [],
2044
2044
  "args": {},
2045
- "description": "Runs a local, web-based development server to test your functions before deploying.\n\nOpen the emulator in your browser to interactively test your functions with the payload editor.\n\nOptionally, set the host and port with the --host and --port flags. Port 8974 is reserved for the emulator's live-reload WebSocket server. Function timeout can be configured with the --timeout flag.\n\nTo invoke a function with the CLI, use 'functions test'.",
2045
+ "description": "Runs a local, web-based development server to test your functions before deploying.\n\nOpen the emulator in your browser to interactively test your functions with the payload editor.\n\nOptionally, set the host and port with the --host and --port flags. The live-reload WebSocket server shares this host and port. Function timeout can be configured with the --timeout flag.\n\nTo invoke a function with the CLI, use 'functions test'.",
2046
2046
  "examples": [
2047
2047
  "<%= config.bin %> <%= command.id %> --host 127.0.0.1 --port 3333",
2048
2048
  "<%= config.bin %> <%= command.id %> --timeout 60"
@@ -3148,5 +3148,5 @@
3148
3148
  ]
3149
3149
  }
3150
3150
  },
3151
- "version": "17.12.0"
3151
+ "version": "17.13.0-next.g06897b0"
3152
3152
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sanity/runtime-cli",
3
3
  "description": "Sanity's Runtime CLI for Blueprints and Functions",
4
- "version": "17.12.0",
4
+ "version": "17.13.0-next.g06897b0",
5
5
  "author": "Sanity Runtime Team",
6
6
  "type": "module",
7
7
  "license": "MIT",
@@ -34,8 +34,8 @@
34
34
  "sanity-run": "bin/run.js"
35
35
  },
36
36
  "scripts": {
37
- "build": "npm run build:rollup && npm run build:ts && npm run build:static && npm run build:oclif",
38
- "build:rollup": "rollup -c",
37
+ "build": "npm run build:vendor && npm run build:ts && npm run build:static && npm run build:oclif",
38
+ "build:vendor": "vite build --config vite.vendor.config.mjs",
39
39
  "build:oclif": "oclif manifest && oclif readme",
40
40
  "build:ts": "shx rm -rf dist *.tsbuildinfo && tsc -b",
41
41
  "build:static": "npm run copy:wrapper && npm run copy:design-tokens && npm run copy:server",
@@ -52,7 +52,7 @@
52
52
  "postpack": "shx rm -f oclif.manifest.json",
53
53
  "test": "npm run typecheck && vitest run && npm run test:depmgmt && npm run lint",
54
54
  "test:depmgmt": "vitest run --config ./test-depmgmt/vitest.config.ts",
55
- "test:coverage": "vitest run --coverage --coverage.include \"runtime-cli/src/**/*\"",
55
+ "test:coverage": "vitest run --coverage --coverage.include \"src/**/*\" --coverage.exclude \"src/server/static/**\"",
56
56
  "test:scenarios": "vitest run test/scenarios",
57
57
  "test:playwright": "playwright install chromium && playwright install-deps chromium && playwright test",
58
58
  "test:bp-version-smoke": "node ./test/blueprints-version-smoke.js",
@@ -64,16 +64,16 @@
64
64
  "dependencies": {
65
65
  "@architect/hydrate": "~6.0.2",
66
66
  "@architect/inventory": "~6.1.0",
67
- "@inquirer/prompts": "^8.7.0",
67
+ "@inquirer/prompts": "^8.7.2",
68
68
  "@oclif/core": "^4.14.0",
69
69
  "@oclif/plugin-help": "^6.3.0",
70
70
  "@sanity-labs/oclif-plugin-skills-flag": "^0.2.1",
71
71
  "@sanity/blueprints": "^0.24.0",
72
72
  "@sanity/blueprints-parser": "^0.5.0",
73
- "@sanity/cli-build": "^5.2.0",
74
- "@sanity/cli-core": "^3.5.0",
75
- "@sanity/client": "^8.4.0",
76
- "adm-zip": "^0.6.0",
73
+ "@sanity/cli-build": "^6.3.0",
74
+ "@sanity/cli-core": "^3.7.0",
75
+ "@sanity/client": "^8.6.1",
76
+ "adm-zip": "^0.6.1",
77
77
  "array-treeify": "^0.2.0",
78
78
  "cardinal": "^2.1.1",
79
79
  "empathic": "^2.0.1",
@@ -86,37 +86,35 @@
86
86
  "strip-ansi": "^7.2.0",
87
87
  "tar-fs": "^3.1.3",
88
88
  "tar-stream": "^3.2.1",
89
- "vite": "^8.2.2",
89
+ "vite": "^8.3.0",
90
90
  "ws": "^8.21.3",
91
91
  "xdg-basedir": "^5.1.0"
92
92
  },
93
93
  "devDependencies": {
94
- "@biomejs/biome": "2.5.11",
94
+ "@biomejs/biome": "2.5.13",
95
95
  "@codemirror/lang-json": "^6.0.2",
96
96
  "@codemirror/language": "^6.12.4",
97
- "@codemirror/state": "^6.7.1",
97
+ "@codemirror/state": "^6.7.4",
98
98
  "@enhance/store": "^1.0.2",
99
99
  "@lezer/highlight": "^1.2.3",
100
100
  "@oclif/test": "^4.2.0",
101
- "@playwright/test": "^1.62.1",
102
- "@rollup/plugin-node-resolve": "^16.0.3",
101
+ "@playwright/test": "^1.63.0",
103
102
  "@sanity-labs/design-tokens": "^0.0.2-alpha.2",
104
- "@sanity/functions": "^1.7.1",
103
+ "@sanity/functions": "^1.7.2",
105
104
  "@types/cardinal": "^2.1.1",
106
105
  "@types/mime-types": "^3.0.1",
107
106
  "@types/node": "^22",
108
107
  "@types/tar-fs": "^2.0.4",
109
108
  "@types/ws": "^8.18.1",
110
- "@vitest/coverage-v8": "4.1.11",
109
+ "@vitest/coverage-v8": "5.0.0",
111
110
  "codemirror": "^6.0.2",
112
111
  "mentoss": "^0.13.0",
113
112
  "oclif": "^4.24.0",
114
- "pretty-bytes": "^7.1.2",
113
+ "pretty-bytes": "^7.1.3",
115
114
  "pretty-ms": "^9.3.1",
116
- "rollup": "^4.63.1",
117
115
  "shx": "^0.4.0",
118
116
  "typescript": "^7.0.2",
119
- "vitest": "4.1.11"
117
+ "vitest": "5.0.0"
120
118
  },
121
119
  "oclif": {
122
120
  "bin": "sanity-run",
@@ -16,6 +16,6 @@ The emulator is available at `http://localhost:8080`. Choose another loopback po
16
16
  npx @sanity/cli functions dev --host 127.0.0.1 --port 3333 --timeout 60
17
17
  ```
18
18
 
19
- Port 8974 is not available: the emulator's WebSocket server binds it. `--timeout` sets the execution limit in seconds for every Function, taking precedence over each Function's configured `timeout` (default 10 seconds). Bind to a non-loopback interface only when network access is requested and exposing the development server is acceptable.
19
+ The emulator's live-reload WebSocket server shares the same host and port, so no additional port is reserved. `--timeout` sets the execution limit in seconds for every Function, taking precedence over each Function's configured `timeout` (default 10 seconds). Bind to a non-loopback interface only when network access is requested and exposing the development server is acceptable.
20
20
 
21
21
  Run this command in a persistent terminal, report the URL, and stop the process when development is complete. For one bounded invocation without a web server, use `functions test`.
@@ -1,5 +0,0 @@
1
- import { type PathLike } from 'node:fs';
2
- /**
3
- * Recursively calculates the total size of a folder, including all nested files and subdirectories.
4
- */
5
- export declare const getFolderSize: (folder: PathLike) => number;
@@ -1,24 +0,0 @@
1
- import { readdirSync, statSync } from 'node:fs';
2
- import path from 'node:path';
3
- /**
4
- * Recursively calculates the total size of a folder, including all nested files and subdirectories.
5
- */
6
- export const getFolderSize = (folder) => {
7
- if (!statSync(folder).isDirectory()) {
8
- throw new Error('Provided path is not a directory');
9
- }
10
- let totalSize = 0;
11
- const fileEntries = readdirSync(folder);
12
- for (const file of fileEntries) {
13
- const filePath = path.join(folder.toString(), file);
14
- const stats = statSync(filePath);
15
- if (stats.isFile()) {
16
- totalSize += stats.size;
17
- }
18
- else if (stats.isDirectory()) {
19
- const recursiveSize = getFolderSize(filePath);
20
- totalSize += recursiveSize;
21
- }
22
- }
23
- return totalSize;
24
- };