@devvit/public-api 0.10.18-next-2024-03-07-40aa25dcb.0 → 0.10.18-next-2024-03-11-41153468b.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,4 @@
1
- import type { Metadata, UIRequest, UIResponse } from '@devvit/protos';
1
+ import { type Metadata, type UIRequest, type UIResponse } from '@devvit/protos';
2
2
  import type { Hook, HookSegment, HookParams } from './types.js';
3
3
  import { RenderContext } from './RenderContext.js';
4
4
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"BlocksHandler.d.ts","sourceRoot":"","sources":["../../../../../src/devvit/internals/blocks/handler/BlocksHandler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAW,SAAS,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAK/E,OAAO,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAS,MAAM,YAAY,CAAC;AAEvE,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEnD;;;;;;GAMG;AACH,eAAO,IAAI,oBAAoB,EAAE,aAAa,GAAG,IAAW,CAAC;AAE7D,wBAAgB,WAAW,IAAI,OAAO,CAErC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,IAAI,EACzC,OAAO,EAAE,WAAW,EACpB,WAAW,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,CAAC,GAChC,CAAC,CAgCH;AAED,eAAO,IAAI,oBAAoB,EAAE,aAAa,GAAG,IAAW,CAAC;AAE7D;;;;GAIG;AACH,qBAAa,aAAa;;IAIxB,oBAAoB,EAAE,aAAa,GAAG,IAAI,CAAQ;gBAEtC,IAAI,EAAE,GAAG,CAAC,iBAAiB;IAKjC,MAAM,CAAC,OAAO,EAAE,SAAS,EAAE,QAAQ,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC;CAkL3E"}
1
+ {"version":3,"file":"BlocksHandler.d.ts","sourceRoot":"","sources":["../../../../../src/devvit/internals/blocks/handler/BlocksHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,QAAQ,EAEb,KAAK,SAAS,EACd,KAAK,UAAU,EAChB,MAAM,gBAAgB,CAAC;AAKxB,OAAO,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAsB,MAAM,YAAY,CAAC;AAEpF,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGnD;;;;;;GAMG;AACH,eAAO,IAAI,oBAAoB,EAAE,aAAa,GAAG,IAAW,CAAC;AAE7D,wBAAgB,WAAW,IAAI,OAAO,CAErC;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,IAAI,EACzC,OAAO,EAAE,WAAW,EACpB,WAAW,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,CAAC,GAChC,CAAC,CAgCH;AAED,eAAO,IAAI,oBAAoB,EAAE,aAAa,GAAG,IAAW,CAAC;AAE7D;;;;GAIG;AACH,qBAAa,aAAa;;IAIxB,oBAAoB,EAAE,aAAa,GAAG,IAAI,CAAQ;gBAEtC,IAAI,EAAE,GAAG,CAAC,iBAAiB;IAKjC,MAAM,CAAC,OAAO,EAAE,SAAS,EAAE,QAAQ,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC;CAoR3E"}
@@ -10,6 +10,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
10
10
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
11
  };
12
12
  var _BlocksHandler_instances, _BlocksHandler_root, _BlocksHandler_contextBuilder, _BlocksHandler_blocksTransformer, _BlocksHandler_loadHooks, _BlocksHandler_handleAsyncQueues, _BlocksHandler_attemptHook, _BlocksHandler_handleMainQueue, _BlocksHandler_renderRoot, _BlocksHandler_render, _BlocksHandler_renderList, _BlocksHandler_renderElement, _BlocksHandler_reifyProps;
13
+ import { EffectType, } from '@devvit/protos';
13
14
  import { ContextBuilder } from './ContextBuilder.js';
14
15
  import { BlocksTransformer } from '../BlocksTransformer.js';
15
16
  import { RenderInterruptError } from './types.js';
@@ -25,6 +26,9 @@ export let _activeRenderContext = null;
25
26
  export function isRendering() {
26
27
  return _activeRenderContext !== null;
27
28
  }
