@worker-protocol/cloudflare 0.8.0 → 0.8.1

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.
package/README.md CHANGED
@@ -202,6 +202,15 @@ Every id is derived from the transition, the resource and its `since`, and the i
202
202
  brings its birth with it. `heartbeat()` compares up to now and points the shared alarm at the next
203
203
  instant; a cron that calls it starts the chain and restarts it if an alarm is lost.
204
204
 
205
+ **What a mixin does internally is a function of its module, never a method.** `private` is
206
+ TypeScript's alone: at run time it is a method on the prototype, and a Worker whose class declares
207
+ one of the same name replaces it silently. Only the methods each mixin's interface lists are on the
208
+ object.
209
+
210
+ `withSubscriptions` also answers `wantedHere(source, events)`: which events a live subscription it
211
+ holds would receive, synchronously, so an object that keeps an outbox and the subscriptions leaves
212
+ the rest out inside the transaction of the write that raised them.
213
+
205
214
  ## Also exported
206
215
 
207
216
  `durableOutcomes`, `durableSubscriptions` and `durableLogs` turn a stub into the store `mount()`
package/dist/lifecycle.js CHANGED
@@ -51,24 +51,31 @@ export function lifecycleEventsBetween(given) {
51
51
  ]),
52
52
  ];
53
53
  }
