@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
|
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,
|
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
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
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,
|
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,
|
117
|
-
|
118
|
-
|
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
|
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,
|
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
|
142
|
-
__classPrivateFieldGet(this, _BlocksHandler_instances, "m", _BlocksHandler_loadHooks).call(this, context,
|
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-
|
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-
|
30
|
-
"@devvit/runtimes": "0.10.18-next-2024-03-
|
31
|
-
"@devvit/shared-types": "0.10.18-next-2024-03-
|
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-
|
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": "
|
55
|
+
"gitHead": "b8bb1a407a37092b0c4e5dc4d31d5e3a6f7069b7"
|
56
56
|
}
|