29
+ function _structuredClone(obj) {
30
+ return JSON.parse(JSON.stringify(obj));
31
+ }
28
32
  /**
29
33
  * This is the recommended low-level interface for creating hooks like useState or useAsync.
30
34
  *
@@ -87,12 +91,103 @@ export class BlocksHandler {
87
91
  const devvitContext = __classPrivateFieldGet(this, _BlocksHandler_contextBuilder, "f").buildContext(context, metadata);
88
92
  context.devvitContext = devvitContext;
89
93
  let blocks;
90
- // Async queue operations
91
- if (request.events.some((ev) => ev.queue)) {
92
- await __classPrivateFieldGet(this, _BlocksHandler_instances, "m", _BlocksHandler_handleAsyncQueues).call(this, context, request);
93
- }
94
- else {
95
- await __classPrivateFieldGet(this, _BlocksHandler_instances, "m", _BlocksHandler_handleMainQueue).call(this, context, request);
94
+ /**
95
+ * Events on the main queue must be handled in order, so that state is updated in the correct order. Events
96
+ * on other queues can be handled in parallel, because they only emit effects.
97
+ *
98
+ * There is an optimization here to process SendEventEffects locally, instead of letting them bubble up to the
99
+ * platform. This prevents a round trip to the platform for every event.
100
+ *
101
+ * This also means we need to respect execution queues here, and not just in the platform.
102
+ */
103
+ const eventsToProcess = request.events;
104
+ const noEvents = !request.events?.length;
105
+ const isMainQueue = noEvents || eventsToProcess.some((e) => !e.queue);
106
+ const isBlockingSSR = eventsToProcess.some((e) => e.blocking);
107
+ let progress;
108
+ let remaining = [...eventsToProcess];
109
+ while (eventsToProcess.length > 0) {
110
+ /**
111
+ * A concurrently executable batch is a set of events that can be executed in parallel. This either one main queue event,
112
+ * or any number of other queue events.
113
+ */
114
+ const batch = [];
115
+ if (!eventsToProcess[0].queue) {
116
+ batch.push(eventsToProcess.shift());
117
+ }
118
+ else {
119
+ while (eventsToProcess[0]?.queue) {
120
+ batch.push(eventsToProcess.shift());
121
+ }
122
+ }
123
+ assert(batch.length > 0, 'batch must have at least one event');
124
+ try {
125
+ if (batch[0].queue) {
126
+ const stateCopy = _structuredClone(context._state);
127
+ await __classPrivateFieldGet(this, _BlocksHandler_instances, "m", _BlocksHandler_handleAsyncQueues).call(this, context, ...batch);
128
+ // enforce that state updates are only allowed on the main queue.
129
+ context._state = stateCopy;
130
+ }
131
+ else {
132
+ await __classPrivateFieldGet(this, _BlocksHandler_instances, "m", _BlocksHandler_handleMainQueue).call(this, context, ...batch);
133
+ }
134
+ }
135
+ catch (e) {
136
+ /**
137
+ * If we have a progress, we can recover from an error by rolling back to the last progress, and then letting the
138
+ * remaining events be reprocessed.
139
+ */
140
+ if (progress) {
141
+ context._state = progress._state;
142
+ context._effects = progress._effects;
143
+ remaining.forEach((e, i) => {
144
+ const effect = {
145
+ type: EffectType.EFFECT_SEND_EVENT,
146
+ sendEvent: {
147
+ event: e,
148
+ },
149
+ };
150
+ context.emitEffect(`remaining-${i}`, effect);
151
+ });
152
+ break;
153
+ }
154
+ else {
155
+ throw e;
156
+ }
157
+ }
158
+ /**
159
+ * If we have any SendEventEffects, we can push them back on the queue to process them locally
160
+ */
161
+ for (const [key, effect] of Object.entries(context._effects)) {
162
+ if (effect.sendEvent?.event) {
163
+ if (!isMainQueue && !effect.sendEvent?.event?.queue) {
164
+ // We're async, this is a main queue event. We need to send it back to the platform to let
165
+ // the platform synchronize it.
166
+ break;
167
+ }
168
+ if (isMainQueue && effect.sendEvent?.event?.queue && !isBlockingSSR) {
169
+ // We're main queue, and this is an async event. We're not in SSR mode, so let's prioritize
170
+ // returning control quickly to the platform so we don't block event loops.
171
+ break;
172
+ }
173
+ //Ok, we can handle this event locally.
174
+ const event = effect.sendEvent.event;
175
+ eventsToProcess.push(event);
176
+ delete context._effects[key];
177
+ }
178
+ }
179
+ /**
180
+ * If we're going back through this again, we need to capture the progress, and the remaining events.
181
+ */
182
+ if (eventsToProcess.length > 0) {
183
+ progress = {
184
+ _state: _structuredClone(context._state),
185
+ _effects: { ...context._effects },
186
+ };
187
+ remaining = [...eventsToProcess];
188
+ }
189
+ } // End of while loop
190
+ if (isMainQueue) {
96
191
  // Rendering only happens on the main queue.
97
192
  const tags = __classPrivateFieldGet(this, _BlocksHandler_instances, "m", _BlocksHandler_renderRoot).call(this, __classPrivateFieldGet(this, _BlocksHandler_root, "f"), context._rootProps ?? {}, context);
98
193
  if (tags) {
@@ -106,20 +201,18 @@ export class BlocksHandler {
106
201
  };
107
202
  }
108
203
  }
