@sanity/runtime-cli 14.10.1 → 14.12.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.
@@ -17,8 +17,20 @@ export declare const EVENT_MEDIA_LIBRARY_ASSET_CREATE = "media-library-asset-cre
17
17
  export declare const EVENT_MEDIA_LIBRARY_ASSET_UPDATE = "media-library-asset-update";
18
18
  export declare const EVENT_MEDIA_LIBRARY_ASSET_DELETE = "media-library-asset-delete";
19
19
  export declare const EVENT_SCHEDULED = "scheduled-function";
20
- export declare const FUNCTION_TYPES: readonly ["document-publish", "document-create", "document-update", "document-delete", "media-library-asset-create", "media-library-asset-update", "media-library-asset-delete", "scheduled-function"];
21
- export type ARRAY_OF_FUNCTION_TYPES = (typeof FUNCTION_TYPES)[number][];
20
+ export declare const EVENT_SYNC_TAG_INVALIDATE = "sync-tag-invalidate";
21
+ export declare const MAP_EVENT_TO_FUNCTION_TYPE: {
22
+ readonly "document-publish": "sanity.function.document";
23
+ readonly "document-create": "sanity.function.document";
24
+ readonly "document-delete": "sanity.function.document";
25
+ readonly "document-update": "sanity.function.document";
26
+ readonly "media-library-asset-create": "sanity.function.media-library.asset";
27
+ readonly "media-library-asset-delete": "sanity.function.media-library.asset";
28
+ readonly "media-library-asset-update": "sanity.function.media-library.asset";
29
+ readonly "scheduled-function": "sanity.function.cron";
30
+ readonly "sync-tag-invalidate": "sanity.function.sync-tag-invalidate";
31
+ };
32
+ export declare const FUNCTION_TYPES: (keyof typeof MAP_EVENT_TO_FUNCTION_TYPE)[];
33
+ export type ARRAY_OF_FUNCTION_TYPES = typeof FUNCTION_TYPES;
22
34
  export declare const PROJECT_SCOPED_FUNCTION_TYPES: ReadonlySet<string>;
23
35
  export declare const ORGANIZATION_SCOPED_FUNCTION_TYPES: ReadonlySet<string>;
24
36
  export declare const MAX_ASSET_SIZE = 209715200;
@@ -30,3 +42,4 @@ export declare const LABEL_MEDIA_LIBRARY_ASSET_CREATE = "Media Library Asset Cre
30
42
  export declare const LABEL_MEDIA_LIBRARY_ASSET_UPDATE = "Media Library Asset Update";
31
43
  export declare const LABEL_MEDIA_LIBRARY_ASSET_DELETE = "Media Library Asset Delete";
32
44
  export declare const LABEL_SCHEDULED = "Scheduled";
45
+ export declare const LABEL_SYNC_TAG_INVALIDATE = "Sync Tag Invalidate";
package/dist/constants.js CHANGED
@@ -17,16 +17,19 @@ export const EVENT_MEDIA_LIBRARY_ASSET_CREATE = 'media-library-asset-create';
17
17
  export const EVENT_MEDIA_LIBRARY_ASSET_UPDATE = 'media-library-asset-update';
18
18
  export const EVENT_MEDIA_LIBRARY_ASSET_DELETE = 'media-library-asset-delete';
19
19
  export const EVENT_SCHEDULED = 'scheduled-function';