54
+ /**
55
+ * Owes each event not owed before, in the transaction the caller is in, and answers how many were
56
+ * new. The outbox holds an id back only while it waits; this remembers it after.
57
+ *
58
+ * **A function of the module and not a method of the mixin, and that is the rule for every mixin
59
+ * here.** `private` is TypeScript's alone: at run time it is a method on the prototype like any
60
+ * other, and `Mixed` does not carry it, so a Worker whose class declares a method of the same name
61
+ * replaces it without a word from the compiler or the runtime. Only what a mixin publishes as its
62
+ * interface is a method; what it does internally is reached by no subclass.
63
+ */
64
+ function owe(given) {
65
+ const { storage, enqueue, now, events } = given;
66
+ const sql = migrate(storage, SCHEMA);
67
+ // `RETURNING` answers a row only for an id this inserted, and nothing for one already there.
68
+ const fresh = events.filter((event) => sql
69
+ .exec("INSERT INTO wp_lifecycle_owed (id, at) VALUES (?, ?) ON CONFLICT DO NOTHING RETURNING id", event.id, now)
70
+ .toArray().length > 0);
71
+ enqueue(now, fresh);
72
+ return fresh.length;
73
+ }
54
74
  export function withLifecycle(Base, options = {}) {
55
75
  const catchUpMs = options.catchUpMs ?? 120 * MINUTE;
56
76
  const mostPerRunMs = options.mostPerRunMs ?? 60 * MINUTE;
57
77
  const keepAnnouncedMs = options.keepAnnouncedMs ?? 2 * 24 * 60 * MINUTE;
58
78
  class WithLifecycle extends Base {
59
- /**
60
- * Owes each event not owed before, in the transaction the calling method is in, and answers how
61
- * many were new. The outbox holds an id back only while it waits; this remembers it after.
62
- */
63
- owe(now, events) {
64
- const sql = migrate(this.ctx.storage, SCHEMA);
65
- // `RETURNING` answers a row only for an id this inserted, and nothing for one already there.
66
- const fresh = events.filter((event) => sql
67
- .exec("INSERT INTO wp_lifecycle_owed (id, at) VALUES (?, ?) ON CONFLICT DO NOTHING RETURNING id", event.id, now)
68
- .toArray().length > 0);
69
- this.enqueue(now, fresh);
70
- return fresh.length;
71
- }
72
79
  /**
73
80
  * Performs `write` and owes what it changed: the Tasks and Alerts at `now` before it and after
74
81
  * it, compared in the same transaction as the write. `write` is synchronous, because the
@@ -77,7 +84,12 @@ export function withLifecycle(Base, options = {}) {
77
84
  changing(now, write) {
78
85
  const before = this.snapshot(now);
79
86
  const done = write();
80
- this.owe(now, lifecycleEventsBetween({ previous: before, current: this.snapshot(now), at: now }));
87
+ owe({
88
+ storage: this.ctx.storage,
89
+ enqueue: (at, events) => this.enqueue(at, events),
90
+ now,
91
+ events: lifecycleEventsBetween({ previous: before, current: this.snapshot(now), at: now }),
92
+ });
81
93
  return done;
82
94
  }
83
95
  /**
@@ -110,7 +122,12 @@ export function withLifecycle(Base, options = {}) {
110
122
  at = next;
111
123
  }
112
124
  }
113
- const fresh = this.owe(now, owed);
125
+ const fresh = owe({
126
+ storage: this.ctx.storage,
127
+ enqueue: (at, events) => this.enqueue(at, events),
128
+ now,
129
+ events: owed,
130
+ });
114
131
  sql.exec("INSERT OR REPLACE INTO wp_lifecycle_mark (one, at) VALUES (1, ?)", Math.max(to, from));
115
132
  sql.exec("DELETE FROM wp_lifecycle_owed WHERE at < ?", now - keepAnnouncedMs);
116
133
  await this.flush();
@@ -1,4 +1,4 @@
1
- import type { SubscriptionStore } from "@worker-protocol/hono";
1
+ import { type Publishable, type SubscriptionStore } from "@worker-protocol/hono";
2
2
  import { type DurableObjectClass, type Mixed, type Rpc } from "./durable.ts";
3
3
  /** What `withSubscriptions` adds to a Durable Object. Every subscription crosses as JSON. */
4
4
  export interface SubscriptionMethods {
@@ -16,6 +16,7 @@ export interface SubscriptionMethods {
16
16
  clear: string[];
17
17
  }): void;
18
18
  removeSubscription(id: string): void;
19
+ wantedHere(source: string, events: Publishable[]): boolean[];
19
20
  }
20
21
  export declare function withSubscriptions<B extends DurableObjectClass>(Base: B): Mixed<B, SubscriptionMethods>;
21
22
  /** What `durableSubscriptions` needs of a stub: the methods `withSubscriptions` adds, over RPC. */
@@ -1,3 +1,4 @@
1
+ import { wantedBy, } from "@worker-protocol/hono";
1
2
  import { first } from "./durable.js";
2
3
  import { migrate } from "./schema.js";
3
4
  /**
@@ -103,6 +104,19 @@ export function withSubscriptions(Base) {
103
104
  const sql = migrate(this.ctx.storage, SCHEMA);
104
105
  sql.exec("DELETE FROM wp_subscription WHERE id = ?", id);
105
106
  }
107
+ /**
108
+ * Which of these events a live subscription held here would receive (SUB-13), answered
109
+ * synchronously — so a Worker can leave the rest out of its outbox inside the very transaction
110
+ * of the write that raised them, as `changing()` needs. `source` is the Worker's id, which every
111
+ * event carries (EVT-1); the envelope is the hub's own, so the filters read the same attributes.
112
+ */
113
+ wantedHere(source, events) {
114
+ return wantedBy({
115
+ source,
116
+ subscribed: (type) => this.subscriptionsFor(type).map(parse),
117
+ events,
118
+ });
119
+ }
106
120
  }
107
121
  return WithSubscriptions;
108
122
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@worker-protocol/cloudflare",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "workerProtocolEdition": "0.6",
5
5
  "description": "The protocol's stores in Durable Objects, and the Queues between an outbox and a sink: mixins for ENDP-16, SUB-7, LOG-2 and an outbox, for Workers on Cloudflare",
6
6
  "keywords": [
@@ -39,7 +39,7 @@
39
39
  "NOTICE"
40
40
  ],
41
41
  "dependencies": {
42
- "@worker-protocol/hono": "0.8.0"
42
+ "@worker-protocol/hono": "0.8.1"
43
43
  },
44
44
  "peerDependencies": {
45
45
  "hono": "^4.13.7",