@devvit/ui-renderer 0.10.17-next-2024-02-23-ad2efe2c5.0 → 0.10.17-next-2024-02-26-9e49b4eeb.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.
@@ -0,0 +1,67 @@
1
+ import { BlockRenderEventType, Effect, LinkedBundle, Metadata, RenderPostRequest, RenderPostResponse, Struct, UIEvent } from '@devvit/protos';
2
+ import { ErrorCallback, LiteClient } from '@devvit/runtime-lite/client/BrowserLiteClient.js';
3
+ import { PluginCallback } from '@devvit/runtime-lite/runtime/RuntimeLite.js';
4
+ import { T3ID, TID } from '@devvit/shared-types/tid.js';
5
+ import { UIApp } from './ui-app.js';
6
+ type AppState = {
7
+ __postData?: {
8
+ thingId: TID | undefined;
9
+ config: Struct['fields'] | undefined;
10
+ };
11
+ [key: string]: unknown;
12
+ };
13
+ type EventData = {
14
+ [key: string]: unknown;
15
+ };
16
+ /**
17
+ * App render and effect looping logic. Rendering and event processing is as
18
+ * follows:
19
+ *
20
+ * 1. Try the local runtime, if available.
21
+ * 2. If a local runtime or bundle is unavailable, or local execution fails
22
+ * (including CircuitBreak), try the remote.
23
+ * 3. Report execution errors by callback only. In general, all errors are
24
+ * attempted to be tolerated.
25
+ *
26
+ * App / render state is always preserved even on error so a buggy app will try
27
+ * not to show any issues. A bundle or runtime can be replaced on the fly with
28
+ * losing state.
29
+ */
30
+ export declare class AppUILooper {
31
+ #private;
32
+ meta?: Metadata | undefined;
33
+ onError?: ErrorCallback | undefined;
34
+ onPluginCall?: PluginCallback | undefined;
35
+ onRenderPost?: ((req: RenderPostRequest, rsp: RenderPostResponse) => void) | undefined;
36
+ onUserAction?: ((fx: Effect) => void) | undefined;
37
+ postConfig?: Struct['fields'] | undefined;
38
+ postID?: T3ID | undefined;
39
+ /** A client to the remote runtime. */
40
+ remote?: UIApp | undefined;
41
+ /**
42
+ * A client to the local runtime.
43
+ * @internal
44
+ */
45
+ _local?: LiteClient | undefined;
46
+ /** Get the current app state with the current post state. */
47
+ get appState(): AppState;
48
+ /**
49
+ * Load an app bundle into the local runtime, if available. The bundle is
50
+ * assumed to implement a UIApp.
51
+ */
52
+ load(app: Readonly<LinkedBundle>, sandbox: boolean): Promise<void>;
53
+ queueEvent(ev: UIEvent): void;
54
+ queueRenderPost(type: BlockRenderEventType, id: string | undefined, data: EventData): void;
55
+ get rendered(): boolean;
56
+ /**
57
+ * Discard app and render state. This is used indirectly by devvit-preview
58
+ * which frequently loads new bundles.
59
+ */
60
+ resetAppState(): void;
61
+ /** Set the runtime source code. */
62
+ setLocalSrc(src: Blob | undefined): void;
63
+ /** Unload the app and reject any open requests. */
64
+ unload(): void;
65
+ }
66
+ export {};
67
+ //# sourceMappingURL=app-ui-looper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-ui-looper.d.ts","sourceRoot":"","sources":["../../library/src/client/app-ui-looper.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,EACpB,MAAM,EAGN,YAAY,EACZ,QAAQ,EAER,iBAAiB,EACjB,kBAAkB,EAClB,MAAM,EACN,OAAO,EACR,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAEL,aAAa,EACb,UAAU,EACX,MAAM,kDAAkD,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,6CAA6C,CAAC;AAE7E,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,6BAA6B,CAAC;AAGxD,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAQpC,KAAK,QAAQ,GAAG;IACd,UAAU,CAAC,EAAE;QACX,OAAO,EAAE,GAAG,GAAG,SAAS,CAAC;QACzB,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,GAAG,SAAS,CAAC;KACtC,CAAC;IACF,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,CAAC;AACF,KAAK,SAAS,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,CAAC;AAE5C;;;;;;;;;;;;;GAaG;AACH,qBAAa,WAAW;;IACtB,IAAI,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IAC5B,OAAO,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IACpC,YAAY,CAAC,EAAE,cAAc,GAAG,SAAS,CAAC;IAC1C,YAAY,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,iBAAiB,EAAE,GAAG,EAAE,kBAAkB,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACvF,YAAY,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAClD,UAAU,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,GAAG,SAAS,CAAC;IAC1C,MAAM,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC;IAC1B,sCAAsC;IACtC,MAAM,CAAC,EAAE,KAAK,GAAG,SAAS,CAAC;IAU3B;;;OAGG;IACH,MAAM,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;IAWhC,6DAA6D;IAC7D,IAAI,QAAQ,IAAI,QAAQ,CAGvB;IAED;;;OAGG;IACG,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IASxE,UAAU,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI;IAU7B,eAAe,CAAC,IAAI,EAAE,oBAAoB,EAAE,EAAE,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,SAAS,GAAG,IAAI;IAI1F,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED;;;OAGG;IACH,aAAa,IAAI,IAAI;IAKrB,mCAAmC;IACnC,WAAW,CAAC,GAAG,EAAE,IAAI,GAAG,SAAS,GAAG,IAAI;IAUxC,mDAAmD;IACnD,MAAM,IAAI,IAAI;CAoGf"}
@@ -0,0 +1,206 @@
1
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
2
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
3
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
4
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
5
+ };
6
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
7
+ if (kind === "m") throw new TypeError("Private method is not writable");
8
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
9
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
10
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
11
+ };
12
+ var _AppUILooper_instances, _AppUILooper_appState, _AppUILooper_q, _AppUILooper_rendered, _AppUILooper_rerenderTimeout, _AppUILooper_onEffects, _AppUILooper_onPluginCall, _AppUILooper_processEvent, _AppUILooper_renderPost;
13
+ import { BlockRenderEventType, RealtimeDefinition, } from '@devvit/protos';
14
+ import { BrowserLiteClient, } from '@devvit/runtime-lite/client/BrowserLiteClient.js';
15
+ import { isCircuitBreaker } from '@devvit/runtime-lite/types/CircuitBreaker.js';
16
+ import PQueue from 'p-queue';
17
+ import { initDevvitGlobal } from '../types/devvit-global.js';
18
+ // Effects that can only be fired as the result of a user action
19
+ const userActionEffects = ['showToast', 'showForm', 'navigateToUrl'];
20
+ initDevvitGlobal();
21
+ /**
22
+ * App render and effect looping logic. Rendering and event processing is as
23
+ * follows:
24
+ *
25
+ * 1. Try the local runtime, if available.
26
+ * 2. If a local runtime or bundle is unavailable, or local execution fails
27
+ * (including CircuitBreak), try the remote.
28
+ * 3. Report execution errors by callback only. In general, all errors are
29
+ * attempted to be tolerated.
30
+ *
31
+ * App / render state is always preserved even on error so a buggy app will try
32
+ * not to show any issues. A bundle or runtime can be replaced on the fly with
33
+ * losing state.
34
+ */
35
+ export class AppUILooper {
36
+ constructor() {
37
+ _AppUILooper_instances.add(this);
38
+ /**
39
+ * App state is never reset in production. Prior remote executions may have
40
+ * occurred. The next loop will replace the state. Do not lose the current
41
+ * state even after a bundle has been downloaded and is ready to load. The
42
+ * only time state should be reinitialized is when a bundle has been
43
+ * _replaced_ such as by devvit-preview.
44
+ */
45
+ _AppUILooper_appState.set(this, {});
46
+ /** No concurrency is used to enforce order. to-do: replace with array? */
47
+ _AppUILooper_q.set(this, new PQueue({ concurrency: 1 }));
48
+ /**
49
+ * True if app has rendered since loaded. Since a successful render isn't
50
+ * known at queue time, it's possible for multiple redundant renders to be
51
+ * enqueued.
52
+ */
53
+ _AppUILooper_rendered.set(this, false);
54
+ _AppUILooper_rerenderTimeout.set(this, void 0);
55
+ _AppUILooper_onPluginCall.set(this, (id, serviceName, method, args, meta) => {
56
+ this.onPluginCall?.(id, serviceName, method, args, meta);
57
+ if (serviceName === RealtimeDefinition.fullName &&
58
+ method === RealtimeDefinition.methods.send.name)
59
+ this._local?.onPluginResponse(id, { error: undefined, success: true, value: {} });
60
+ });
61
+ }
62
+ /** Get the current app state with the current post state. */
63
+ get appState() {
64
+ __classPrivateFieldGet(this, _AppUILooper_appState, "f").__postData = { thingId: this.postID, config: this.postConfig };
65
+ return __classPrivateFieldGet(this, _AppUILooper_appState, "f");
66
+ }
67
+ /**
68
+ * Load an app bundle into the local runtime, if available. The bundle is
69
+ * assumed to implement a UIApp.
70
+ */
71
+ async load(app, sandbox) {
72
+ this.unload();
73
+ try {
74
+ await this._local?.loadBundle(app, sandbox);
75
+ }
76
+ catch (err) {
77
+ this.onError?.('Error', err);
78
+ }
79
+ }
80
+ queueEvent(ev) {
81
+ if (ev.queue) {
82
+ // TODO: Add QoS for different types of events on alternate queues. For
83
+ // now, treat alternate queues as having infinite concurrency.
84
+ void __classPrivateFieldGet(this, _AppUILooper_instances, "m", _AppUILooper_processEvent).call(this, ev);
85
+ }
86
+ else {
87
+ void __classPrivateFieldGet(this, _AppUILooper_q, "f").add(() => __classPrivateFieldGet(this, _AppUILooper_instances, "m", _AppUILooper_processEvent).call(this, ev));
88
+ }
89
+ }
90
+ queueRenderPost(type, id, data) {
91
+ void __classPrivateFieldGet(this, _AppUILooper_q, "f").add(() => __classPrivateFieldGet(this, _AppUILooper_instances, "m", _AppUILooper_renderPost).call(this, type, id, data));
92
+ }
93
+ get rendered() {
94
+ return __classPrivateFieldGet(this, _AppUILooper_rendered, "f");
95
+ }
96
+ /**
97
+ * Discard app and render state. This is used indirectly by devvit-preview
98
+ * which frequently loads new bundles.
99
+ */
100
+ resetAppState() {
101
+ __classPrivateFieldSet(this, _AppUILooper_rendered, false, "f");
102
+ __classPrivateFieldSet(this, _AppUILooper_appState, {}, "f");
103
+ }
104
+ /** Set the runtime source code. */
105
+ setLocalSrc(src) {
106
+ this.unload();
107
+ this._local = src
108
+ ? new BrowserLiteClient(src, (type, err) => this.onError?.(type, err), {
109
+ callback: __classPrivateFieldGet(this, _AppUILooper_onPluginCall, "f"),
110
+ plugins: [RealtimeDefinition.fullName],
111
+ })
112
+ : undefined;
113
+ }
114
+ /** Unload the app and reject any open requests. */
115
+ unload() {
116
+ if (__classPrivateFieldGet(this, _AppUILooper_rerenderTimeout, "f") != null)
117
+ clearTimeout(__classPrivateFieldGet(this, _AppUILooper_rerenderTimeout, "f"));
118
+ // Disable errors, wipe the old client rejecting any open promises, then
119
+ // re-enable errors. Leave app state as-is.
120
+ const onError = this.onError;
121
+ this.onError = undefined;
122
+ this._local?.quit();
123
+ this.onError = onError;
124
+ }
125
+ }
126
+ _AppUILooper_appState = new WeakMap(), _AppUILooper_q = new WeakMap(), _AppUILooper_rendered = new WeakMap(), _AppUILooper_rerenderTimeout = new WeakMap(), _AppUILooper_onPluginCall = new WeakMap(), _AppUILooper_instances = new WeakSet(), _AppUILooper_onEffects = function _AppUILooper_onEffects(renderType, effects) {
127
+ for (const effect of effects) {
128
+ if (effect.rerenderUi) {
129
+ if (__classPrivateFieldGet(this, _AppUILooper_rerenderTimeout, "f") != null)
130
+ clearTimeout(__classPrivateFieldGet(this, _AppUILooper_rerenderTimeout, "f"));
131
+ __classPrivateFieldSet(this, _AppUILooper_rerenderTimeout, setTimeout(() => {
132
+ this.queueRenderPost(BlockRenderEventType.RENDER_EFFECT_EVENT, 'rerender-effect', {});
133
+ }, (effect.rerenderUi.delaySeconds ?? 0) * 1000), "f");
134
+ }
135
+ else if (effect.sendEvent?.event) {
136
+ this.queueEvent(effect.sendEvent.event);
137
+ }
138
+ else if (renderType !== BlockRenderEventType.RENDER_USER_ACTION ||
139
+ userActionEffects.some((type) => effect[type])) {
140
+ this.onUserAction?.(effect);
141
+ }
142
+ }
143
+ }, _AppUILooper_processEvent = async function _AppUILooper_processEvent(ev) {
144
+ let rsp;
145
+ const req = { event: ev, state: this.appState };
146
+ if (this._local?.ready) {
147
+ try {
148
+ rsp = (await this._local.call('HandleUIEvent', req, this.meta));
149
+ }
150
+ catch (err) {
151
+ if (!isCircuitBreaker(err) || !this.remote)
152
+ this.onError?.('Error', err);
153
+ if (devvit?.logLocalErrors)
154
+ console.info('remote call', err);
155
+ }
156
+ }
157
+ if (!rsp) {
158
+ try {
159
+ rsp = await this.remote?.HandleUIEvent(req, this.meta);
160
+ }
161
+ catch (err) {
162
+ this.onError?.('Error', err);
163
+ }
164
+ }
165
+ if (rsp) {
166
+ // Can only change state on the main queue.
167
+ if (!ev.queue) {
168
+ __classPrivateFieldSet(this, _AppUILooper_appState, rsp.state ?? {}, "f");
169
+ }
170
+ const renderType = ev.realtimeEvent
171
+ ? BlockRenderEventType.RENDER_EFFECT_EVENT
172
+ : BlockRenderEventType.RENDER_USER_ACTION;
173
+ __classPrivateFieldGet(this, _AppUILooper_instances, "m", _AppUILooper_onEffects).call(this, renderType, rsp.effects);
174
+ }
175
+ }, _AppUILooper_renderPost = async function _AppUILooper_renderPost(type, id, data) {
176
+ const req = { blocks: { type, id, data }, state: this.appState };
177
+ let rsp;
178
+ if (this._local?.ready) {
179
+ try {
180
+ rsp = (await this._local?.call('RenderPost', req, this.meta));
181
+ }
182
+ catch (err) {
183
+ if (isCircuitBreaker(err) && err.response)
184
+ rsp = err.response;
185
+ if (!isCircuitBreaker(err) || !this.remote)
186
+ this.onError?.('Error', err);
187
+ if (devvit?.logLocalErrors)
188
+ console.info('remote call', err);
189
+ }
190
+ }
191
+ if (!rsp) {
192
+ try {
193
+ rsp = await this.remote?.RenderPost(req, this.meta);
194
+ }
195
+ catch (err) {
196
+ this.onError?.('Error', err);
197
+ }
198
+ }
199
+ if (rsp) {
200
+ __classPrivateFieldSet(this, _AppUILooper_rendered, true, "f");
201
+ if (rsp.state)
202
+ __classPrivateFieldSet(this, _AppUILooper_appState, rsp.state, "f");
203
+ this.onRenderPost?.(req, rsp);
204
+ __classPrivateFieldGet(this, _AppUILooper_instances, "m", _AppUILooper_onEffects).call(this, req.blocks.type, rsp.effects);
205
+ }
206
+ };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-ui-looper.test.d.ts","sourceRoot":"","sources":["../../library/src/client/app-ui-looper.test.ts"],"names":[],"mappings":""}
@@ -5,6 +5,7 @@ import { WorkerErrorType } from '@devvit/runtime-lite/client/BrowserLiteClient.j
5
5
  import type { ActorRef } from '@devvit/runtimes/common/runtime/ActorRef.js';