20
- export const FUNCTION_TYPES = [
21
- EVENT_DOCUMENT_PUBLISH,
22
- EVENT_DOCUMENT_CREATE,
23
- EVENT_DOCUMENT_UPDATE,
24
- EVENT_DOCUMENT_DELETE,
25
- EVENT_MEDIA_LIBRARY_ASSET_CREATE,
26
- EVENT_MEDIA_LIBRARY_ASSET_UPDATE,
27
- EVENT_MEDIA_LIBRARY_ASSET_DELETE,
28
- EVENT_SCHEDULED,
29
- ];
20
+ export const EVENT_SYNC_TAG_INVALIDATE = 'sync-tag-invalidate';
21
+ export const MAP_EVENT_TO_FUNCTION_TYPE = {
22
+ [EVENT_DOCUMENT_PUBLISH]: SANITY_FUNCTION_DOCUMENT,
23
+ [EVENT_DOCUMENT_CREATE]: SANITY_FUNCTION_DOCUMENT,
24
+ [EVENT_DOCUMENT_DELETE]: SANITY_FUNCTION_DOCUMENT,
25
+ [EVENT_DOCUMENT_UPDATE]: SANITY_FUNCTION_DOCUMENT,
26
+ [EVENT_MEDIA_LIBRARY_ASSET_CREATE]: SANITY_FUNCTION_MEDIA_LIBRARY_ASSET,
27
+ [EVENT_MEDIA_LIBRARY_ASSET_DELETE]: SANITY_FUNCTION_MEDIA_LIBRARY_ASSET,
28
+ [EVENT_MEDIA_LIBRARY_ASSET_UPDATE]: SANITY_FUNCTION_MEDIA_LIBRARY_ASSET,
29
+ [EVENT_SCHEDULED]: SANITY_FUNCTION_SCHEDULED,
30
+ [EVENT_SYNC_TAG_INVALIDATE]: SANITY_FUNCTION_SYNC_TAG_INVALIDATE,
31
+ };
32
+ export const FUNCTION_TYPES = Object.keys(MAP_EVENT_TO_FUNCTION_TYPE);
30
33
  export const PROJECT_SCOPED_FUNCTION_TYPES = new Set([
31
34
  SANITY_FUNCTION_DOCUMENT,
32
35
  SANITY_FUNCTION_MEDIA_LIBRARY_ASSET,
@@ -44,3 +47,4 @@ export const LABEL_MEDIA_LIBRARY_ASSET_CREATE = 'Media Library Asset Create';
44
47
  export const LABEL_MEDIA_LIBRARY_ASSET_UPDATE = 'Media Library Asset Update';
45
48
  export const LABEL_MEDIA_LIBRARY_ASSET_DELETE = 'Media Library Asset Delete';
46
49
  export const LABEL_SCHEDULED = 'Scheduled';
50
+ export const LABEL_SYNC_TAG_INVALIDATE = 'Sync Tag Invalidate';
@@ -5,26 +5,29 @@ import { checkbox, confirm, input, select } from '@inquirer/prompts';
5
5
  import { highlight } from 'cardinal';
6
6
  import { createFunctionResource } from '../../actions/blueprints/resources.js';
7
7
  import { verifyExampleExists, writeExample } from '../../actions/sanity/examples.js';
8
- import { EVENT_DOCUMENT_CREATE, EVENT_DOCUMENT_DELETE, EVENT_DOCUMENT_UPDATE, EVENT_MEDIA_LIBRARY_ASSET_CREATE, EVENT_MEDIA_LIBRARY_ASSET_DELETE, EVENT_MEDIA_LIBRARY_ASSET_UPDATE,
8
+ import { EVENT_DOCUMENT_CREATE, EVENT_DOCUMENT_DELETE, EVENT_DOCUMENT_UPDATE, EVENT_MEDIA_LIBRARY_ASSET_CREATE, EVENT_MEDIA_LIBRARY_ASSET_DELETE, EVENT_MEDIA_LIBRARY_ASSET_UPDATE, EVENT_SYNC_TAG_INVALIDATE,
9
9
  // EVENT_SCHEDULED,
10
- FUNCTION_TYPES, LABEL_DOCUMENT_CREATE, LABEL_DOCUMENT_DELETE, LABEL_DOCUMENT_UPDATE, LABEL_MEDIA_LIBRARY_ASSET_CREATE, LABEL_MEDIA_LIBRARY_ASSET_DELETE, LABEL_MEDIA_LIBRARY_ASSET_UPDATE, LABEL_SCHEDULED, SANITY_FUNCTION_MEDIA_LIBRARY_ASSET, SANITY_FUNCTION_SCHEDULED, } from '../../constants.js';
10
+ FUNCTION_TYPES, LABEL_DOCUMENT_CREATE, LABEL_DOCUMENT_DELETE, LABEL_DOCUMENT_UPDATE, LABEL_MEDIA_LIBRARY_ASSET_CREATE, LABEL_MEDIA_LIBRARY_ASSET_DELETE, LABEL_MEDIA_LIBRARY_ASSET_UPDATE, LABEL_SCHEDULED, LABEL_SYNC_TAG_INVALIDATE, MAP_EVENT_TO_FUNCTION_TYPE, SANITY_FUNCTION_DOCUMENT, SANITY_FUNCTION_MEDIA_LIBRARY_ASSET, SANITY_FUNCTION_SCHEDULED, SANITY_FUNCTION_SYNC_TAG_INVALIDATE, } from '../../constants.js';
11
11
  import { check, indent, warn } from '../../utils/display/presenters.js';
12
12
  import { styleText } from '../../utils/style-text.js';
13
13
  import { validateFunctionName } from '../../utils/validate/resource.js';
14
14
  export const generateFunctionBlueprintResourceTemplate = (fnName, eventNames) => {
15
- const functionType = eventNames[0].substring(0, eventNames[0].lastIndexOf('-'));
15
+ const functionType = MAP_EVENT_TO_FUNCTION_TYPE[eventNames[0]];
16
16
  let definer = '';
17
17
  const eventOns = eventNames.map((e) => `'${e.substring(e.lastIndexOf('-') + 1)}'`);
18
18
  switch (functionType) {
19
- case 'document':
19
+ case SANITY_FUNCTION_DOCUMENT:
20
20
  definer = `defineDocumentFunction({name: '${fnName}', event: {on: [${eventOns.join(', ')}]}}), // ← add this line`;
21
21
  break;
22
- case 'media-library-asset':
22
+ case SANITY_FUNCTION_MEDIA_LIBRARY_ASSET:
23
23
  definer = `defineMediaLibraryAssetFunction({name: '${fnName}', event: {on: [${eventOns.join(', ')}], resource: {type: 'media-library', id: 'my-media-library-id'}}}), // ← add this line`;
24
24
  break;
25
- case 'scheduled':
25
+ case SANITY_FUNCTION_SCHEDULED:
26
26
  definer = `defineScheduledFunction({name: '${fnName}', event: {expression: '0 0 * * *'}}), // ← add this line`;
27
27
  break;
28
+ case SANITY_FUNCTION_SYNC_TAG_INVALIDATE:
29
+ definer = `defineSyncTagInvalidateFunction({name: '${fnName}'}), // ← add this line`;
30
+ break;
28
31
  }
29
32
  return `
30
33
  export default defineBlueprint({
@@ -154,7 +157,7 @@ export async function functionAddCore(options) {
154
157
  }
155
158
  const eventSources = new Set(fnTypes.map((t) => t.substring(0, t.lastIndexOf('-'))));
156
159
  if (eventSources.size > 1) {
157
- throw new Error('Invalid function type. Cannot mix document-*, media-library-asset-*, and/or scheduled-* types.');
160
+ throw new Error('Invalid function type. Cannot mix document-*, media-library-asset-*, sync-tag-* and/or scheduled-* types.');
158
161
  }
159
162
  let addHelpers;
160
163
  let installCommand;
@@ -238,6 +241,7 @@ async function promptForFunctionType() {
238
241
  { name: LABEL_MEDIA_LIBRARY_ASSET_CREATE, value: EVENT_MEDIA_LIBRARY_ASSET_CREATE },
239
242
  { name: LABEL_MEDIA_LIBRARY_ASSET_UPDATE, value: EVENT_MEDIA_LIBRARY_ASSET_UPDATE },
240
243
  { name: LABEL_MEDIA_LIBRARY_ASSET_DELETE, value: EVENT_MEDIA_LIBRARY_ASSET_DELETE },
244
+ { name: LABEL_SYNC_TAG_INVALIDATE, value: EVENT_SYNC_TAG_INVALIDATE },
241
245
  // {name: LABEL_SCHEDULED, value: EVENT_SCHEDULED},
242
246
  ],
243
247
  validate(choices) {
@@ -10,5 +10,6 @@ export class ApiBaseElement extends HTMLElement {
10
10
  this.SANITY_FUNCTION_DOCUMENT = 'sanity.function.document'
11
11
  this.SANITY_FUNCTION_MEDIA_LIBRARY_ASSET = 'sanity.function.media-library.asset'
12
12
  this.SANITY_FUNCTION_SCHEDULED = 'sanity.function.cron'
13
+ this.SANITY_FUNCTION_SYNC_TAG_INVALIDATE = 'sanity.function.sync-tag-invalidate'
13
14
  }
14
15
  }
@@ -80,17 +80,24 @@ class FiltersComponent extends ApiBaseElement {
80
80
  this.SANITY_FUNCTION_MEDIA_LIBRARY_ASSET,
81
81
  )
82
82
  const scheduleFunction = this.api.store.selectedFunctionType === this.SANITY_FUNCTION_SCHEDULED
83
+ const stiFunction =
84
+ this.api.store.selectedFunctionType === this.SANITY_FUNCTION_SYNC_TAG_INVALIDATE
83
85
 
84
86
  const container = this.shadowRoot.querySelector('fieldset')
85
- container.innerHTML = this.buildFilters(docFunction, mediaFunction, scheduleFunction)
87
+ container.innerHTML = this.buildFilters(
88
+ docFunction,
89
+ mediaFunction,
90
+ scheduleFunction,
91
+ stiFunction,
92
+ )
86
93
  }
87
94
 
88
- buildFilters = (docFunction, mediaFunction, scheduleFunction) => {
95
+ buildFilters = (docFunction, mediaFunction, scheduleFunction, stiFunction) => {
89
96
  return `
90
97
  <legend class="config-label">Client Options</legend>
91
98
  <div id="dynamic-dropdowns" class="flex gap-2">
92
99
  ${
93
- docFunction || scheduleFunction
100
+ docFunction || scheduleFunction || stiFunction
94
101
  ? `<select-dropdown
95
102
  label="Project"
96
103
  store-key="projects"
@@ -61,6 +61,7 @@ class FunctionList extends ApiBaseElement {
61
61
  case this.SANITY_FUNCTION_SCHEDULED:
62
62
  return 'Scheduled'
63
63
  default:
64
+ // this works well for sync-tag-invalidate functions!
64
65
  return type.split('.').pop().replaceAll('-', ' ')
65
66
  }
66
67
  }
@@ -29,7 +29,7 @@ template.innerHTML = `
29
29
  <div id="payloadContainer" class="gutter-gradient relative h-100 max-h-100 y-scroll border-top border-top-none-l">
30
30
  <div class="bg gutter-gradient sticky top-0 right-0 left-0 z-100">
31
31
  <div class="flex items-center space-between pad-t-2 pad-r-5 pad-b-2 pad-l-12">
32
- <h2 class="config-label mar-t-0 mar-b-0">Document</h2>
32
+ <h2 id="payloadTitle" class="config-label mar-t-0 mar-b-0">Document</h2>
33
33
  </div>
34
34
  <hr class='hr-border mar-l-specific' />
35
35
  </div>
@@ -75,11 +75,35 @@ class PayloadPanel extends ApiBaseElement {
75
75
 
76
76
  this.api.subscribe(this.updatePayload, ['document'])
77
77
  this.api.subscribe(this.updateSelectedEvent, ['selectedEvent'])
78
- this.api.subscribe(this.updateCodeMirror, ['selectedFunctionType'])
78
+ this.api.subscribe(this.updateCodeMirror, ['selectedIndex', 'functions'])
79
+ this.api.subscribe(this.updatePanelTitle, ['selectedFunctionType'])
80
+ this.documentCacheByFunction = {}
81
+ this.currentIndex = -1
79
82
  }
80
83
 
81
- updateCodeMirror = ({selectedFunctionType}) => {
82
- if (selectedFunctionType === this.SANITY_FUNCTION_SCHEDULED) {
84
+ updateCodeMirror = () => {
85
+ const {
86
+ functions,
87
+ selectedIndex: selectedFuncName,
88
+ payload: {state},
89
+ } = this.api.store
90
+ const docText = state.doc.text.join('')
91
+ // save the previous payload in the cache before we do anything else
92
+ if (docText) {
93
+ const currentDoc = JSON.parse(docText)
94
+ this.documentCacheByFunction[this.currentIndex] = currentDoc
95
+ } else {
96
+ this.documentCacheByFunction[this.currentIndex] = ''
97
+ }
98
+ const selectedIndex = functions.findIndex((f) => f.name === selectedFuncName)
99
+ if (selectedIndex === this.currentIndex) return
100
+ const selectedFunction = functions[selectedIndex]
101
+ // flip editable of code mirror textarea based on function type
102
+ if (
103
+ [this.SANITY_FUNCTION_SCHEDULED, this.SANITY_FUNCTION_SYNC_TAG_INVALIDATE].includes(
104
+ selectedFunction.type,
105
+ )
106
+ ) {
83
107
  this.api.store.beforePayload.dispatch({
84
108
  effects: editableCompartment.reconfigure(EditorView.editable.of(false)),
85
109
  })
@@ -100,6 +124,16 @@ class PayloadPanel extends ApiBaseElement {
100
124
  effects: editableCompartment.reconfigure(EditorView.editable.of(true)),
101
125
  })
102
126
  }
127
+ // for certain other specific function types, set the payload to something specific, or reload whatever was loaded into its document payload before
128
+ switch (selectedFunction.type) {
129
+ case this.SANITY_FUNCTION_SYNC_TAG_INVALIDATE:
130
+ this.#updateCodeView(this.api.store.payload, {data: {syncTags: ['s1:1234', 's2:5678']}})
131
+ break
132
+ default:
133
+ this.#updateCodeView(this.api.store.payload, this.documentCacheByFunction[selectedIndex])
134
+ break
135
+ }
136
+ this.currentIndex = selectedIndex
103
137
  }
104
138
  updatePayload = ({document}) => {
105
139
  if (!document) return
@@ -112,7 +146,7 @@ class PayloadPanel extends ApiBaseElement {
112
146
  const transaction = view.state.update({
113
147
  changes: {
114
148
  from: 0,
115
- insert: JSON.stringify(document, null, 2),
149
+ insert: document ? JSON.stringify(document, null, 2) : '',
116
150
  to: view.state.doc.length,
117
151
  },
118
152
  })
@@ -126,6 +160,22 @@ class PayloadPanel extends ApiBaseElement {
126
160
  payloadContainer.style.display = isUpdateEvent ? 'none' : 'block'
127
161
  deltaPayloadContainer.style.display = isUpdateEvent ? 'block' : 'none'
128
162
  }
163
+ updatePanelTitle = ({selectedFunctionType}) => {
164
+ const payloadTitle = this.shadowRoot.querySelector('#payloadTitle')
165
+ let title = 'Document'
166
+ switch (selectedFunctionType) {
167
+ case this.SANITY_FUNCTION_SYNC_TAG_INVALIDATE:
168
+ title = 'Sync Tag Payload'
169
+ break
170
+ case this.SANITY_FUNCTION_SCHEDULED:
171
+ title = 'Schedule Payload'
172
+ break
173
+ case this.SANITY_FUNCTION_DOCUMENT:
174
+ case this.SANITY_FUNCTION_MEDIA_LIBRARY_ASSET:
175
+ title = 'Document'
176
+ }
177
+ payloadTitle.innerHTML = title
178
+ }
129
179
 
130
180
  disconnectedCallback() {
131
181
  if (this.api) {
@@ -14,7 +14,7 @@ template.innerHTML = `<div class="border-left y-scroll min-h-0">
14
14
  <h3 class="config-label mar-t-0 hidden">Filter/Projection</h3>
15
15
  <header class='flex space-between'>
16
16
  <dl class='flex items-center margin-0 pad-3 slab-stat'>
17
- <dt class='nowrap'>Filter/Projection</dt>
17
+ <dt id="ruleTitle" class="nowrap">Filter/Projection</dt>
18
18
  </dl>
19
19
  </header>
20
20
  <div id="rule" name="rule" class="cm-s-dracula"></div>
@@ -30,17 +30,27 @@ class RulePanel extends ApiBaseElement {
30
30
  updateRule = () => {
31
31
  const {functions, rule, selectedIndex} = this.api.store
32
32
  const func = functions.find((func) => func.name === selectedIndex)
33
-
34
- if (func.event) {
35
- const transaction = rule.state.update({
36
- changes: {
37
- from: 0,
38
- insert: JSON.stringify(func.event, null, 2),
39
- to: rule.state.doc.length,
40
- },
41
- })
42
- rule.dispatch(transaction)
33
+ switch (func.type) {
34
+ case this.SANITY_FUNCTION_SCHEDULED:
35
+ this.ruleTitle.innerHTML = 'Schedule Rule'
36
+ break
37
+ case this.SANITY_FUNCTION_DOCUMENT:
38
+ case this.SANITY_FUNCTION_MEDIA_LIBRARY_ASSET:
39
+ this.ruleTitle.innerHTML = 'Filter/Projection'
40
+ break
41
+ case this.SANITY_FUNCTION_SYNC_TAG_INVALIDATE:
42
+ this.ruleTitle.innerHTML = 'Resource'
43
+ break
43
44
  }
45
+
46
+ const transaction = rule.state.update({
47
+ changes: {
48
+ from: 0,
49
+ insert: func.event ? JSON.stringify(func.event, null, 2) : '',
50
+ to: rule.state.doc.length,
51
+ },
52
+ })
53
+ rule.dispatch(transaction)
44
54
  }
45
55
 
46
56
  async connectedCallback() {
@@ -48,6 +58,7 @@ class RulePanel extends ApiBaseElement {
48
58
  this.shadowRoot.adoptedStyleSheets = sheets
49
59
 
50
60
  this.rule = this.shadowRoot.querySelector('#rule')
61
+ this.ruleTitle = this.shadowRoot.querySelector('#ruleTitle')
51
62
 
52
63
  if (this.api) {
53
64
  this.api.subscribe(this.updateRule, ['selectedIndex', 'functions'])
@@ -117,7 +117,12 @@ process.on('message', async (data) => {
117
117
  )
118
118
  }
119
119
 
120
- json = await eventHandler({context, event})
120
+ json = await eventHandler({
121
+ context,
122
+ event,
123
+ // mock sync-tag-invalidate done callback
124
+ done: async (_tags) => new Response(null, {status: 204}),
125
+ })
121
126
 
122
127
  // Restore streams
123
128
  process.stdout.write = originalStdoutWrite
@@ -70,7 +70,7 @@ export default async function invoke(resource, payload, context, options) {
70
70
  createTempPackageJson(functionPath);
71
71
  }
72
72
  const { forceColor = true, timeout = 10 } = options;
73
- let filteredData = {};
73
+ let filteredData = payload?.payload?.data || {};
74
74
  if (resource.type === 'sanity.function.document' ||
75
75
  resource.type === 'sanity.function.media-library.asset') {
76
76
  if (!isGroqContextOptions(context)) {