@sanity/runtime-cli 17.0.0 → 17.1.0-next.g3cb2d2e
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 +21 -21
- package/dist/actions/functions/test.js +1 -1
- package/dist/baseCommands.js +13 -2
- package/dist/config.d.ts +1 -0
- package/dist/config.js +7 -1
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +1 -0
- package/dist/cores/functions/test.js +44 -0
- package/dist/server/app.js +31 -3
- package/dist/server/handlers/invoke.d.ts +2 -2
- package/dist/server/handlers/invoke.js +7 -4
- package/dist/server/static/components/console-panel.js +59 -8
- package/dist/server/static/components/rule-panel.js +11 -8
- package/dist/server/static/components/shared-styles.js +24 -21
- package/dist/server/static/index.html +1 -1
- package/dist/server/static/vendor/vendor.bundle.js +110 -61
- package/dist/server/static/web-socket.js +41 -0
- package/dist/utils/functions/child-process-wrapper.js +62 -7
- package/dist/utils/functions/{invoke-local.d.ts → invoke/local.d.ts} +1 -1
- package/dist/utils/functions/invoke/local.js +306 -0
- package/dist/utils/functions/invoke/nested.d.ts +25 -0
- package/dist/utils/functions/invoke/nested.js +41 -0
- package/dist/utils/functions/invoke/prepare.d.ts +20 -0
- package/dist/utils/functions/invoke/prepare.js +90 -0
- package/dist/utils/isTrueish.d.ts +6 -0
- package/dist/utils/isTrueish.js +9 -0
- package/dist/utils/types.d.ts +62 -4
- package/oclif.manifest.json +1 -1
- package/package.json +6 -2
- package/dist/server/static/hot-reload.js +0 -16
- package/dist/utils/functions/invoke-local.js +0 -185
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ $ npm install -g @sanity/runtime-cli
|
|
|
20
20
|
$ sanity-run COMMAND
|
|
21
21
|
running command...
|
|
22
22
|
$ sanity-run (--version)
|
|
23
|
-
@sanity/runtime-cli/17.0.
|
|
23
|
+
@sanity/runtime-cli/17.1.0-next.g3cb2d2e linux-x64 node-v24.18.0
|
|
24
24
|
$ sanity-run --help [COMMAND]
|
|
25
25
|
USAGE
|
|
26
26
|
$ sanity-run COMMAND
|
|
@@ -103,7 +103,7 @@ EXAMPLES
|
|
|
103
103
|
$ sanity-run blueprints add function --name my-function --fn-type document-create --fn-type document-update --lang js
|
|
104
104
|
```
|
|
105
105
|
|
|
106
|
-
_See code: [src/commands/blueprints/add.ts](https://github.com/sanity-io/runtime-cli/blob/v17.0.
|
|
106
|
+
_See code: [src/commands/blueprints/add.ts](https://github.com/sanity-io/runtime-cli/blob/v17.1.0-next.g3cb2d2e/src/commands/blueprints/add.ts)_
|
|
107
107
|
|
|
108
108
|
## `sanity-run blueprints config`
|
|
109
109
|
|
|
@@ -140,7 +140,7 @@ EXAMPLES
|
|
|
140
140
|
$ sanity-run blueprints config --edit --project-id <projectId> --stack <name-or-id>
|
|
141
141
|
```
|
|
142
142
|
|
|
143
|
-
_See code: [src/commands/blueprints/config.ts](https://github.com/sanity-io/runtime-cli/blob/v17.0.
|
|
143
|
+
_See code: [src/commands/blueprints/config.ts](https://github.com/sanity-io/runtime-cli/blob/v17.1.0-next.g3cb2d2e/src/commands/blueprints/config.ts)_
|
|
144
144
|
|
|
145
145
|
## `sanity-run blueprints deploy`
|
|
146
146
|
|
|
@@ -194,7 +194,7 @@ EXAMPLES
|
|
|
194
194
|
$ sanity-run blueprints deploy --new-stack-name <new-name>
|
|
195
195
|
```
|
|
196
196
|
|
|
197
|
-
_See code: [src/commands/blueprints/deploy.ts](https://github.com/sanity-io/runtime-cli/blob/v17.0.
|
|
197
|
+
_See code: [src/commands/blueprints/deploy.ts](https://github.com/sanity-io/runtime-cli/blob/v17.1.0-next.g3cb2d2e/src/commands/blueprints/deploy.ts)_
|
|
198
198
|
|
|
199
199
|
## `sanity-run blueprints destroy`
|
|
200
200
|
|
|
@@ -232,7 +232,7 @@ EXAMPLES
|
|
|
232
232
|
$ sanity-run blueprints destroy --stack <name-or-id> --project-id <projectId> --force --no-wait
|
|
233
233
|
```
|
|
234
234
|
|
|
235
|
-
_See code: [src/commands/blueprints/destroy.ts](https://github.com/sanity-io/runtime-cli/blob/v17.0.
|
|
235
|
+
_See code: [src/commands/blueprints/destroy.ts](https://github.com/sanity-io/runtime-cli/blob/v17.1.0-next.g3cb2d2e/src/commands/blueprints/destroy.ts)_
|
|
236
236
|
|
|
237
237
|
## `sanity-run blueprints doctor`
|
|
238
238
|
|
|
@@ -263,7 +263,7 @@ EXAMPLES
|
|
|
263
263
|
$ sanity-run blueprints doctor --fix
|
|
264
264
|
```
|
|
265
265
|
|
|
266
|
-
_See code: [src/commands/blueprints/doctor.ts](https://github.com/sanity-io/runtime-cli/blob/v17.0.
|
|
266
|
+
_See code: [src/commands/blueprints/doctor.ts](https://github.com/sanity-io/runtime-cli/blob/v17.1.0-next.g3cb2d2e/src/commands/blueprints/doctor.ts)_
|
|
267
267
|
|
|
268
268
|
## `sanity-run blueprints info`
|
|
269
269
|
|
|
@@ -300,7 +300,7 @@ EXAMPLES
|
|
|
300
300
|
$ sanity-run blueprints info --organization-id <orgId> --stack <name-or-id>
|
|
301
301
|
```
|
|
302
302
|
|
|
303
|
-
_See code: [src/commands/blueprints/info.ts](https://github.com/sanity-io/runtime-cli/blob/v17.0.
|
|
303
|
+
_See code: [src/commands/blueprints/info.ts](https://github.com/sanity-io/runtime-cli/blob/v17.1.0-next.g3cb2d2e/src/commands/blueprints/info.ts)_
|
|
304
304
|
|
|
305
305
|
## `sanity-run blueprints init [DIR]`
|
|
306
306
|
|
|
@@ -364,7 +364,7 @@ EXAMPLES
|
|
|
364
364
|
$ sanity-run blueprints init old-stack --type <json|js|ts> --project-id <projectId> --stack-id <existingStackId>
|
|
365
365
|
```
|
|
366
366
|
|
|
367
|
-
_See code: [src/commands/blueprints/init.ts](https://github.com/sanity-io/runtime-cli/blob/v17.0.
|
|
367
|
+
_See code: [src/commands/blueprints/init.ts](https://github.com/sanity-io/runtime-cli/blob/v17.1.0-next.g3cb2d2e/src/commands/blueprints/init.ts)_
|
|
368
368
|
|
|
369
369
|
## `sanity-run blueprints logs`
|
|
370
370
|
|
|
@@ -410,7 +410,7 @@ EXAMPLES
|
|
|
410
410
|
$ sanity-run blueprints logs --before 2026-05-01T00:00:00Z
|
|
411
411
|
```
|
|
412
412
|
|
|
413
|
-
_See code: [src/commands/blueprints/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v17.0.
|
|
413
|
+
_See code: [src/commands/blueprints/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v17.1.0-next.g3cb2d2e/src/commands/blueprints/logs.ts)_
|
|
414
414
|
|
|
415
415
|
## `sanity-run blueprints mint-deploy-token`
|
|
416
416
|
|
|
@@ -455,7 +455,7 @@ EXAMPLES
|
|
|
455
455
|
$ sanity-run blueprints mint-deploy-token --organization-id <orgId>
|
|
456
456
|
```
|
|
457
457
|
|
|
458
|
-
_See code: [src/commands/blueprints/mint-deploy-token.ts](https://github.com/sanity-io/runtime-cli/blob/v17.0.
|
|
458
|
+
_See code: [src/commands/blueprints/mint-deploy-token.ts](https://github.com/sanity-io/runtime-cli/blob/v17.1.0-next.g3cb2d2e/src/commands/blueprints/mint-deploy-token.ts)_
|
|
459
459
|
|
|
460
460
|
## `sanity-run blueprints plan`
|
|
461
461
|
|
|
@@ -488,7 +488,7 @@ EXAMPLES
|
|
|
488
488
|
$ sanity-run blueprints plan --organization-id <orgId> --stack <name-or-id>
|
|
489
489
|
```
|
|
490
490
|
|
|
491
|
-
_See code: [src/commands/blueprints/plan.ts](https://github.com/sanity-io/runtime-cli/blob/v17.0.
|
|
491
|
+
_See code: [src/commands/blueprints/plan.ts](https://github.com/sanity-io/runtime-cli/blob/v17.1.0-next.g3cb2d2e/src/commands/blueprints/plan.ts)_
|
|
492
492
|
|
|
493
493
|
## `sanity-run blueprints promote`
|
|
494
494
|
|
|
@@ -524,7 +524,7 @@ EXAMPLES
|
|
|
524
524
|
$ sanity-run blueprints promote --new-stack-name <new-name>
|
|
525
525
|
```
|
|
526
526
|
|
|
527
|
-
_See code: [src/commands/blueprints/promote.ts](https://github.com/sanity-io/runtime-cli/blob/v17.0.
|
|
527
|
+
_See code: [src/commands/blueprints/promote.ts](https://github.com/sanity-io/runtime-cli/blob/v17.1.0-next.g3cb2d2e/src/commands/blueprints/promote.ts)_
|
|
528
528
|
|
|
529
529
|
## `sanity-run blueprints stacks`
|
|
530
530
|
|
|
@@ -560,7 +560,7 @@ EXAMPLES
|
|
|
560
560
|
$ sanity-run blueprints stacks --organization-id <organizationId> --include-projects
|
|
561
561
|
```
|
|
562
562
|
|
|
563
|
-
_See code: [src/commands/blueprints/stacks.ts](https://github.com/sanity-io/runtime-cli/blob/v17.0.
|
|
563
|
+
_See code: [src/commands/blueprints/stacks.ts](https://github.com/sanity-io/runtime-cli/blob/v17.1.0-next.g3cb2d2e/src/commands/blueprints/stacks.ts)_
|
|
564
564
|
|
|
565
565
|
## `sanity-run functions add`
|
|
566
566
|
|
|
@@ -612,7 +612,7 @@ EXAMPLES
|
|
|
612
612
|
$ sanity-run functions add --name my-function --type document-create --type document-update --lang js
|
|
613
613
|
```
|
|
614
614
|
|
|
615
|
-
_See code: [src/commands/functions/add.ts](https://github.com/sanity-io/runtime-cli/blob/v17.0.
|
|
615
|
+
_See code: [src/commands/functions/add.ts](https://github.com/sanity-io/runtime-cli/blob/v17.1.0-next.g3cb2d2e/src/commands/functions/add.ts)_
|
|
616
616
|
|
|
617
617
|
## `sanity-run functions dev`
|
|
618
618
|
|
|
@@ -647,7 +647,7 @@ EXAMPLES
|
|
|
647
647
|
$ sanity-run functions dev --timeout 60
|
|
648
648
|
```
|
|
649
649
|
|
|
650
|
-
_See code: [src/commands/functions/dev.ts](https://github.com/sanity-io/runtime-cli/blob/v17.0.
|
|
650
|
+
_See code: [src/commands/functions/dev.ts](https://github.com/sanity-io/runtime-cli/blob/v17.1.0-next.g3cb2d2e/src/commands/functions/dev.ts)_
|
|
651
651
|
|
|
652
652
|
## `sanity-run functions env add NAME KEY VALUE`
|
|
653
653
|
|
|
@@ -677,7 +677,7 @@ EXAMPLES
|
|
|
677
677
|
$ sanity-run functions env add MyFunction API_URL https://api.example.com/
|
|
678
678
|
```
|
|
679
679
|
|
|
680
|
-
_See code: [src/commands/functions/env/add.ts](https://github.com/sanity-io/runtime-cli/blob/v17.0.
|
|
680
|
+
_See code: [src/commands/functions/env/add.ts](https://github.com/sanity-io/runtime-cli/blob/v17.1.0-next.g3cb2d2e/src/commands/functions/env/add.ts)_
|
|
681
681
|
|
|
682
682
|
## `sanity-run functions env list NAME`
|
|
683
683
|
|
|
@@ -704,7 +704,7 @@ EXAMPLES
|
|
|
704
704
|
$ sanity-run functions env list MyFunction
|
|
705
705
|
```
|
|
706
706
|
|
|
707
|
-
_See code: [src/commands/functions/env/list.ts](https://github.com/sanity-io/runtime-cli/blob/v17.0.
|
|
707
|
+
_See code: [src/commands/functions/env/list.ts](https://github.com/sanity-io/runtime-cli/blob/v17.1.0-next.g3cb2d2e/src/commands/functions/env/list.ts)_
|
|
708
708
|
|
|
709
709
|
## `sanity-run functions env remove NAME KEY`
|
|
710
710
|
|
|
@@ -733,7 +733,7 @@ EXAMPLES
|
|
|
733
733
|
$ sanity-run functions env remove MyFunction API_URL
|
|
734
734
|
```
|
|
735
735
|
|
|
736
|
-
_See code: [src/commands/functions/env/remove.ts](https://github.com/sanity-io/runtime-cli/blob/v17.0.
|
|
736
|
+
_See code: [src/commands/functions/env/remove.ts](https://github.com/sanity-io/runtime-cli/blob/v17.1.0-next.g3cb2d2e/src/commands/functions/env/remove.ts)_
|
|
737
737
|
|
|
738
738
|
## `sanity-run functions logs [NAME]`
|
|
739
739
|
|
|
@@ -773,7 +773,7 @@ EXAMPLES
|
|
|
773
773
|
$ sanity-run functions logs <name> --delete
|
|
774
774
|
```
|
|
775
775
|
|
|
776
|
-
_See code: [src/commands/functions/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v17.0.
|
|
776
|
+
_See code: [src/commands/functions/logs.ts](https://github.com/sanity-io/runtime-cli/blob/v17.1.0-next.g3cb2d2e/src/commands/functions/logs.ts)_
|
|
777
777
|
|
|
778
778
|
## `sanity-run functions test [NAME]`
|
|
779
779
|
|
|
@@ -830,7 +830,7 @@ EXAMPLES
|
|
|
830
830
|
$ sanity-run functions test <name> --event update --data-before '{ "title": "before" }' --data-after '{ "title": "after" }'
|
|
831
831
|
```
|
|
832
832
|
|
|
833
|
-
_See code: [src/commands/functions/test.ts](https://github.com/sanity-io/runtime-cli/blob/v17.0.
|
|
833
|
+
_See code: [src/commands/functions/test.ts](https://github.com/sanity-io/runtime-cli/blob/v17.1.0-next.g3cb2d2e/src/commands/functions/test.ts)_
|
|
834
834
|
|
|
835
835
|
## `sanity-run help [COMMAND]`
|
|
836
836
|
|
|
@@ -850,5 +850,5 @@ DESCRIPTION
|
|
|
850
850
|
Display help for sanity-run.
|
|
851
851
|
```
|
|
852
852
|
|
|
853
|
-
_See code: [@oclif/plugin-help](https://github.com/oclif/plugin-help/blob/6.2.
|
|
853
|
+
_See code: [@oclif/plugin-help](https://github.com/oclif/plugin-help/blob/6.2.53/src/commands/help.ts)_
|
|
854
854
|
<!-- commandsstop -->
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import invoke from '../../utils/functions/invoke
|
|
1
|
+
import invoke from '../../utils/functions/invoke/local.js';
|
|
2
2
|
export async function testAction(resource, payload, context, options) {
|
|
3
3
|
try {
|
|
4
4
|
const { json, logs, error } = await invoke(resource, payload, context, options);
|
package/dist/baseCommands.js
CHANGED
|
@@ -4,6 +4,8 @@ import { findBlueprintFile, loadBlueprintFile, parseBlueprintContent, } from './
|
|
|
4
4
|
import { backfillProjectBasedStackId, readConfigFile, } from './actions/blueprints/config.js';
|
|
5
5
|
import { resolveIds } from './actions/blueprints/resolve.js';
|
|
6
6
|
import { getStack, resolveStackIdByNameOrId } from './actions/blueprints/stacks.js';
|
|
7
|
+
import config from './config.js';
|
|
8
|
+
import { CI_SETUP_DOCS_URL } from './constants.js';
|
|
7
9
|
import { presentBlueprintParserErrors } from './utils/display/errors.js';
|
|
8
10
|
import { Logger } from './utils/logger.js';
|
|
9
11
|
import { styleText } from './utils/style-text.js';
|
|
@@ -319,10 +321,15 @@ export class ResolvedCommand extends RuntimeCommand {
|
|
|
319
321
|
// 5. Scope
|
|
320
322
|
if (needsScope) {
|
|
321
323
|
if (!resolved.scopeType || !resolved.scopeId) {
|
|
322
|
-
|
|
324
|
+
// In CI environments, since .sanity/blueprint.config.json is not guaranteed to exist, we can provide a more specific hint
|
|
325
|
+
const errorMessage = config.isCi
|
|
326
|
+
? 'set SANITY_PROJECT_ID or SANITY_ORGANIZATION_ID.'
|
|
327
|
+
: 'provide --project-id or --organization-id, or run `blueprints init` to create a Blueprint config (.sanity/blueprint.config.json).';
|
|
328
|
+
this.error(`Missing scope: ${errorMessage}`, {
|
|
323
329
|
suggestions: [
|
|
324
330
|
`Run \`${this.config.bin} blueprints doctor\` to check your configuration.`,
|
|
325
331
|
],
|
|
332
|
+
ref: CI_SETUP_DOCS_URL,
|
|
326
333
|
});
|
|
327
334
|
}
|
|
328
335
|
this.scopeType = resolved.scopeType;
|
|
@@ -341,10 +348,14 @@ export class ResolvedCommand extends RuntimeCommand {
|
|
|
341
348
|
stackId = await resolveStackIdByNameOrId(stackOverride, auth, log);
|
|
342
349
|
}
|
|
343
350
|
if (!stackId) {
|
|
344
|
-
|
|
351
|
+
const errorMessage = config.isCi
|
|
352
|
+
? "couldn't determine which Stack to target. Set SANITY_BLUEPRINT_STACK_ID."
|
|
353
|
+
: `provide --stack, or set a Stack in your Blueprint config (\`${this.config.bin} blueprints config --edit\`).`;
|
|
354
|
+
this.error(`Missing Stack: ${errorMessage}`, {
|
|
345
355
|
suggestions: [
|
|
346
356
|
`Run \`${this.config.bin} blueprints doctor\` to check your configuration.`,
|
|
347
357
|
],
|
|
358
|
+
ref: CI_SETUP_DOCS_URL,
|
|
348
359
|
});
|
|
349
360
|
}
|
|
350
361
|
this.stackId = stackId;
|
package/dist/config.d.ts
CHANGED
package/dist/config.js
CHANGED
|
@@ -3,6 +3,7 @@ import { env } from 'node:process';
|
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
4
|
import * as pkg from 'empathic/package';
|
|
5
5
|
import getToken from './utils/get-token.js';
|
|
6
|
+
import isTrueish from './utils/isTrueish.js';
|
|
6
7
|
export const BLUEPRINT_CONFIG_VERSION = 'v2025-05-08';
|
|
7
8
|
// Default API version for Blueprints service calls.
|
|
8
9
|
// Overridable globally via env, or per-endpoint via the `version` arg to `blueprintsUrl`.
|
|
@@ -29,7 +30,11 @@ const isTest = nodeEnv === 'test';
|
|
|
29
30
|
const sanityEnv = env.SANITY_INTERNAL_ENV?.toLowerCase() ?? 'production';
|
|
30
31
|
const sanityProd = sanityEnv === 'production';
|
|
31
32
|
const isLive = sanityProd || sanityEnv === 'staging';
|
|
32
|
-
const isPublishing = env?.PUBLISHING
|
|
33
|
+
const isPublishing = isTrueish(env?.PUBLISHING);
|
|
34
|
+
// https://github.com/sanity-io/cli/blob/adf90ea6691ee6c36849e426de9729533706f47f/packages/%40sanity/cli-core/src/util/isCi.ts
|
|
35
|
+
const isCi =
|
|
36
|
+
// Travis CI, CircleCI, Gitlab CI, Appveyor, CodeShip, Travis CI, Jenkins, TeamCity
|
|
37
|
+
isTrueish(env?.CI) || isTrueish(env?.CONTINUOUS_INTEGRATION) || isTrueish(env?.BUILD_NUMBER);
|
|
33
38
|
const apiUrls = {
|
|
34
39
|
production: 'https://api.sanity.io/',
|
|
35
40
|
staging: 'https://api.sanity.work/',
|
|
@@ -74,6 +79,7 @@ export default {
|
|
|
74
79
|
isLive,
|
|
75
80
|
apiUrl,
|
|
76
81
|
populusApiUrl,
|
|
82
|
+
isCi,
|
|
77
83
|
get token() {
|
|
78
84
|
return _token === undefined ? loadToken() : _token;
|
|
79
85
|
},
|
package/dist/constants.d.ts
CHANGED
|
@@ -43,3 +43,4 @@ export declare const LABEL_MEDIA_LIBRARY_ASSET_UPDATE = "Media Library Asset Upd
|
|
|
43
43
|
export declare const LABEL_MEDIA_LIBRARY_ASSET_DELETE = "Media Library Asset Delete";
|
|
44
44
|
export declare const LABEL_SCHEDULED = "Scheduled";
|
|
45
45
|
export declare const LABEL_SYNC_TAG_INVALIDATE = "Sync Tag Invalidate";
|
|
46
|
+
export declare const CI_SETUP_DOCS_URL = "https://www.sanity.io/docs/blueprints/deploy-blueprints-from-ci";
|
package/dist/constants.js
CHANGED
|
@@ -48,3 +48,4 @@ export const LABEL_MEDIA_LIBRARY_ASSET_UPDATE = 'Media Library Asset Update';
|
|
|
48
48
|
export const LABEL_MEDIA_LIBRARY_ASSET_DELETE = 'Media Library Asset Delete';
|
|
49
49
|
export const LABEL_SCHEDULED = 'Scheduled';
|
|
50
50
|
export const LABEL_SYNC_TAG_INVALIDATE = 'Sync Tag Invalidate';
|
|
51
|
+
export const CI_SETUP_DOCS_URL = 'https://www.sanity.io/docs/blueprints/deploy-blueprints-from-ci';
|
|
@@ -8,6 +8,7 @@ import { resolveDeployedResources } from '../../utils/blueprints/resolve-deploye
|
|
|
8
8
|
import buildPayload from '../../utils/functions/build-payload.js';
|
|
9
9
|
import { fetchAsset, fetchDocument } from '../../utils/functions/fetch-document.js';
|
|
10
10
|
import { findFunctionInResources, getFunctionNames } from '../../utils/functions/find.js';
|
|
11
|
+
import { runFunction } from '../../utils/functions/invoke/nested.js';
|
|
11
12
|
import { parseJsonObject } from '../../utils/parse-json-object.js';
|
|
12
13
|
import { isEventType, } from '../../utils/types.js';
|
|
13
14
|
export async function functionTestCore(options) {
|
|
@@ -146,12 +147,54 @@ export async function functionTestCore(options) {
|
|
|
146
147
|
: {
|
|
147
148
|
event: 'scheduled',
|
|
148
149
|
};
|
|
150
|
+
// Records each nested context.invoke() so they can be surfaced in order after the spinner resolves.
|
|
151
|
+
const nestedRuns = [];
|
|
152
|
+
let nestedOrder = 0;
|
|
153
|
+
/**
|
|
154
|
+
* Re-entrant callback that resolves a nested context.invoke(name, payload):
|
|
155
|
+
* it finds the named function in the same blueprint, reuses the already
|
|
156
|
+
* resolved resources, and runs it via the shared invoke-chain helper (which
|
|
157
|
+
* applies the depth/cycle guard and extends the call stack). Passing itself
|
|
158
|
+
* as onNestedInvoke keeps it recursable. Every run is recorded so it can be
|
|
159
|
+
* surfaced after the spinner resolves, including failed ones — a downstream
|
|
160
|
+
* error is fire-and-forget (it doesn't reject the caller) but is still shown.
|
|
161
|
+
*/
|
|
162
|
+
const runNested = async (nestedName, nestedPayload, ctx, depth, callStack) => {
|
|
163
|
+
const order = ++nestedOrder;
|
|
164
|
+
const nestedResource = findFunctionInResources(blueprint.resources, nestedName);
|
|
165
|
+
const result = await runFunction(nestedResource, { payload: { data: nestedPayload }, event: 'nested' }, { ...ctx, resources: resolvedResources }, { depth, callStack, onNestedInvoke: runNested });
|
|
166
|
+
nestedRuns.push({ order, name: nestedName, logs: result.logs, error: result.error });
|
|
167
|
+
return result;
|
|
168
|
+
};
|
|
169
|
+
const logNestedRuns = () => {
|
|
170
|
+
for (const run of [...nestedRuns].sort((a, b) => a.order - b.order)) {
|
|
171
|
+
log(`↳ BEGIN: invoke('${run.name}')`);
|
|
172
|
+
// Show any logs received
|
|
173
|
+
if (run.logs?.trim()) {
|
|
174
|
+
log(run.logs.trim());
|
|
175
|
+
}
|
|
176
|
+
// Before any error occurs
|
|
177
|
+
if (run.error) {
|
|
178
|
+
log(run.error.stack || run.error.message || run.error.name);
|
|
179
|
+
}
|
|
180
|
+
log(`↳ END: invoke('${run.name}')`);
|
|
181
|
+
}
|
|
182
|
+
};
|
|
149
183
|
const spinner = log.ora('Executing function...').start();
|
|
150
184
|
const { json: response, logs, error, } = await testAction(resource, invokeOptions, contextOptions, {
|
|
151
185
|
timeout: timeout ? timeout : resource.timeout,
|
|
186
|
+
onNestedInvoke: runNested,
|
|
187
|
+
depth: 0,
|
|
188
|
+
callStack: [fnName],
|
|
152
189
|
});
|
|
153
190
|
if (error) {
|
|
154
191
|
spinner.fail('Function execution failed.');
|
|
192
|
+
// Surface the function's own logs even on failure
|
|
193
|
+
if (logs?.trim()) {
|
|
194
|
+
log('Logs:');
|
|
195
|
+
log(logs);
|
|
196
|
+
}
|
|
197
|
+
logNestedRuns();
|
|
155
198
|
const errorMessage = error.stack || error.message || error.name;
|
|
156
199
|
const isTimeout = error.message?.includes('Timeout');
|
|
157
200
|
return {
|
|
@@ -166,6 +209,7 @@ export async function functionTestCore(options) {
|
|
|
166
209
|
spinner.succeed('Function execution succeeded.');
|
|
167
210
|
log('Logs:');
|
|
168
211
|
log(logs || '');
|
|
212
|
+
logNestedRuns();
|
|
169
213
|
if (response) {
|
|
170
214
|
log('Response:');
|
|
171
215
|
log(JSON.stringify(response, null, 2));
|
package/dist/server/app.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync, readFileSync, watchFile } from 'node:fs';
|
|
2
2
|
import * as http from 'node:http';
|
|
3
3
|
import { default as mime } from 'mime-types';
|
|
4
|
-
import { WebSocketServer } from 'ws';
|
|
4
|
+
import { WebSocket, WebSocketServer } from 'ws';
|
|
5
5
|
import { findBlueprintFile, loadAndParseBlueprint } from '../actions/blueprints/blueprint.js';
|
|
6
6
|
import { readConfigFile } from '../actions/blueprints/config.js';
|
|
7
7
|
import { resolveIds } from '../actions/blueprints/resolve.js';
|
|
@@ -10,6 +10,35 @@ import { isRecord } from '../utils/is-record.js';
|
|
|
10
10
|
import { isEventType, isGroqContextOptions, } from '../utils/types.js';
|
|
11
11
|
import { handleInvokeRequest } from './handlers/invoke.js';
|
|
12
12
|
const app = (host, port, validateResources, executionOptions) => {
|
|
13
|
+
const wss = new WebSocketServer({ port: 8974 });
|
|
14
|
+
// Send a JSON envelope to every connected UI client.
|
|
15
|
+
const broadcast = (envelope) => {
|
|
16
|
+
const message = JSON.stringify(envelope);
|
|
17
|
+
for (const client of wss.clients) {
|
|
18
|
+
if (client.readyState === WebSocket.OPEN)
|
|
19
|
+
client.send(message);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
let nestedOrder = 0;
|
|
23
|
+
/**
|
|
24
|
+
* Re-entrant callback that resolves a nested `context.invoke(name, payload)` by
|
|
25
|
+
* re-entering the invoke pipeline (which spawns its own child) and streams the
|
|
26
|
+
* nested run to the UI. Passing `nestedInvoke` to itself keeps it recursable.
|
|
27
|
+
*/
|
|
28
|
+
const nestedInvoke = async (name, payload, ctx, depth, callStack) => {
|
|
29
|
+
const order = ++nestedOrder;
|
|
30
|
+
const result = await handleInvokeRequest(name, { data: payload }, { event: 'nested' }, ctx, validateResources, executionOptions, depth, callStack, nestedInvoke);
|
|
31
|
+
broadcast({
|
|
32
|
+
type: 'nested-invoke',
|
|
33
|
+
name,
|
|
34
|
+
order,
|
|
35
|
+
logs: result.logs,
|
|
36
|
+
json: result.json,
|
|
37
|
+
error: result.error,
|
|
38
|
+
timings: result.timings,
|
|
39
|
+
});
|
|
40
|
+
return result;
|
|
41
|
+
};
|
|
13
42
|
const requestListener = async (req, res) => {
|
|
14
43
|
res.setHeader('Content-Type', 'application/json');
|
|
15
44
|
switch (true) {
|
|
@@ -50,7 +79,7 @@ const app = (host, port, validateResources, executionOptions) => {
|
|
|
50
79
|
delete context.clientOptions.token;
|
|
51
80
|
}
|
|
52
81
|
}
|
|
53
|
-
const result = await handleInvokeRequest(functionName, event, metadata, context, validateResources, executionOptions);
|
|
82
|
+
const result = await handleInvokeRequest(functionName, event, metadata, context, validateResources, executionOptions, 0, [], nestedInvoke);
|
|
54
83
|
// Add Server-Timing header
|
|
55
84
|
const timingHeaders = [];
|
|
56
85
|
for (const [key, value] of Object.entries(result.timings)) {
|
|
@@ -235,7 +264,6 @@ const app = (host, port, validateResources, executionOptions) => {
|
|
|
235
264
|
};
|
|
236
265
|
const server = http.createServer(requestListener);
|
|
237
266
|
server.listen(port, host, () => { });
|
|
238
|
-
const wss = new WebSocketServer({ port: 8974 });
|
|
239
267
|
wss.on('connection', function connection(ws) {
|
|
240
268
|
ws.on('error', console.error);
|
|
241
269
|
const fileInfo = findBlueprintFile();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { InvocationResponse, InvokeContextOptions, InvokeExecutionOptions, InvokePayloadMetadata } from '../../utils/types.js';
|
|
2
|
-
export declare function handleInvokeRequest(functionName: string, event: Record<string, unknown>, metadata: InvokePayloadMetadata, context: InvokeContextOptions, validateResources: boolean, executionOptions?: Partial<InvokeExecutionOptions
|
|
1
|
+
import type { InvocationResponse, InvokeContextOptions, InvokeExecutionOptions, InvokePayloadMetadata, NestedInvoke } from '../../utils/types.js';
|
|
2
|
+
export declare function handleInvokeRequest(functionName: string, event: Record<string, unknown>, metadata: InvokePayloadMetadata, context: InvokeContextOptions, validateResources: boolean, executionOptions?: Partial<InvokeExecutionOptions>, depth?: number, callStack?: string[], onNestedInvoke?: NestedInvoke): Promise<InvocationResponse & {
|
|
3
3
|
timings: Record<string, number>;
|
|
4
4
|
}>;
|
|
@@ -2,10 +2,10 @@ import { readLocalBlueprint } from '../../actions/blueprints/blueprint.js';
|
|
|
2
2
|
import config from '../../config.js';
|
|
3
3
|
import { resolveDeployedResources } from '../../utils/blueprints/resolve-deployed-resources.js';
|
|
4
4
|
import { findFunctionInResources } from '../../utils/functions/find.js';
|
|
5
|
-
import
|
|
5
|
+
import { runFunction } from '../../utils/functions/invoke/nested.js';
|
|
6
6
|
import { Logger } from '../../utils/logger.js';
|
|
7
7
|
const silentLogger = Logger(() => { });
|
|
8
|
-
export async function handleInvokeRequest(functionName, event, metadata, context, validateResources, executionOptions) {
|
|
8
|
+
export async function handleInvokeRequest(functionName, event, metadata, context, validateResources, executionOptions, depth = 0, callStack = [], onNestedInvoke) {
|
|
9
9
|
const start = performance.now();
|
|
10
10
|
const { resources, stackId, scopeType, scopeId } = await readLocalBlueprint(silentLogger, {
|
|
11
11
|
resources: validateResources,
|
|
@@ -22,9 +22,12 @@ export async function handleInvokeRequest(functionName, event, metadata, context
|
|
|
22
22
|
payload: event,
|
|
23
23
|
...metadata,
|
|
24
24
|
};
|
|
25
|
-
const response = await
|
|
25
|
+
const response = await runFunction(resource, payload, { ...context, resources: resolvedResources }, {
|
|
26
26
|
forceColor: executionOptions?.forceColor ?? false,
|
|
27
|
-
timeout: executionOptions?.timeout
|
|
27
|
+
timeout: executionOptions?.timeout,
|
|
28
|
+
depth,
|
|
29
|
+
callStack,
|
|
30
|
+
onNestedInvoke,
|
|
28
31
|
});
|
|
29
32
|
const timings = { ...response.timings, 'blueprint:read': readBlueprintTime };
|
|
30
33
|
return { ...response, timings };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* globals customElements document */
|
|
1
|
+
/* globals customElements document window */
|
|
2
2
|
import {ApiBaseElement} from './api-base.js'
|
|
3
3
|
import {getSharedStyleSheets} from './shared-styles.js'
|
|
4
4
|
|
|
@@ -36,6 +36,16 @@ template.innerHTML = `
|
|
|
36
36
|
`
|
|
37
37
|
|
|
38
38
|
class ConsolePanel extends ApiBaseElement {
|
|
39
|
+
/**
|
|
40
|
+
* Nested context.invoke() output for the current run, kept as a list so the
|
|
41
|
+
* final render can order it by call order. Nested runs finish innermost-first
|
|
42
|
+
* (A → B → C resolves C then B), so each carries an `order` stamped when it was
|
|
43
|
+
* initiated; the final render sorts by it. Buffered separately so it survives
|
|
44
|
+
* the result render, which would otherwise overwrite the console when Preserve
|
|
45
|
+
* Log is off.
|
|
46
|
+
*/
|
|
47
|
+
nestedEntries = []
|
|
48
|
+
|
|
39
49
|
constructor() {
|
|
40
50
|
super()
|
|
41
51
|
this.attachShadow({mode: 'open'}).appendChild(template.content.cloneNode(true))
|
|
@@ -45,25 +55,59 @@ class ConsolePanel extends ApiBaseElement {
|
|
|
45
55
|
if (!this.consoleOutput || !result) return
|
|
46
56
|
|
|
47
57
|
const {error, logs} = result
|
|
48
|
-
|
|
58
|
+
// Always show the logs. Then append the error, rather than replacing the logs with it.
|
|
59
|
+
let update = logs ?? ''
|
|
49
60
|
if (error) {
|
|
50
|
-
|
|
51
|
-
update = (
|
|
52
|
-
} else {
|
|
53
|
-
// Display regular logs
|
|
54
|
-
update = logs ?? '' // Handle case where logs might be null/undefined
|
|
61
|
+
const errText = (error.stack || error.message || error.name) ?? 'An error occurred.'
|
|
62
|
+
update = update.trim() ? `${update.trimEnd()}\n${errText}` : errText
|
|
55
63
|
}
|
|
56
64
|
|
|
57
65
|
if (this.api.store.preserveLog) {
|
|
66
|
+
// Nested output was already appended live during the run; keep it in place.
|
|
58
67
|
this.consoleOutput.innerText = this.consoleOutput.innerText + update
|
|
59
68
|
} else {
|
|
60
|
-
|
|
69
|
+
// Re-render so the nested output is shown in call order rather than the
|
|
70
|
+
// completion order it streamed in (and so it survives this overwrite).
|
|
71
|
+
this.consoleOutput.innerText = update + this.renderNestedEntries()
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Join the nested runs in call order (the order they were initiated).
|
|
76
|
+
renderNestedEntries = () =>
|
|
77
|
+
[...this.nestedEntries]
|
|
78
|
+
.sort((a, b) => a.order - b.order)
|
|
79
|
+
.map((e) => e.text)
|
|
80
|
+
.join('')
|
|
81
|
+
|
|
82
|
+
updateNested = (event) => {
|
|
83
|
+
const nestedInvoke = event.detail
|
|
84
|
+
if (!this.consoleOutput || !nestedInvoke) return
|
|
85
|
+
|
|
86
|
+
const {name, order, logs, json, error} = nestedInvoke
|
|
87
|
+
// Always show the logs. Then append the error, rather than replacing the logs with it.
|
|
88
|
+
let body = logs ?? ''
|
|
89
|
+
if (error) {
|
|
90
|
+
const errText = (error.stack || error.message || error.name) ?? 'An error occurred.'
|
|
91
|
+
body = body.trim() ? `${body.trimEnd()}\n${errText}` : errText
|
|
92
|
+
} else if (typeof json !== 'undefined') {
|
|
93
|
+
body += `${body && !body.endsWith('\n') ? '\n' : ''}→ returned ${JSON.stringify(json)}\n`
|
|
61
94
|
}
|
|
95
|
+
|
|
96
|
+
const text = `↳ BEGIN: invoke('${name}')\n${body.trim()}\n↳ END: invoke('${name}')\n`
|
|
97
|
+
this.nestedEntries.push({order, text})
|
|
98
|
+
// Append live for immediate feedback; the final result render reorders.
|
|
99
|
+
this.consoleOutput.innerText = this.consoleOutput.innerText + text
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// A new invocation is starting; drop the previous run's nested output.
|
|
103
|
+
resetNested = ({inprogress}) => {
|
|
104
|
+
if (inprogress) this.nestedEntries = []
|
|
62
105
|
}
|
|
63
106
|
|
|
64
107
|
clear = () => {
|
|
65
108
|
const backUp = this.api.store.preserveLog
|
|
66
109
|
this.api.store.result = {logs: undefined, error: undefined}
|
|
110
|
+
this.nestedEntries = []
|
|
67
111
|
this.consoleOutput.innerText = ''
|
|
68
112
|
this.api.store.preserveLog = backUp
|
|
69
113
|
}
|
|
@@ -75,9 +119,14 @@ class ConsolePanel extends ApiBaseElement {
|
|
|
75
119
|
this.consoleOutput = this.shadowRoot.querySelector('#console-output')
|
|
76
120
|
this.addEventListener('clear-console', this.clear)
|
|
77
121
|
|
|
122
|
+
// Nested context.invoke() runs arrive as a stream of discrete DOM events
|
|
123
|
+
// (not state) so none are dropped when several land in one frame.
|
|
124
|
+
window.addEventListener('nested-invoke', this.updateNested)
|
|
125
|
+
|
|
78
126
|
// Subscribe to changes in the result state to update the console
|
|
79
127
|
if (this.api) {
|
|
80
128
|
this.api.subscribe(this.updateConsole, ['result'])
|
|
129
|
+
this.api.subscribe(this.resetNested, ['inprogress'])
|
|
81
130
|
// Initial update in case result is already populated
|
|
82
131
|
if (this.api.store.result) {
|
|
83
132
|
this.updateConsole({result: this.api.store.result})
|
|
@@ -90,9 +139,11 @@ class ConsolePanel extends ApiBaseElement {
|
|
|
90
139
|
|
|
91
140
|
disconnectedCallback() {
|
|
92
141
|
this.removeEventListener('clear-console', this.clear)
|
|
142
|
+
window.removeEventListener('nested-invoke', this.updateNested)
|
|
93
143
|
// Unsubscribe when the element is removed from the DOM
|
|
94
144
|
if (this.api) {
|
|
95
145
|
this.api.unsubscribe(this.updateConsole)
|
|
146
|
+
this.api.unsubscribe(this.resetNested)
|
|
96
147
|
}
|
|
97
148
|
}
|
|
98
149
|
}
|
|
@@ -43,14 +43,17 @@ class RulePanel extends ApiBaseElement {
|
|
|
43
43
|
break
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
46
|
+
// Guard against the sate not being setup
|
|
47
|
+
if (rule?.state) {
|
|
48
|
+
const transaction = rule.state.update({
|
|
49
|
+
changes: {
|
|
50
|
+
from: 0,
|
|
51
|
+
insert: func.event ? JSON.stringify(func.event, null, 2) : '',
|
|
52
|
+
to: rule.state.doc.length,
|
|
53
|
+
},
|
|
54
|
+
})
|
|
55
|
+
rule.dispatch(transaction)
|
|
56
|
+
}
|
|
54
57
|
}
|
|
55
58
|
|
|
56
59
|
async connectedCallback() {
|
|
@@ -1,31 +1,34 @@
|
|
|
1
1
|
/* globals fetch */
|
|
2
2
|
|
|
3
|
-
let
|
|
3
|
+
let sharedStyleSheetsPromise = null
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
async function loadSharedStyleSheets() {
|
|
6
|
+
// Fetch both stylesheets
|
|
7
|
+
const [mCssResponse, appCssResponse] = await Promise.all([
|
|
8
|
+
fetch('./vendor/m-.css'),
|
|
9
|
+
fetch('./components/app.css'),
|
|
10
|
+
])
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
// Fetch both stylesheets
|
|
12
|
-
const [mCssResponse, appCssResponse] = await Promise.all([
|
|
13
|
-
fetch('./vendor/m-.css'),
|
|
14
|
-
fetch('./components/app.css'),
|
|
15
|
-
])
|
|
12
|
+
const [mCssText, appCssText] = await Promise.all([mCssResponse.text(), appCssResponse.text()])
|
|
16
13
|
|
|
17
|
-
|
|
14
|
+
// Create CSSStyleSheet objects
|
|
15
|
+
const mSheet = new CSSStyleSheet()
|
|
16
|
+
const appSheet = new CSSStyleSheet()
|
|
18
17
|
|
|
19
|
-
|
|
20
|
-
const mSheet = new CSSStyleSheet()
|
|
21
|
-
const appSheet = new CSSStyleSheet()
|
|
18
|
+
await Promise.all([mSheet.replace(mCssText), appSheet.replace(appCssText)])
|
|
22
19
|
|
|
23
|
-
|
|
20
|
+
return [mSheet, appSheet]
|
|
21
|
+
}
|
|
24
22
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
23
|
+
export function getSharedStyleSheets() {
|
|
24
|
+
if (!sharedStyleSheetsPromise) {
|
|
25
|
+
sharedStyleSheetsPromise = loadSharedStyleSheets().catch((error) => {
|
|
26
|
+
console.error('Failed to load shared stylesheets:', error)
|
|
27
|
+
// Reset so a later caller can retry instead of being stuck with [].
|
|
28
|
+
sharedStyleSheetsPromise = null
|
|
29
|
+
return []
|
|
30
|
+
})
|
|
30
31
|
}
|
|
32
|
+
|
|
33
|
+
return sharedStyleSheetsPromise
|
|
31
34
|
}
|