6
6
  import { FaceplateEvent } from '@reddit/faceplate/event-types.js';
7
7
  import { LitElement, PropertyValues } from 'lit';
8
+ import { UIApp } from './ui-app.js';
8
9
  import '../blocks/components/devvit-blocks-renderer.js';
9
10
  import '../blocks/components/devvit-blocks-webview.js';
10
11
  import './devvit-animation-player.js';
@@ -49,12 +50,16 @@ export type DevvitUserActionEvent = FaceplateEvent<'devvit-user-action'> & {
49
50
  *
50
51
  * Assumes the loaded program is a CustomPost & UIEventHandler.
51
52
  *
53
+ * The complexity of this class should only be the UI itself and proxying state
54
+ * to devvit-blocks-renderer and AppUILooper. Don't put logic here that you
55
+ * can't unit test.
56
+ *
52
57
  * @slot - Loading / unloaded state.
53
58
  */
54
59
  export declare class DevvitCustomPost extends LitElement {
55
60
  #private;
56
61
  /** Probably a wrapper around the remote runtime (compute-go). */
57
- actorRef?: ActorRef | undefined;
62
+ actorRef?: ActorRef | UIApp | undefined;
58
63
  bundle?: LinkedBundle | undefined;
59
64
  localRuntimeCode?: Blob | undefined;
60
65
  metadata?: Metadata | undefined;
@@ -68,6 +73,7 @@ export declare class DevvitCustomPost extends LitElement {
68
73
  private _isSuspended;
69
74
  private _rootBlock?;
70
75
  constructor();
76
+ connectedCallback(): Promise<void>;
71
77
  disconnectedCallback(): void;
72
78
  get blocksConfig(): string;
73
79
  rerender(): void;
@@ -1 +1 @@
1
- {"version":3,"file":"devvit-custom-post.d.ts","sourceRoot":"","sources":["../../library/src/client/devvit-custom-post.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"devvit-custom-post.d.ts","sourceRoot":"","sources":["../../library/src/client/devvit-custom-post.ts"],"names":[],"mappings":"AAAA,OAAO,kEAAkE,CAAC;AAE1E,OAAO,KAAK,EACV,WAAW,EAEX,MAAM,EACN,YAAY,EACZ,QAAQ,EAER,kBAAkB,EAClB,MAAM,EACN,OAAO,EACR,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAKL,aAAa,EACd,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAiB,eAAe,EAAE,MAAM,kDAAkD,CAAC;AAGlG,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,6CAA6C,CAAC;AAE5E,OAAO,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAElE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAiB,MAAM,KAAK,CAAC;AAOhE,OAAO,EAAa,KAAK,EAAE,MAAM,aAAa,CAAC;AAE/C,OAAO,gDAAgD,CAAC;AACxD,OAAO,+CAA+C,CAAC;AACvD,OAAO,8BAA8B,CAAC;AAEtC,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,oBAAoB,EAAE,gBAAgB,CAAC;KACxC;IACD,UAAU,mBAAmB;QAC3B,sBAAsB,EAAE,uBAAuB,CAAC;QAChD,kBAAkB,EAAE,mBAAmB,CAAC;QACxC,iBAAiB,EAAE,cAAc,CAAC,iBAAiB,CAAC,GAAG;YAAE,MAAM,EAAE,aAAa,CAAA;SAAE,CAAC;QACjF,oBAAoB,EAAE,qBAAqB,CAAC;KAC7C;CACF;AAED,MAAM,MAAM,kBAAkB,GAAG;IAAE,KAAK,EAAE,aAAa,CAAA;CAAE,CAAC;AAC1D,MAAM,MAAM,uBAAuB,GAAG,cAAc,CAAC,sBAAsB,CAAC,GAAG;IAC7E,MAAM,EAAE,kBAAkB,CAAC;CAC5B,CAAC;AACF,MAAM,MAAM,cAAc,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAA;CAAE,CAAC;AAC/E,MAAM,MAAM,mBAAmB,GAAG,cAAc,CAAC,kBAAkB,CAAC,GAAG;IAAE,MAAM,EAAE,cAAc,CAAA;CAAE,CAAC;AAClG,MAAM,MAAM,aAAa,GAAG;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,GAAG,EAAE,OAAO,CAAA;CAAE,CAAC;AACpE,MAAM,MAAM,gBAAgB,GAAG;IAAE,MAAM,EAAE,WAAW,CAAA;CAAE,CAAC;AACvD,MAAM,MAAM,qBAAqB,GAAG,cAAc,CAAC,oBAAoB,CAAC,GAAG;IACzE,MAAM,EAAE,gBAAgB,CAAC;CAC1B,CAAC;AAEF;;;;;;;;;;GAUG;AACH,qBACa,gBAAiB,SAAQ,UAAU;;IAC9C,iEAAiE;IACjC,QAAQ,CAAC,EAAE,QAAQ,GAAG,KAAK,GAAG,SAAS,CAAC;IACxC,MAAM,CAAC,EAAE,YAAY,GAAG,SAAS,CAAC;IAClC,gBAAgB,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC;IACpC,QAAQ,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,GAAG,SAAS,CAAC;IACtC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;IAEzE,qBAAqB,EAAE,OAAO,CAAS;IAGgB,UAAU,EAAE,OAAO,CAAQ;IACzE,cAAc,CAAC,EAAE,kBAAkB,GAAG,SAAS,CAAC;IAExB,OAAO,CAAC,eAAe,CAAwB;IACvE,OAAO,CAAC,YAAY,CAAkB;IACtC,OAAO,CAAC,UAAU,CAAC,CAAoB;;IAcjC,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC;IAKxC,oBAAoB,IAAI,IAAI;IAMrC,IAAI,YAAY,IAAI,MAAM,CAGzB;IAGD,QAAQ,IAAI,IAAI;IAIhB,WAAoB,MAAM,8BAEzB;cAEkB,MAAM;cAoBA,UAAU,CAAC,KAAK,EAAE,cAAc,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;CAuFhF"}
@@ -18,36 +18,33 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (
18
18
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
19
19
  return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
20
20
  };
21
- var _DevvitCustomPost_instances, _DevvitCustomPost_appState, _DevvitCustomPost_customPostActor, _DevvitCustomPost_hasInitialRender, _DevvitCustomPost_lastRequest, _DevvitCustomPost_localClient, _DevvitCustomPost_rerenderTimeout, _DevvitCustomPost_rootHeight, _DevvitCustomPost_rpcQueue, _DevvitCustomPost_uiEventHandlerActor, _DevvitCustomPost_bubble, _DevvitCustomPost_effectEventRender, _DevvitCustomPost_handleEffects, _DevvitCustomPost_handleUserAction, _DevvitCustomPost_initialRender, _DevvitCustomPost_makeRenderContext, _DevvitCustomPost_newRenderRequest, _DevvitCustomPost_onPluginCall, _DevvitCustomPost_processEvent, _DevvitCustomPost_queueEvent, _DevvitCustomPost_queueRenderPost, _DevvitCustomPost_renderPost, _DevvitCustomPost_state_get, _DevvitCustomPost_suspendPost, _DevvitCustomPost_userActionRender;
21
+ var _DevvitCustomPost_instances, _DevvitCustomPost_lastRequest, _DevvitCustomPost_looper, _DevvitCustomPost_rootHeight, _DevvitCustomPost_bubble, _DevvitCustomPost_newRenderContext, _DevvitCustomPost_onError, _DevvitCustomPost_onPluginCall, _DevvitCustomPost_onRenderPost, _DevvitCustomPost_onUserActionEffect, _DevvitCustomPost_onUserActionEvent;
22
22
  import '@reddit/faceplate-ui/templateRenderingStrategy/clientStrategy.js';
23
23
  import { Block } from '@devvit/protos';
24
- import { BlockConfig_Root, BlockRenderEventType, BlockStackDirection, CustomPostDefinition, EffectType, RealtimeDefinition, UIEventHandlerDefinition, } from '@devvit/protos';
24
+ import { BlockConfig_Root, BlockRenderEventType, BlockStackDirection, RealtimeDefinition, } from '@devvit/protos';
25
25
  import * as b64 from 'base64-js';
26
- import { BrowserLiteClient, } from '@devvit/runtime-lite/client/BrowserLiteClient.js';
27
- import { isCircuitBreaker, } from '@devvit/runtime-lite/types/CircuitBreaker.js';
26
+ import { isCircuitBreaker } from '@devvit/runtime-lite/types/CircuitBreaker.js';
27
+ import { asT3ID } from '@devvit/shared-types/tid.js';
28
28
  import { customEvent } from '@reddit/faceplate/lib/custom-event.js';
29
- import { LitElement, html } from 'lit';
29
+ import { LitElement, html, nothing } from 'lit';
30
30
  import { customElement, property, query, state } from 'lit/decorators.js';
31
- import PQueue from 'p-queue';
32
31
  import { DevvitBlocksRenderer } from '../blocks/components/devvit-blocks-renderer.js';
33
32
  import { DEFAULT_ROOT_HEIGHT } from '../blocks/templates/renderBlock.js';
34
33
  import { styles } from '../styles.js';
35
- import { initDevvitGlobal } from '../types/devvit-global.js';
34
+ import { AppUILooper } from './app-ui-looper.js';
35
+ import { RemoteApp } from './ui-app.js';
36
36
  import '../blocks/components/devvit-blocks-renderer.js';
37
37
  import '../blocks/components/devvit-blocks-webview.js';
38
38
  import './devvit-animation-player.js';
39
- initDevvitGlobal();
40
- // Effects that can only be fired as the result of a user action
41
- const USER_ACTION_EFFECTS = [
42
- EffectType.EFFECT_SHOW_TOAST,
43
- EffectType.EFFECT_SHOW_FORM,
44
- EffectType.EFFECT_NAVIGATE_TO_URL,
45
- ];
46
39
  /**
47
40
  * Blocks program renderer.
48
41
  *
49
42
  * Assumes the loaded program is a CustomPost & UIEventHandler.
50
43
  *
44
+ * The complexity of this class should only be the UI itself and proxying state
45
+ * to devvit-blocks-renderer and AppUILooper. Don't put logic here that you
46
+ * can't unit test.
47
+ *
51
48
  * @slot - Loading / unloaded state.
52
49
  */
53
50
  let DevvitCustomPost = class DevvitCustomPost extends LitElement {
@@ -58,53 +55,66 @@ let DevvitCustomPost = class DevvitCustomPost extends LitElement {
58
55
  // to-do: don't use attributes initialized to true. They cannot be passed by
59
56
  // attributes.
60
57
  this.useSandbox = true;
61
- _DevvitCustomPost_appState.set(this, {});
62
- _DevvitCustomPost_customPostActor.set(this, void 0);
63
- _DevvitCustomPost_hasInitialRender.set(this, false);
64
58
  this._isSuspended = false; // eslint-disable-line @typescript-eslint/naming-convention
65
59
  _DevvitCustomPost_lastRequest.set(this, void 0);
66
- _DevvitCustomPost_localClient.set(this, void 0);
67
- _DevvitCustomPost_rerenderTimeout.set(this, void 0);
60
+ _DevvitCustomPost_looper.set(this, new AppUILooper());
68
61
  _DevvitCustomPost_rootHeight.set(this, void 0);
69
- // No concurrency is used to enforce order. Priority is used to allow server
70
- // responses to jump to the front when received.
71
- _DevvitCustomPost_rpcQueue.set(this, new PQueue({ concurrency: 1 }));
72
- _DevvitCustomPost_uiEventHandlerActor.set(this, void 0);
73
- _DevvitCustomPost_handleUserAction.set(this, async (event) => {
74
- await __classPrivateFieldGet(this, _DevvitCustomPost_instances, "m", _DevvitCustomPost_userActionRender).call(this, event.detail.action.id, event.detail.action.data);
75
- });
76
- _DevvitCustomPost_makeRenderContext.set(this, () => ({
62
+ _DevvitCustomPost_newRenderContext.set(this, () => ({
63
+ experimental: this.useExperimentalBlocks,
64
+ onAction: (action) => __classPrivateFieldGet(this, _DevvitCustomPost_looper, "f").queueRenderPost(BlockRenderEventType.RENDER_USER_ACTION, action.id, action.data ?? {}),
77
65
  request: __classPrivateFieldGet(this, _DevvitCustomPost_lastRequest, "f"),
78
- state: __classPrivateFieldGet(this, _DevvitCustomPost_instances, "a", _DevvitCustomPost_state_get),
79
66
  stackDirection: BlockStackDirection.UNRECOGNIZED,
80
- onAction: (action) => void __classPrivateFieldGet(this, _DevvitCustomPost_instances, "m", _DevvitCustomPost_userActionRender).call(this, action.id, action.data),
81
- experimental: this.useExperimentalBlocks,
67
+ state: __classPrivateFieldGet(this, _DevvitCustomPost_looper, "f").appState,
82
68
  }));
83
- _DevvitCustomPost_newRenderRequest.set(this, (type, id, data = {}) => ({
84
- blocks: { type, id, data },
85
- }));
86
- _DevvitCustomPost_onPluginCall.set(this, (id, serviceName, method, args) => {
69
+ _DevvitCustomPost_onError.set(this, (type, err) => {
70
+ if (!isCircuitBreaker(err))
71
+ console.error(err);
72
+ __classPrivateFieldGet(this, _DevvitCustomPost_instances, "m", _DevvitCustomPost_bubble).call(this, 'devvit-ui-error', { err, type });
73
+ });
74
+ _DevvitCustomPost_onPluginCall.set(this, (_id, serviceName, method, args, _meta) => {
87
75
  if (serviceName === RealtimeDefinition.fullName &&
88
- method === RealtimeDefinition.methods.send.name) {
76
+ method === RealtimeDefinition.methods.send.name)
89
77
  __classPrivateFieldGet(this, _DevvitCustomPost_instances, "m", _DevvitCustomPost_bubble).call(this, 'devvit-realtime-send', { event: args });
90
- __classPrivateFieldGet(this, _DevvitCustomPost_localClient, "f")?.onPluginResponse(id, { error: undefined, success: true, value: {} });
91
- }
92
78
  });
93
- _DevvitCustomPost_queueEvent.set(this, (ev) => {
94
- if (ev.queue) {
95
- // TODO: Add QoS for different types of events on alternate queues. For now, treat alternate queues as having
96
- // infinite concurrency.
97
- void __classPrivateFieldGet(this, _DevvitCustomPost_instances, "m", _DevvitCustomPost_processEvent).call(this, ev);
98
- }
99
- else {
100
- void __classPrivateFieldGet(this, _DevvitCustomPost_rpcQueue, "f").add(() => __classPrivateFieldGet(this, _DevvitCustomPost_instances, "m", _DevvitCustomPost_processEvent).call(this, ev));
79
+ _DevvitCustomPost_onRenderPost.set(this, (req, rsp) => {
80
+ var _a, _b;
81
+ this.renderResponse = rsp;
82
+ if (this.renderResponse?.blocks?.ui) {
83
+ __classPrivateFieldSet(this, _DevvitCustomPost_lastRequest, req.blocks, "f");
84
+ this._rootBlock = this.renderResponse.blocks.ui;
85
+ if (!__classPrivateFieldGet(this, _DevvitCustomPost_rootHeight, "f")) {
86
+ // Lock in the initial height
87
+ __classPrivateFieldSet(this, _DevvitCustomPost_rootHeight, this._rootBlock.config?.rootConfig?.height || DEFAULT_ROOT_HEIGHT, "f");
88
+ }
89
+ else {
90
+ (_a = this._rootBlock).config ?? (_a.config = {});
91
+ (_b = this._rootBlock.config).rootConfig ?? (_b.rootConfig = BlockConfig_Root.fromPartial({}));
92
+ this._rootBlock.config.rootConfig.height = __classPrivateFieldGet(this, _DevvitCustomPost_rootHeight, "f");
93
+ }
101
94
  }
102
95
  });
103
- // eslint-disable-next-line @typescript-eslint/no-misused-promises
104
- this.addEventListener('devvit-user-action', __classPrivateFieldGet(this, _DevvitCustomPost_handleUserAction, "f"));
96
+ _DevvitCustomPost_onUserActionEffect.set(this, (fx) => {
97
+ __classPrivateFieldGet(this, _DevvitCustomPost_instances, "m", _DevvitCustomPost_bubble).call(this, 'devvit-ui-effect', {
98
+ effect: fx,
99
+ onEvent: (ev) => __classPrivateFieldGet(this, _DevvitCustomPost_looper, "f").queueEvent(ev),
100
+ });
101
+ });
102
+ _DevvitCustomPost_onUserActionEvent.set(this, (ev) => {
103
+ __classPrivateFieldGet(this, _DevvitCustomPost_looper, "f").queueRenderPost(BlockRenderEventType.RENDER_USER_ACTION, ev.detail.action.id, ev.detail.action.data);
104
+ });
105
+ this.addEventListener('devvit-user-action', __classPrivateFieldGet(this, _DevvitCustomPost_onUserActionEvent, "f"));
106
+ __classPrivateFieldGet(this, _DevvitCustomPost_looper, "f").onError = __classPrivateFieldGet(this, _DevvitCustomPost_onError, "f");
107
+ __classPrivateFieldGet(this, _DevvitCustomPost_looper, "f").onPluginCall = __classPrivateFieldGet(this, _DevvitCustomPost_onPluginCall, "f");
108
+ __classPrivateFieldGet(this, _DevvitCustomPost_looper, "f").onRenderPost = __classPrivateFieldGet(this, _DevvitCustomPost_onRenderPost, "f");
109
+ __classPrivateFieldGet(this, _DevvitCustomPost_looper, "f").onUserAction = __classPrivateFieldGet(this, _DevvitCustomPost_onUserActionEffect, "f");
110
+ }
111
+ async connectedCallback() {
112
+ super.connectedCallback();
113
+ this._isSuspended = false;
105
114
  }
106
115
  disconnectedCallback() {
107
- __classPrivateFieldGet(this, _DevvitCustomPost_instances, "m", _DevvitCustomPost_suspendPost).call(this);
116
+ this._isSuspended = true;
117
+ __classPrivateFieldGet(this, _DevvitCustomPost_looper, "f").unload();
108
118
  super.disconnectedCallback();
109
119
  }
110
120
  get blocksConfig() {
@@ -112,6 +122,7 @@ let DevvitCustomPost = class DevvitCustomPost extends LitElement {
112
122
  return '';
113
123
  return b64.fromByteArray(Block.encode(this._rootBlock).finish());
114
124
  }
125
+ // to-do [studio]: remove.
115
126
  rerender() {
116
127
  this._blocksRenderer?.requestUpdate();
117
128
  }
@@ -127,225 +138,54 @@ let DevvitCustomPost = class DevvitCustomPost extends LitElement {
127
138
  <devvit-blocks-renderer
128
139
  class="${this.scheme ?? ''}"
129
140
  .block="${this._rootBlock}"
130
- .renderContext="${__classPrivateFieldGet(this, _DevvitCustomPost_makeRenderContext, "f")}"
141
+ .renderContext="${__classPrivateFieldGet(this, _DevvitCustomPost_newRenderContext, "f")}"
131
142
  >
132
143
  <slot slot="empty-state-image" name="empty-state-image"></slot>
133
144
  </devvit-blocks-renderer>
134
145
  ${this._isSuspended
135
146
  ? html `<div class="absolute top-0 left-0 w-full h-full bg-transparent border-none"></div>`
136
- : html ``}
147
+ : nothing}
137
148
  </div>`;
138
149
  }
139
150
  async willUpdate(props) {
140
- if (props.has('localRuntimeCode') || props.has('bundle') || props.has('useSandbox')) {
141
- // The runtime or the bundle has changed. Reset.
142
- __classPrivateFieldGet(this, _DevvitCustomPost_localClient, "f")?.quit();
143
- __classPrivateFieldGet(this, _DevvitCustomPost_rpcQueue, "f").clear();
144
- if (__classPrivateFieldGet(this, _DevvitCustomPost_rerenderTimeout, "f") != null)
145
- clearTimeout(__classPrivateFieldGet(this, _DevvitCustomPost_rerenderTimeout, "f"));
146
- __classPrivateFieldSet(this, _DevvitCustomPost_rerenderTimeout, undefined, "f");
147
- }
148
- if (props.has('localRuntimeCode') && this.localRuntimeCode) {
149
- __classPrivateFieldSet(this, _DevvitCustomPost_localClient, new BrowserLiteClient(this.localRuntimeCode, (type, err) => __classPrivateFieldGet(this, _DevvitCustomPost_instances, "m", _DevvitCustomPost_bubble).call(this, 'devvit-ui-error', { type, err }), { callback: __classPrivateFieldGet(this, _DevvitCustomPost_onPluginCall, "f"), plugins: [RealtimeDefinition.fullName] }), "f");
151
+ __classPrivateFieldGet(this, _DevvitCustomPost_looper, "f").meta = this.metadata;
152
+ __classPrivateFieldGet(this, _DevvitCustomPost_looper, "f").postConfig = this.postConfig;
153
+ if (props.has('postId')) {
154
+ __classPrivateFieldGet(this, _DevvitCustomPost_looper, "f").postID = this.postId ? asT3ID(this.postId) : undefined;
150
155
  }
151
156
  if (props.has('actorRef')) {
152
- __classPrivateFieldSet(this, _DevvitCustomPost_customPostActor, this.actorRef?.As(CustomPostDefinition), "f");
153
- __classPrivateFieldSet(this, _DevvitCustomPost_uiEventHandlerActor, this.actorRef?.As(UIEventHandlerDefinition), "f");
154
- }
155
- if (props.has('actorRef') || props.has('bundle') || props.has('useSandbox')) {
156
- if (this.bundle) {
157
- // offline/online, defer initialRender for offline client
158
- try {
159
- await __classPrivateFieldGet(this, _DevvitCustomPost_localClient, "f")?.loadBundle(this.bundle, this.useSandbox);
160
- }
161
- catch (err) {
162
- __classPrivateFieldGet(this, _DevvitCustomPost_instances, "m", _DevvitCustomPost_bubble).call(this, 'devvit-ui-error', { type: 'Error', err });
163
- throw err;
164
- }
165
- // No remote runtime. Queue initial render.
166
- if (!this.actorRef)
167
- __classPrivateFieldGet(this, _DevvitCustomPost_instances, "m", _DevvitCustomPost_initialRender).call(this);
168
- }
169
- else if ((this.bundle && props.has('bundle')) || !__classPrivateFieldGet(this, _DevvitCustomPost_hasInitialRender, "f")) {
170
- // online only
171
- __classPrivateFieldGet(this, _DevvitCustomPost_instances, "m", _DevvitCustomPost_initialRender).call(this);
172
- }
173
- }
174
- else if (props.has('useExperimentalBlocks')) {
175
- __classPrivateFieldGet(this, _DevvitCustomPost_instances, "m", _DevvitCustomPost_initialRender).call(this);
176
- }
157
+ if (!this.actorRef)
158
+ __classPrivateFieldGet(this, _DevvitCustomPost_looper, "f").remote = undefined;
159
+ else if ('As' in this.actorRef)
160
+ __classPrivateFieldGet(this, _DevvitCustomPost_looper, "f").remote = new RemoteApp(this.actorRef);
161
+ else
162
+ __classPrivateFieldGet(this, _DevvitCustomPost_looper, "f").remote = this.actorRef;
163
+ }
164
+ if (props.has('localRuntimeCode'))
165
+ __classPrivateFieldGet(this, _DevvitCustomPost_looper, "f").setLocalSrc(this.localRuntimeCode);
166
+ if (props.has('bundle') && this.bundle) {
167
+ await __classPrivateFieldGet(this, _DevvitCustomPost_looper, "f").load(this.bundle, this.useSandbox);
168
+ // Bundle was _replaced_. Require a new render.
169
+ if (props.get('bundle'))
170
+ __classPrivateFieldGet(this, _DevvitCustomPost_looper, "f").resetAppState();
171
+ }
172
+ if (!__classPrivateFieldGet(this, _DevvitCustomPost_looper, "f").rendered)
173
+ __classPrivateFieldGet(this, _DevvitCustomPost_looper, "f").queueRenderPost(BlockRenderEventType.RENDER_INITIAL, undefined, {});
177
174
  }
178
175
  };
179
- _DevvitCustomPost_appState = new WeakMap();
180
- _DevvitCustomPost_customPostActor = new WeakMap();
181
- _DevvitCustomPost_hasInitialRender = new WeakMap();
182
176
  _DevvitCustomPost_lastRequest = new WeakMap();
183
- _DevvitCustomPost_localClient = new WeakMap();
184
- _DevvitCustomPost_rerenderTimeout = new WeakMap();
177
+ _DevvitCustomPost_looper = new WeakMap();
185
178
  _DevvitCustomPost_rootHeight = new WeakMap();
186
- _DevvitCustomPost_rpcQueue = new WeakMap();
187
- _DevvitCustomPost_uiEventHandlerActor = new WeakMap();
188
- _DevvitCustomPost_handleUserAction = new WeakMap();
189
- _DevvitCustomPost_makeRenderContext = new WeakMap();
190
- _DevvitCustomPost_newRenderRequest = new WeakMap();
179
+ _DevvitCustomPost_newRenderContext = new WeakMap();
180
+ _DevvitCustomPost_onError = new WeakMap();
191
181
  _DevvitCustomPost_onPluginCall = new WeakMap();
192
- _DevvitCustomPost_queueEvent = new WeakMap();
182
+ _DevvitCustomPost_onRenderPost = new WeakMap();
183
+ _DevvitCustomPost_onUserActionEffect = new WeakMap();
184
+ _DevvitCustomPost_onUserActionEvent = new WeakMap();
193
185
  _DevvitCustomPost_instances = new WeakSet();
194
186
  _DevvitCustomPost_bubble = function _DevvitCustomPost_bubble(type, detail) {
195
187
  this.dispatchEvent(customEvent(type, detail));
196
188
  };
197
- _DevvitCustomPost_effectEventRender = async function _DevvitCustomPost_effectEventRender(effectType, effectData) {
198
- await __classPrivateFieldGet(this, _DevvitCustomPost_instances, "m", _DevvitCustomPost_queueRenderPost).call(this, __classPrivateFieldGet(this, _DevvitCustomPost_newRenderRequest, "f").call(this, BlockRenderEventType.RENDER_EFFECT_EVENT, effectType, effectData), undefined);
199
- };
200
- _DevvitCustomPost_handleEffects = async function _DevvitCustomPost_handleEffects(renderType, effects) {
201
- effects.forEach((effect) => {
202
- if (effect.type === EffectType.EFFECT_SEND_EVENT) {
203
- if (effect.sendEvent?.event) {
204
- __classPrivateFieldGet(this, _DevvitCustomPost_queueEvent, "f").call(this, effect.sendEvent.event);
205
- }
206
- }
207
- else if (effect.type === EffectType.EFFECT_RERENDER_UI) {
208
- if (__classPrivateFieldGet(this, _DevvitCustomPost_rerenderTimeout, "f") != null)
209
- clearTimeout(__classPrivateFieldGet(this, _DevvitCustomPost_rerenderTimeout, "f"));
210
- __classPrivateFieldSet(this, _DevvitCustomPost_rerenderTimeout, setTimeout(() => {
211
- void __classPrivateFieldGet(this, _DevvitCustomPost_instances, "m", _DevvitCustomPost_effectEventRender).call(this, 'rerender-effect', {});
212
- }, (effect.rerenderUi?.delaySeconds ?? 0) * 1000), "f");
213
- }
214
- else {
215
- if (renderType !== BlockRenderEventType.RENDER_USER_ACTION ||
216
- USER_ACTION_EFFECTS.includes(effect.type)) {
217
- __classPrivateFieldGet(this, _DevvitCustomPost_instances, "m", _DevvitCustomPost_bubble).call(this, 'devvit-ui-effect', {
218
- effect,
219
- onEvent: (ev) => __classPrivateFieldGet(this, _DevvitCustomPost_queueEvent, "f").call(this, ev),
220
- });
221
- }
222
- }
223
- });
224
- };
225
- _DevvitCustomPost_initialRender = function _DevvitCustomPost_initialRender() {
226
- if (this._isSuspended) {
227
- this._isSuspended = false;
228
- }
229
- __classPrivateFieldSet(this, _DevvitCustomPost_rootHeight, undefined, "f");
230
- __classPrivateFieldSet(this, _DevvitCustomPost_hasInitialRender, true, "f");
231
- __classPrivateFieldGet(this, _DevvitCustomPost_instances, "m", _DevvitCustomPost_queueRenderPost).call(this, __classPrivateFieldGet(this, _DevvitCustomPost_newRenderRequest, "f").call(this, BlockRenderEventType.RENDER_INITIAL), undefined);
232
- };
233
- _DevvitCustomPost_processEvent = async function _DevvitCustomPost_processEvent(ev) {
234
- const req = {
235
- event: ev,
236
- state: __classPrivateFieldGet(this, _DevvitCustomPost_instances, "a", _DevvitCustomPost_state_get),
237
- };
238
- let call;
239
- try {
240
- call = (await __classPrivateFieldGet(this, _DevvitCustomPost_localClient, "f")?.call('HandleUIEvent', req, this.metadata));
241
- }
242
- catch (err) {
243
- if ((!isCircuitBreaker(err) && __classPrivateFieldGet(this, _DevvitCustomPost_localClient, "f")?.ready) || !__classPrivateFieldGet(this, _DevvitCustomPost_uiEventHandlerActor, "f")) {
244
- console.error(err);
245
- __classPrivateFieldGet(this, _DevvitCustomPost_instances, "m", _DevvitCustomPost_bubble).call(this, 'devvit-ui-error', { err, type: 'Error' });
246
- }
247
- else {
248
- if (devvit?.logCircuitBreaks)
249
- console.warn('remote call triggered at', err);
250
- }
251
- // Call remote runtime.
252
- try {
253
- call = await __classPrivateFieldGet(this, _DevvitCustomPost_uiEventHandlerActor, "f")?.HandleUIEvent(req, this.metadata);
254
- }
255
- catch (err) {
256
- __classPrivateFieldGet(this, _DevvitCustomPost_instances, "m", _DevvitCustomPost_bubble).call(this, 'devvit-ui-error', { err, type: 'Error' });
257
- throw err;
258
- }
259
- }
260
- if (call) {
261
- // Can only change state on the main queue.
262
- if (!ev.queue) {
263
- __classPrivateFieldSet(this, _DevvitCustomPost_appState, call.state ?? {}, "f");
264
- }
265
- const renderType = ev.realtimeEvent
266
- ? BlockRenderEventType.RENDER_EFFECT_EVENT
267
- : BlockRenderEventType.RENDER_USER_ACTION;
268
- await __classPrivateFieldGet(this, _DevvitCustomPost_instances, "m", _DevvitCustomPost_handleEffects).call(this, renderType, call.effects);
269
- }
270
- };
271
- _DevvitCustomPost_queueRenderPost = function _DevvitCustomPost_queueRenderPost(req, circuitBreak) {
272
- void __classPrivateFieldGet(this, _DevvitCustomPost_rpcQueue, "f").add(() => __classPrivateFieldGet(this, _DevvitCustomPost_instances, "m", _DevvitCustomPost_renderPost).call(this, req, circuitBreak),
273
- // Cut in line to re-render from server response immediately
274
- { priority: circuitBreak ? 100 : 0 });
275
- };
276
- _DevvitCustomPost_renderPost = async function _DevvitCustomPost_renderPost(req, circuitBreak) {
277
- var _a, _b;
278
- let call;
279
- req.state = __classPrivateFieldGet(this, _DevvitCustomPost_instances, "a", _DevvitCustomPost_state_get);
280
- try {
281
- if (circuitBreak)
282
- throw circuitBreak;
283
- call = (await __classPrivateFieldGet(this, _DevvitCustomPost_localClient, "f")?.call('RenderPost', req, this.metadata));
284
- }
285
- catch (err) {
286
- // Local runtime call failed.
287
- if (!circuitBreak && isCircuitBreaker(err)) {
288
- // Circuit break. Enqueue remote runtime render.
289
- if (err.response) {
290
- call = err.response;
291
- }
292
- __classPrivateFieldGet(this, _DevvitCustomPost_instances, "m", _DevvitCustomPost_queueRenderPost).call(this, req, err);
293
- }
294
- else {
295
- // Render now.
296
- if ((!circuitBreak || !__classPrivateFieldGet(this, _DevvitCustomPost_customPostActor, "f")) && __classPrivateFieldGet(this, _DevvitCustomPost_localClient, "f")?.ready) {
297
- __classPrivateFieldGet(this, _DevvitCustomPost_instances, "m", _DevvitCustomPost_bubble).call(this, 'devvit-ui-error', { err, type: 'Error' });
298
- console.error(err);
299
- }
300
- else {
301
- if (devvit?.logCircuitBreaks)
302
- console.warn('remote call triggered at', err);
303
- }
304
- // Call remote runtime.
305
- try {
306
- call = await __classPrivateFieldGet(this, _DevvitCustomPost_customPostActor, "f")?.RenderPost(req, this.metadata);
307
- }
308
- catch (err) {
309
- __classPrivateFieldGet(this, _DevvitCustomPost_instances, "m", _DevvitCustomPost_bubble).call(this, 'devvit-ui-error', { err, type: 'Error' });
310
- throw err;
311
- }
312
- }
313
- }
314
- if (call) {
315
- this.renderResponse = call;
316
- if (this.renderResponse.state) {
317
- __classPrivateFieldSet(this, _DevvitCustomPost_appState, this.renderResponse.state, "f");
318
- }
319
- if (this.renderResponse?.blocks?.ui) {
320
- __classPrivateFieldSet(this, _DevvitCustomPost_lastRequest, req.blocks, "f");
321
- this._rootBlock = this.renderResponse.blocks.ui;
322
- if (!__classPrivateFieldGet(this, _DevvitCustomPost_rootHeight, "f")) {
323
- // Lock in the initial height
324
- __classPrivateFieldSet(this, _DevvitCustomPost_rootHeight, this._rootBlock.config?.rootConfig?.height || DEFAULT_ROOT_HEIGHT, "f");
325
- }
326
- else {
327
- (_a = this._rootBlock).config ?? (_a.config = {});
328
- (_b = this._rootBlock.config).rootConfig ?? (_b.rootConfig = BlockConfig_Root.fromPartial({}));
329
- this._rootBlock.config.rootConfig.height = __classPrivateFieldGet(this, _DevvitCustomPost_rootHeight, "f");
330
- }
331
- await this.updateComplete;
332
- }
333
- await __classPrivateFieldGet(this, _DevvitCustomPost_instances, "m", _DevvitCustomPost_handleEffects).call(this, req.blocks.type, this.renderResponse.effects);
334
- }
335
- };
336
- _DevvitCustomPost_state_get = function _DevvitCustomPost_state_get() {
337
- const data = { __postData: { thingId: this.postId, config: this.postConfig } };
338
- return Object.assign({}, __classPrivateFieldGet(this, _DevvitCustomPost_appState, "f"), data);
339
- };
340
- _DevvitCustomPost_suspendPost = function _DevvitCustomPost_suspendPost() {
341
- this._isSuspended = true;
342
- if (__classPrivateFieldGet(this, _DevvitCustomPost_rerenderTimeout, "f") != null)
343
- clearTimeout(__classPrivateFieldGet(this, _DevvitCustomPost_rerenderTimeout, "f"));
344
- __classPrivateFieldGet(this, _DevvitCustomPost_localClient, "f")?.quit();
345
- };
346
- _DevvitCustomPost_userActionRender = async function _DevvitCustomPost_userActionRender(actionId, data = {}) {
347
- await __classPrivateFieldGet(this, _DevvitCustomPost_instances, "m", _DevvitCustomPost_queueRenderPost).call(this, __classPrivateFieldGet(this, _DevvitCustomPost_newRenderRequest, "f").call(this, BlockRenderEventType.RENDER_USER_ACTION, actionId, data), undefined);
348
- };
349
189
  __decorate([
350
190
  property({ attribute: false }),
351
191
  __metadata("design:type", Object)
@@ -0,0 +1,18 @@
1
+ import { CustomPost, HandleUIEventRequest, HandleUIEventResponse, Metadata, RenderPostRequest, RenderPostResponse, UIEventHandler, UIRequest, UIResponse } from '@devvit/protos';
2
+ import { ActorRef } from '@devvit/runtimes/common/runtime/ActorRef.js';
3
+ /** An app that can render a custom post and respond to events. */
4
+ export type UIApp = CustomPost & UIEventHandler;
5
+ /**
6
+ * Adapts an ActorRef (usually provided by the deprecated BrowserRuntime) to the
7
+ * APIs implemented by a UIApp.
8
+ * @deprecated
9
+ */
10
+ export declare class RemoteApp implements UIApp {
11
+ #private;
12
+ constructor(ref: ActorRef);
13
+ HandleUIEvent(req: HandleUIEventRequest, meta: Metadata | undefined): Promise<HandleUIEventResponse>;
14
+ RenderPost(req: RenderPostRequest, meta: Metadata | undefined): Promise<RenderPostResponse>;
15
+ RenderPostContent(req: UIRequest, meta: Metadata | undefined): Promise<UIResponse>;
16
+ RenderPostComposer(req: UIRequest, meta: Metadata | undefined): Promise<UIResponse>;
17
+ }
18
+ //# sourceMappingURL=ui-app.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ui-app.d.ts","sourceRoot":"","sources":["../../library/src/client/ui-app.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EAEV,oBAAoB,EACpB,qBAAqB,EACrB,QAAQ,EACR,iBAAiB,EACjB,kBAAkB,EAClB,cAAc,EAEd,SAAS,EACT,UAAU,EACX,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAE,MAAM,6CAA6C,CAAC;AAEvE,kEAAkE;AAClE,MAAM,MAAM,KAAK,GAAG,UAAU,GAAG,cAAc,CAAC;AAEhD;;;;GAIG;AACH,qBAAa,SAAU,YAAW,KAAK;;gBAIzB,GAAG,EAAE,QAAQ;IAKzB,aAAa,CACX,GAAG,EAAE,oBAAoB,EACzB,IAAI,EAAE,QAAQ,GAAG,SAAS,GACzB,OAAO,CAAC,qBAAqB,CAAC;IAIjC,UAAU,CAAC,GAAG,EAAE,iBAAiB,EAAE,IAAI,EAAE,QAAQ,GAAG,SAAS,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAI3F,iBAAiB,CAAC,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,GAAG,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC;IAIlF,kBAAkB,CAAC,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,GAAG,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC;CAGpF"}
@@ -0,0 +1,39 @@
1
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
2
+ if (kind === "m") throw new TypeError("Private method is not writable");
3
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
4
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
5
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
6
+ };
7
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
8
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
9
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
+ };
12
+ var _RemoteApp_customPost, _RemoteApp_uiEventHandler;
13
+ import { CustomPostDefinition, UIEventHandlerDefinition, } from '@devvit/protos';
14
+ /**
15
+ * Adapts an ActorRef (usually provided by the deprecated BrowserRuntime) to the
16
+ * APIs implemented by a UIApp.
17
+ * @deprecated
18
+ */
19
+ export class RemoteApp {
20
+ constructor(ref) {
21
+ _RemoteApp_customPost.set(this, void 0);
22
+ _RemoteApp_uiEventHandler.set(this, void 0);
23
+ __classPrivateFieldSet(this, _RemoteApp_customPost, ref.As(CustomPostDefinition), "f");
24
+ __classPrivateFieldSet(this, _RemoteApp_uiEventHandler, ref.As(UIEventHandlerDefinition), "f");
25
+ }
26
+ HandleUIEvent(req, meta) {
27
+ return __classPrivateFieldGet(this, _RemoteApp_uiEventHandler, "f").HandleUIEvent(req, meta);
28
+ }
29
+ RenderPost(req, meta) {
30
+ return __classPrivateFieldGet(this, _RemoteApp_customPost, "f").RenderPost(req, meta);
31
+ }
32
+ RenderPostContent(req, meta) {
33
+ return __classPrivateFieldGet(this, _RemoteApp_customPost, "f").RenderPostContent(req, meta);
34
+ }
35
+ RenderPostComposer(req, meta) {
36
+ return __classPrivateFieldGet(this, _RemoteApp_customPost, "f").RenderPostComposer(req, meta);
37
+ }
38
+ }
39
+ _RemoteApp_customPost = new WeakMap(), _RemoteApp_uiEventHandler = new WeakMap();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devvit/ui-renderer",
3
- "version": "0.10.17-next-2024-02-23-ad2efe2c5.0",
3
+ "version": "0.10.17-next-2024-02-26-9e49b4eeb.0",
4
4
  "license": "BSD-3-Clause",
5
5
  "repository": {
6
6
  "type": "git",
@@ -54,13 +54,13 @@
54
54
  },
55
55
  "types": "./index.d.ts",
56
56
  "dependencies": {
57
- "@devvit/protos": "0.10.17-next-2024-02-23-ad2efe2c5.0",
58
- "@devvit/runtime-lite": "0.10.17-next-2024-02-23-ad2efe2c5.0",
59
- "@devvit/runtimes": "0.10.17-next-2024-02-23-ad2efe2c5.0",
60
- "@devvit/shared-types": "0.10.17-next-2024-02-23-ad2efe2c5.0",
57
+ "@devvit/protos": "0.10.17-next-2024-02-26-9e49b4eeb.0",
58
+ "@devvit/runtime-lite": "0.10.17-next-2024-02-26-9e49b4eeb.0",
59
+ "@devvit/runtimes": "0.10.17-next-2024-02-26-9e49b4eeb.0",
60
+ "@devvit/shared-types": "0.10.17-next-2024-02-26-9e49b4eeb.0",
61
61
  "@dotlottie/player-component": "2.7.2",
62
62
  "p-queue": "7.3.4",
63
- "rxjs": "7.5.7"
63
+ "rxjs": "7.8.1"
64
64
  },
65
65
  "peerDependencies": {
66
66
  "@reddit/baseplate": "1.1.3",
@@ -84,7 +84,7 @@
84
84
  "devDependencies": {
85
85
  "@devvit/eslint-config": "0.10.16",
86
86
  "@devvit/repo-tools": "0.10.16",
87
- "@devvit/tsconfig": "0.10.17-next-2024-02-23-ad2efe2c5.0",
87
+ "@devvit/tsconfig": "0.10.17-next-2024-02-26-9e49b4eeb.0",
88
88
  "@lit-labs/ssr": "^2.2.3",
89
89
  "@lit/localize": "0.11.4",
90
90
  "@open-wc/testing-helpers": "2.3.0",
@@ -117,5 +117,5 @@
117
117
  "directory": "dist"
118
118
  },
119
119
  "source": "./src/index.ts",
120
- "gitHead": "4896ceefb3d2fbb515e6d9412fbc994b1ec9a708"
120
+ "gitHead": "145863b65a5fbd4d78287d528917021d67b9079a"
121
121
  }
@@ -2,24 +2,22 @@ declare global {
2
2
  var devvit: DevvitGlobal | undefined;
3
3
  }
4
4
  export type DevvitGlobal = {
5
- /** Report circuit break errors in devvit-custom-post. */
6
- logCircuitBreaks?: boolean | undefined;
5
+ /** Report circuit break and local errors in devvit-custom-post. */
6
+ logLocalErrors?: boolean | undefined;
7
7
  };
8
8
  /**
9
9
  * The bookmarklet to enable full debugging is:
10
10
  *
11
11
  * ```
12
- * (() => {
12
+ * javascript:(() => {
13
13
  * globalThis.devvit = {};
14
- * globalThis.devvit.logCircuitBreaks = true;
14
+ * globalThis.devvit.logLocalErrors = true;
15
15
  * const url = new URL(location.href);
16
- * url.searchParams.set('devvitLogCircuitBreaks', '');
16
+ * url.searchParams.set('devvitLogLocalErrors', '');
17
17
  * history.replaceState(undefined, '', url);
18
18
  * console.log(`devvit debug enabled`, globalThis.devvit);
19
19
  * })()
20
20
  * ```
21
- *
22
- * Just add a `javascript:` protocol and remove newlines.
23
21
  */
24
22
  export declare function initDevvitGlobal(): void;
25
23
  //# sourceMappingURL=devvit-global.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"devvit-global.d.ts","sourceRoot":"","sources":["../../library/src/types/devvit-global.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,MAAM,CAAC;IAEb,IAAI,MAAM,EAAE,YAAY,GAAG,SAAS,CAAC;CACtC;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,yDAAyD;IACzD,gBAAgB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CACxC,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAMvC"}
1
+ {"version":3,"file":"devvit-global.d.ts","sourceRoot":"","sources":["../../library/src/types/devvit-global.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,MAAM,CAAC;IAEb,IAAI,MAAM,EAAE,YAAY,GAAG,SAAS,CAAC;CACtC;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,mEAAmE;IACnE,cAAc,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CACtC,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAMvC"}
@@ -2,23 +2,21 @@
2
2
  * The bookmarklet to enable full debugging is:
3
3
  *
4
4
  * ```
5
- * (() => {
5
+ * javascript:(() => {
6
6
  * globalThis.devvit = {};
7
- * globalThis.devvit.logCircuitBreaks = true;
7
+ * globalThis.devvit.logLocalErrors = true;
8
8
  * const url = new URL(location.href);
9
- * url.searchParams.set('devvitLogCircuitBreaks', '');
9
+ * url.searchParams.set('devvitLogLocalErrors', '');
10
10
  * history.replaceState(undefined, '', url);
11
11
  * console.log(`devvit debug enabled`, globalThis.devvit);
12
12
  * })()
13
13
  * ```
14
- *
15
- * Just add a `javascript:` protocol and remove newlines.
16
14
  */
17
15
  export function initDevvitGlobal() {
18
16
  var _a;
19
17
  // Initialize defensively to honor any initializations that might occur in a
20
- // temporary hack elsewhere.
21
- const url = new URL(location.href);
18
+ // temporary hack elsewhere. `globalThis.location` check allows Node.js usage.
19
+ const url = new URL(globalThis.location ? location.href : 'file://');
22
20
  globalThis.devvit ?? (globalThis.devvit = {});
23
- (_a = globalThis.devvit).logCircuitBreaks ?? (_a.logCircuitBreaks = url.searchParams.has('devvitLogCircuitBreaks'));
21
+ (_a = globalThis.devvit).logLocalErrors ?? (_a.logLocalErrors = url.searchParams.has('devvitLogLocalErrors'));
24
22
  }