109
- _BlocksHandler_root = new WeakMap(), _BlocksHandler_contextBuilder = new WeakMap(), _BlocksHandler_blocksTransformer = new WeakMap(), _BlocksHandler_instances = new WeakSet(), _BlocksHandler_loadHooks = function _BlocksHandler_loadHooks(context, request, ..._events) {
204
+ _BlocksHandler_root = new WeakMap(), _BlocksHandler_contextBuilder = new WeakMap(), _BlocksHandler_blocksTransformer = new WeakMap(), _BlocksHandler_instances = new WeakSet(), _BlocksHandler_loadHooks = function _BlocksHandler_loadHooks(context, ..._events) {
110
205
  // TBD: partial rendering
111
- __classPrivateFieldGet(this, _BlocksHandler_instances, "m", _BlocksHandler_renderRoot).call(this, __classPrivateFieldGet(this, _BlocksHandler_root, "f"), request.props ?? {}, context);
206
+ __classPrivateFieldGet(this, _BlocksHandler_instances, "m", _BlocksHandler_renderRoot).call(this, __classPrivateFieldGet(this, _BlocksHandler_root, "f"), context.request.props ?? {}, context);
112
207
  }, _BlocksHandler_handleAsyncQueues =
113
208
  /**
114
209
  * These can all run in parallel, because they only emit effects
115
210
  */
116
- async function _BlocksHandler_handleAsyncQueues(context, request) {
117
- // No state updates unless on the main queue.
118
- Object.freeze(context._state);
119
- __classPrivateFieldGet(this, _BlocksHandler_instances, "m", _BlocksHandler_loadHooks).call(this, context, request, ...request.events);
120
- await Promise.all(request.events.map(async (event) => {
211
+ async function _BlocksHandler_handleAsyncQueues(context, ...batch) {
212
+ __classPrivateFieldGet(this, _BlocksHandler_instances, "m", _BlocksHandler_loadHooks).call(this, context, ...batch);
213
+ await Promise.all(batch.map(async (event) => {
121
214
  if (!event.queue) {
122
- throw new Error("You can't mix main and other queues in one request. This is likely a platform bug. Please file an issue in the Discord for someone to help! https://discord.com/channels/1050224141732687912/1115441897079574620");
215
+ throw new Error("You can't mix main and other queues in one batch. This is likely a platform bug. Please file an issue in the Discord for someone to help! https://discord.com/channels/1050224141732687912/1115441897079574620");
123
216
  }
124
217
  await __classPrivateFieldGet(this, _BlocksHandler_instances, "m", _BlocksHandler_attemptHook).call(this, context, event);
125
218
  }));
@@ -131,15 +224,16 @@ async function _BlocksHandler_handleAsyncQueues(context, request) {
131
224
  }
132
225
  catch (e) {
133
226
  console.error('Error in event handler', e);
227
+ throw e;
134
228
  }
135
229
  }
136
230
  else {
137
231
  await context.handleUndeliveredEvent(event);
138
232
  }
139
- }, _BlocksHandler_handleMainQueue = async function _BlocksHandler_handleMainQueue(context, request) {
233
+ }, _BlocksHandler_handleMainQueue = async function _BlocksHandler_handleMainQueue(context, ...batch) {
140
234
  // We need to handle events in order, so that the state is updated in the correct order.
141
- for (const event of request.events) {
142
- __classPrivateFieldGet(this, _BlocksHandler_instances, "m", _BlocksHandler_loadHooks).call(this, context, request, event);
235
+ for (const event of batch) {
236
+ __classPrivateFieldGet(this, _BlocksHandler_instances, "m", _BlocksHandler_loadHooks).call(this, context, event);
143
237
  context._state.__generation = Number(context._state.__generation ?? 0) + 1;
144
238
  await __classPrivateFieldGet(this, _BlocksHandler_instances, "m", _BlocksHandler_attemptHook).call(this, context, event);
145
239
  }
@@ -20,11 +20,11 @@ const intervals = {};
20
20
  RenderContext.addGlobalUndeliveredEventHandler('intervals', async (event, context) => {
21
21
  if (event.timer && event.hook) {
22
22
  delete intervals[event.hook];
23
+ context.emitEffect('timers', {
24
+ type: EffectType.EFFECT_SET_INTERVALS,
25
+ interval: { intervals },
26
+ });
23
27
  }
24
- context.emitEffect('timers', {
25
- type: EffectType.EFFECT_SET_INTERVALS,
26
- interval: { intervals },
27
- });
28
28
  });
29
29
  class IntervalHook {
30
30
  constructor(callback, requestedDelayMs, params) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devvit/public-api",
3
- "version": "0.10.18-next-2024-03-07-40aa25dcb.0",
3
+ "version": "0.10.18-next-2024-03-11-41153468b.0",
4
4
  "license": "BSD-3-Clause",
5
5
  "repository": {
6
6
  "type": "git",
@@ -26,9 +26,9 @@
26
26
  },
27
27
  "types": "./index.d.ts",
28
28
  "dependencies": {
29
- "@devvit/protos": "0.10.18-next-2024-03-07-40aa25dcb.0",
30
- "@devvit/runtimes": "0.10.18-next-2024-03-07-40aa25dcb.0",
31
- "@devvit/shared-types": "0.10.18-next-2024-03-07-40aa25dcb.0",
29
+ "@devvit/protos": "0.10.18-next-2024-03-11-41153468b.0",
30
+ "@devvit/runtimes": "0.10.18-next-2024-03-11-41153468b.0",
31
+ "@devvit/shared-types": "0.10.18-next-2024-03-11-41153468b.0",
32
32
  "base64-js": "1.5.1",
33
33
  "clone-deep": "4.0.1",
34
34
  "core-js": "3.27.2",
@@ -37,7 +37,7 @@
37
37
  "devDependencies": {
38
38
  "@devvit/eslint-config": "0.10.17",
39
39
  "@devvit/repo-tools": "0.10.17",
40
- "@devvit/tsconfig": "0.10.18-next-2024-03-07-40aa25dcb.0",
40
+ "@devvit/tsconfig": "0.10.18-next-2024-03-11-41153468b.0",
41
41
  "@microsoft/api-extractor": "7.41.0",
42
42
  "@reddit/faceplate-ui": "11.1.0",
43
43
  "@types/clone-deep": "4.0.1",
@@ -52,5 +52,5 @@
52
52
  "directory": "dist"
53
53
  },
54
54
  "source": "./src/index.ts",
55
- "gitHead": "488e44435f4873bf294204a30b009d470f9e1a83"
55
+ "gitHead": "b8bb1a407a37092b0c4e5dc4d31d5e3a6f7069b7"
56
56
  }