#4395

feat(events): simplify onPersistent delivery

OpenSalazareowants to merge intofrom

puter.events.onPersistent() no longer routes deliveries. delivery: 'broadcast' | 'single' becomes a boolean single, and targets goes away.

API

  • onPersistent({ ..., single: true }) replaces delivery: 'single'. It still needs handlerName, and keeps today's behavior: each event is leased to one consumer (connected clients first, then the events worker) and stays owed until acked, one at a time.
  • Without single, every event runs the app's published handler in its events worker (when the subscription is an app's and has a handlerName) and is also sent to every connected socket, including onLocal() subscribers. Before, the server picked one: the socket copy carried skipHandler whenever the worker took it, so the page only ran the handler when the worker couldn't. Now both run. skipHandler is only set for events past the handler-chain limit.
  • delivery: 'single' | 'broadcast' is still accepted (conflicting single/delivery is a 400). targets is accepted and ignored, on both persistent and session subscribes.
  • Subscription objects (onPersistent() result, list()) return single: boolean instead of delivery and targets.
  • events:background consent is required when an app subscribes with a handlerName, instead of whenever the row targeted worker (which was the app default). An app subscription without a handler no longer needs consent. Revoking consent suspends the app's rows that have a handler.

Existing subscriptions

No schema change. Stored rows are read by delivery alone: 'single' is single, anything else is the default. The targets column is still written (with what older servers expect) but never read. So:

  • Default rows that had targets: ['socket'] and a handler now also run in the worker. The run is still gated by events:background on each delivery, so a row created without consent gets no worker run.
  • single rows created with targets: ['worker'] (as the background-work recipe suggested) are now offered to connected clients of the app first, like every other single row. The recipe is updated.
  • Rows with only push (reserved, never delivered) now deliver normally.

The default mode is not retried: a worker run that fails, or an event with nothing connected and no handler, isn't redelivered. Billing is unchanged, one delivery line per event, however many places run it.

Tests

  • vitest --config src/backend/vitest.config.ts src/backend/services/events src/backend/controllers/events src/backend/stores/events src/backend/services/notification src/backend/controllers/apps: 1240 passed.
  • puter.js unit tests for modules/events: 204 passed.
  • puter.js API events suite on the node runner: 44 passed.
  • npm run check:puterjs:types, npm run typecheck: clean.

Routing-only tests (invalid targets, push with single, socket-only/worker-only routing) are removed. New tests cover single, the older delivery spelling, ignored targets, worker plus socket on every default delivery, consent only with a handler, and the store's legacy column handling.

Activity

3
github-actions[bot]2d ago

Coverage Report

StatusCategoryPercentageCovered / Total
🔵Lines95.03%
⬆️ +0.01 pp
39163 / 41210
🔵Statements93.21%
⬆️ +0.01 pp
43012 / 46145
🔵Functions92.56%
⬇️ -0.02 pp
7412 / 8007
🔵Branches84.55%
⬆️ +0.01 pp
30140 / 35647
File Coverage
FileStmtsBranchesFunctionsLinesUncovered Lines
Changed Files
src/backend/services/events/EventForwardService.ts86.37%
🟰 ±0 pp
81.81%
⬆️ +0.65 pp
95.74%
🟰 ±0 pp
90.9%
🟰 ±0 pp
105, 152, 167, 174, 190, 193, 204, 206, 252-253, 265, 266, 307, 309, 360, 366-367, 403, 417, 443, 460, 478-481, 500, 502, 505, 511-512, 517, 557, 575, 577-578, 626-627, 634, 645, 649, 656-658
src/backend/services/events/EventsService.ts90.32%
⬆️ +0.05 pp
86.06%
⬇️ -0.12 pp
94.09%
⬇️ -0.12 pp
94.85%
⬆️ +0.15 pp
243-246, 412, 423-426, 440, 480-482, 497, 510, 539-542, 546, 564, 636, 652, 656, 661, 671, 695, 697, 708-709, 718, 746, 748, 751, 767, 771, 773, 789, 869, 871, 902, 903, 979, 986, 997, 999, 1002, 1008, 1049-1052, 1054-1057, 1103, 1121, 1123, 1127, 1203, 1219-1223, 1246-1250, 1278, 1292, 1303, 1317, 1326, 1338-1342, 1356, 1373, 1386, 1409, 1411, 1425, 1434, 1454, 1456, 1458, 1467, 1474, 1480, 1575, 1584, 1632, 1651, 1662-1666, 1683-1684, 1697, 1762, 1861, 1915, 1928, 1981, 2006, 2008, 2009, 2012, 2051, 2091, 2149, 2153, 2156-2157, 2170, 2188, 2229, 2240, 2242, 2244, 2270, 2307, 2385, 2406, 2468, 2537, 2542, 2547, 2567, 2574, 2582, 2588, 2619-2620, 2708-2712, 2770, 2832, 2846, 2858, 2886, 2889-2897, 2914, 2933-2937, 2955, 2960, 2965, 2974, 3022, 3056-3059, 3099-3102, 3184, 3199, 3288, 3345-3349, 3398, 3407, 3420-3425, 3453, 3461, 3562, 3583, 3594, 3602, 3622, 3646-3650, 3682, 3685, 3695, 3707, 3717-3718, 3737, 3782, 3788-3789, 3968-3974, 3986-3987, 4003-4004, 4014
src/backend/services/events/authorization.ts93.67%
⬇️ -0.15 pp
92.85%
🟰 ±0 pp
100%
🟰 ±0 pp
97.01%
⬇️ -0.09 pp
128, 131, 192, 248, 252
src/backend/stores/events/DurableSubscriptionStore.ts96.17%
⬆️ +0.54 pp
89.91%
⬆️ +0.60 pp
93.75%
⬇️ -0.58 pp
97.59%
⬆️ +0.27 pp
353, 400, 419, 703-708, 805-818
src/backend/stores/events/types.ts100%
🟰 ±0 pp
100%
🟰 ±0 pp
100%
🟰 ±0 pp
100%
🟰 ±0 pp
Generated in workflow #3048 for commit 24623a5 by the Vitest Coverage Report Action
github-actions[bot]2d ago

Coverage Report for puter.js SDK

StatusCategoryPercentageCovered / Total
🔵Lines59.48%
⬇️ -0.04 pp
5283 / 8881
🔵Statements57.59%
⬇️ -0.03 pp
5628 / 9772
🔵Functions59.33%
⬇️ -0.06 pp
976 / 1645
🔵Branches52.68%
⬇️ -0.05 pp
3584 / 6803
File Coverage
FileStmtsBranchesFunctionsLinesUncovered Lines
Changed Files
src/puter-js/src/modules/events/onPersistent.js94.44%
🟰 ±0 pp
82.85%
⬇️ -2.44 pp
100%
🟰 ±0 pp
94.11%
🟰 ±0 pp
51-54
src/puter-js/src/modules/events/lib/channel.js76.4%
🟰 ±0 pp
59.87%
🟰 ±0 pp
84.9%
🟰 ±0 pp
81.3%
🟰 ±0 pp
103, 133-134, 139, 239-241, 310-311, 331-332, 339, 344-345, 368-372, 375, 382, 423, 428, 430-431, 441, 451, 460-461, 468-499, 508, 536, 559, 577, 612, 634-636, 669, 682-683, 692, 701, 703, 749, 753-755
Generated in workflow #1673 for commit 24623a5 by the Vitest Coverage Report Action