solid-objects 0.12.1 → 0.13.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.
Files changed (85) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +228 -953
  3. package/dist/actor.d.ts +12 -0
  4. package/dist/actor.d.ts.map +1 -1
  5. package/dist/actor.js +25 -2
  6. package/dist/actor.js.map +1 -1
  7. package/dist/browser/components.d.ts.map +1 -1
  8. package/dist/browser/components.js +4 -1
  9. package/dist/browser/components.js.map +1 -1
  10. package/dist/browser/index.d.ts +1 -0
  11. package/dist/browser/index.d.ts.map +1 -1
  12. package/dist/browser/index.js +8 -0
  13. package/dist/browser/index.js.map +1 -1
  14. package/dist/cli.d.ts.map +1 -1
  15. package/dist/cli.js +13 -1
  16. package/dist/cli.js.map +1 -1
  17. package/dist/configuration.d.ts +1 -0
  18. package/dist/configuration.d.ts.map +1 -1
  19. package/dist/configuration.js.map +1 -1
  20. package/dist/doctor.d.ts.map +1 -1
  21. package/dist/doctor.js +12 -3
  22. package/dist/doctor.js.map +1 -1
  23. package/dist/examples/sqlite-quickstart.js +83 -0
  24. package/dist/examples/sqlite-quickstart.js.map +1 -0
  25. package/dist/index.d.ts +1 -1
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +1 -1
  28. package/dist/index.js.map +1 -1
  29. package/dist/records.d.ts +1 -0
  30. package/dist/records.d.ts.map +1 -1
  31. package/dist/repository.d.ts +4 -1
  32. package/dist/repository.d.ts.map +1 -1
  33. package/dist/repository.js +5 -4
  34. package/dist/repository.js.map +1 -1
  35. package/dist/runtime.d.ts.map +1 -1
  36. package/dist/runtime.js +37 -6
  37. package/dist/runtime.js.map +1 -1
  38. package/dist/schema.d.ts.map +1 -1
  39. package/dist/schema.js +15 -4
  40. package/dist/schema.js.map +1 -1
  41. package/dist/version.d.ts +1 -1
  42. package/dist/version.js +1 -1
  43. package/dist/web/assets.d.ts +4 -0
  44. package/dist/web/assets.d.ts.map +1 -0
  45. package/dist/web/assets.js +11 -0
  46. package/dist/web/assets.js.map +1 -0
  47. package/dist/web/index.d.ts +28 -0
  48. package/dist/web/index.d.ts.map +1 -0
  49. package/dist/web/index.js +642 -0
  50. package/dist/web/index.js.map +1 -0
  51. package/dist/web/node.d.ts +3 -0
  52. package/dist/web/node.d.ts.map +1 -0
  53. package/dist/web/node.js +109 -0
  54. package/dist/web/node.js.map +1 -0
  55. package/dist/web/render.d.ts +64 -0
  56. package/dist/web/render.d.ts.map +1 -0
  57. package/dist/web/render.js +331 -0
  58. package/dist/web/render.js.map +1 -0
  59. package/dist/web/store.d.ts +65 -0
  60. package/dist/web/store.d.ts.map +1 -0
  61. package/dist/web/store.js +303 -0
  62. package/dist/web/store.js.map +1 -0
  63. package/dist/web/types.d.ts +82 -0
  64. package/dist/web/types.d.ts.map +1 -0
  65. package/dist/web/types.js +2 -0
  66. package/dist/web/types.js.map +1 -0
  67. package/docs/api.md +63 -0
  68. package/docs/architecture.md +16 -10
  69. package/docs/authorization.md +12 -3
  70. package/docs/benchmarks.md +123 -0
  71. package/docs/browser-protocol.md +15 -8
  72. package/docs/comparisons.md +36 -0
  73. package/docs/configuration.md +3 -1
  74. package/docs/correctness.md +23 -0
  75. package/docs/dashboard.md +196 -0
  76. package/docs/fit.md +58 -0
  77. package/docs/parity.md +37 -34
  78. package/docs/releasing.md +6 -4
  79. package/docs/state-and-lifecycle.md +10 -0
  80. package/docs/support.md +39 -0
  81. package/examples/failure-recovery/actor.ts +51 -0
  82. package/examples/failure-recovery/demo.ts +233 -0
  83. package/examples/failure-recovery/worker.ts +46 -0
  84. package/examples/sqlite-quickstart.ts +109 -0
  85. package/package.json +25 -4
@@ -0,0 +1,65 @@
1
+ import type { SolidObjectsRuntime } from "../runtime.js";
2
+ export type DashboardRecord = Record<string, unknown>;
3
+ export interface DashboardPageResult {
4
+ readonly records: readonly DashboardRecord[];
5
+ readonly page: number;
6
+ readonly perPage: number;
7
+ readonly total: number;
8
+ readonly lastPage: number;
9
+ }
10
+ export interface DashboardStatistics extends DashboardRecord {
11
+ readonly instances: DashboardRecord;
12
+ readonly mailbox: DashboardRecord;
13
+ readonly effects: DashboardRecord;
14
+ readonly broadcasts: DashboardRecord;
15
+ readonly reminders: DashboardRecord;
16
+ readonly deadLetters: DashboardRecord;
17
+ readonly processes: DashboardRecord;
18
+ readonly serverTime: string;
19
+ }
20
+ export interface DashboardInstanceDetail {
21
+ readonly instance: DashboardRecord;
22
+ readonly readyMessages: readonly DashboardRecord[];
23
+ readonly claimedMessages: readonly DashboardRecord[];
24
+ readonly recentMessages: readonly DashboardRecord[];
25
+ readonly reminders: readonly DashboardRecord[];
26
+ readonly effects: readonly DashboardRecord[];
27
+ readonly broadcasts: readonly DashboardRecord[];
28
+ readonly deadLetters: readonly DashboardRecord[];
29
+ }
30
+ interface PageOptions {
31
+ readonly page?: string | null;
32
+ readonly perPage?: string | null;
33
+ readonly status?: string | null;
34
+ readonly actorType?: string | null;
35
+ readonly actorId?: string | null;
36
+ readonly membership?: string | null;
37
+ }
38
+ export declare class DashboardStore {
39
+ private readonly runtime;
40
+ constructor(runtime: SolidObjectsRuntime);
41
+ statistics(): Promise<DashboardStatistics>;
42
+ instances(options: PageOptions): Promise<DashboardPageResult>;
43
+ mailbox(options: PageOptions): Promise<DashboardPageResult>;
44
+ reminders(options: PageOptions): Promise<DashboardPageResult>;
45
+ effects(options: PageOptions): Promise<DashboardPageResult>;
46
+ broadcasts(options: PageOptions): Promise<DashboardPageResult>;
47
+ deadLetters(options: PageOptions): Promise<DashboardPageResult>;
48
+ processes(options: PageOptions): Promise<DashboardPageResult>;
49
+ instance(id: string): Promise<DashboardInstanceDetail | undefined>;
50
+ message(id: string): Promise<DashboardRecord | undefined>;
51
+ deadLetter(id: string): Promise<DashboardRecord | undefined>;
52
+ dashboard(): Promise<DashboardRecord>;
53
+ setPaused(options: {
54
+ id: string;
55
+ paused: boolean;
56
+ }): Promise<boolean>;
57
+ healthy(): Promise<void>;
58
+ private page;
59
+ private statusPage;
60
+ private grouped;
61
+ private substringExpression;
62
+ private table;
63
+ }
64
+ export {};
65
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/web/store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAA;AAGxD,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAErD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,OAAO,EAAE,SAAS,eAAe,EAAE,CAAA;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAC1B;AAED,MAAM,WAAW,mBAAoB,SAAQ,eAAe;IAC1D,QAAQ,CAAC,SAAS,EAAE,eAAe,CAAA;IACnC,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAA;IACjC,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAA;IACjC,QAAQ,CAAC,UAAU,EAAE,eAAe,CAAA;IACpC,QAAQ,CAAC,SAAS,EAAE,eAAe,CAAA;IACnC,QAAQ,CAAC,WAAW,EAAE,eAAe,CAAA;IACrC,QAAQ,CAAC,SAAS,EAAE,eAAe,CAAA;IACnC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAC5B;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAA;IAClC,QAAQ,CAAC,aAAa,EAAE,SAAS,eAAe,EAAE,CAAA;IAClD,QAAQ,CAAC,eAAe,EAAE,SAAS,eAAe,EAAE,CAAA;IACpD,QAAQ,CAAC,cAAc,EAAE,SAAS,eAAe,EAAE,CAAA;IACnD,QAAQ,CAAC,SAAS,EAAE,SAAS,eAAe,EAAE,CAAA;IAC9C,QAAQ,CAAC,OAAO,EAAE,SAAS,eAAe,EAAE,CAAA;IAC5C,QAAQ,CAAC,UAAU,EAAE,SAAS,eAAe,EAAE,CAAA;IAC/C,QAAQ,CAAC,WAAW,EAAE,SAAS,eAAe,EAAE,CAAA;CACjD;AAED,UAAU,WAAW;IACnB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAChC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAClC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAChC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACpC;AAeD,qBAAa,cAAc;IACb,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,mBAAmB;IAEnD,UAAU,IAAI,OAAO,CAAC,mBAAmB,CAAC;IA8EhD,SAAS,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAuB7D,OAAO,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAc3D,SAAS,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAS7D,OAAO,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAS3D,UAAU,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAS9D,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAa/D,SAAS,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAUvD,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,uBAAuB,GAAG,SAAS,CAAC;IAyExE,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC;IAkBzD,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC;IActD,SAAS,IAAI,OAAO,CAAC,eAAe,CAAC;IAyBrC,SAAS,CAAC,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC;IAWrE,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;YAMhB,IAAI;IA6BlB,OAAO,CAAC,UAAU;YAqBJ,OAAO;IAgBrB,OAAO,CAAC,mBAAmB;IAO3B,OAAO,CAAC,KAAK;CAGd"}
@@ -0,0 +1,303 @@
1
+ const EFFECT_STATUSES = ["pending", "processing", "completed", "dead"];
2
+ const BROADCAST_STATUSES = ["pending", "processing", "delivered", "dead"];
3
+ const REMINDER_STATUSES = ["scheduled", "paused", "completed"];
4
+ const PROCESS_STATUSES = ["running", "draining", "stopped"];
5
+ export class DashboardStore {
6
+ runtime;
7
+ constructor(runtime) {
8
+ this.runtime = runtime;
9
+ }
10
+ async statistics() {
11
+ return this.runtime.settings.database.connection(async (connection) => {
12
+ const now = await connection.nowMilliseconds();
13
+ const [instances, mailbox, effects, broadcasts, reminders, deadLetters, processes] = await Promise.all([
14
+ connection.get(`SELECT COUNT(*) AS total,
15
+ SUM(CASE WHEN paused = 1 THEN 1 ELSE 0 END) AS paused,
16
+ SUM(CASE WHEN activation_expires_at_ms >= ? THEN 1 ELSE 0 END) AS activated
17
+ FROM ${this.table("instances")}`, [now]),
18
+ connection.get(`SELECT COUNT(*) AS ready,
19
+ SUM(CASE WHEN available_at_ms <= ? THEN 1 ELSE 0 END) AS due,
20
+ MIN(CASE WHEN available_at_ms <= ? THEN available_at_ms END) AS oldest_due
21
+ FROM ${this.table("ready_messages")}`, [now, now]),
22
+ this.grouped({
23
+ connection,
24
+ table: "effects",
25
+ column: "status",
26
+ statuses: EFFECT_STATUSES,
27
+ }),
28
+ this.grouped({
29
+ connection,
30
+ table: "broadcasts",
31
+ column: "status",
32
+ statuses: BROADCAST_STATUSES,
33
+ }),
34
+ this.grouped({
35
+ connection,
36
+ table: "reminders",
37
+ column: "status",
38
+ statuses: REMINDER_STATUSES,
39
+ }),
40
+ connection.get(`SELECT COUNT(*) AS total FROM ${this.table("dead_letters")}`),
41
+ this.grouped({
42
+ connection,
43
+ table: "processes",
44
+ column: "shutdown_state",
45
+ statuses: PROCESS_STATUSES,
46
+ }),
47
+ ]);
48
+ const claimed = await connection.get(`SELECT COUNT(*) AS total FROM ${this.table("claimed_messages")}`);
49
+ const reminderDue = await connection.get(`SELECT COUNT(*) AS total FROM ${this.table("reminders")}
50
+ WHERE status = 'scheduled' AND run_at_ms <= ?`, [now]);
51
+ const oldestDue = numeric(mailbox?.oldest_due);
52
+ return freezeRecord({
53
+ instances: freezeRecord({
54
+ total: numeric(instances?.total),
55
+ paused: numeric(instances?.paused),
56
+ activated: numeric(instances?.activated),
57
+ }),
58
+ mailbox: freezeRecord({
59
+ ready: numeric(mailbox?.ready),
60
+ due: numeric(mailbox?.due),
61
+ claimed: numeric(claimed?.total),
62
+ latency: oldestDue === 0 ? 0 : Math.max(0, (now - oldestDue) / 1_000),
63
+ }),
64
+ effects,
65
+ broadcasts,
66
+ reminders: freezeRecord({ ...reminders, due: numeric(reminderDue?.total) }),
67
+ deadLetters: freezeRecord({ total: numeric(deadLetters?.total) }),
68
+ processes,
69
+ serverTime: new Date(now).toISOString(),
70
+ });
71
+ });
72
+ }
73
+ instances(options) {
74
+ const conditions = [];
75
+ const parameters = [];
76
+ if (options.actorType) {
77
+ conditions.push("actor_type = ?");
78
+ parameters.push(options.actorType);
79
+ }
80
+ if (options.actorId) {
81
+ const expression = this.substringExpression("actor_id");
82
+ conditions.push(expression);
83
+ parameters.push(options.actorId);
84
+ }
85
+ return this.page({
86
+ from: this.table("instances"),
87
+ ...(conditions.length === 0 ? {} : { where: conditions.join(" AND ") }),
88
+ parameters,
89
+ order: "updated_at_ms DESC, id DESC",
90
+ }, options);
91
+ }
92
+ mailbox(options) {
93
+ const membership = options.membership === "claimed" ? "claimed_messages" : "ready_messages";
94
+ const timeColumn = membership === "claimed_messages" ? "membership.claimed_at_ms" : "membership.available_at_ms";
95
+ return this.page({
96
+ from: `${this.table("messages")} messages
97
+ INNER JOIN ${this.table(membership)} membership ON membership.message_id = messages.id`,
98
+ order: `${timeColumn} ASC, messages.id ASC`,
99
+ }, options);
100
+ }
101
+ reminders(options) {
102
+ return this.statusPage({
103
+ table: "reminders",
104
+ statuses: REMINDER_STATUSES,
105
+ order: "run_at_ms ASC, id ASC",
106
+ options,
107
+ });
108
+ }
109
+ effects(options) {
110
+ return this.statusPage({
111
+ table: "effects",
112
+ statuses: EFFECT_STATUSES,
113
+ order: "available_at_ms DESC, id DESC",
114
+ options,
115
+ });
116
+ }
117
+ broadcasts(options) {
118
+ return this.statusPage({
119
+ table: "broadcasts",
120
+ statuses: BROADCAST_STATUSES,
121
+ order: "available_at_ms DESC, id DESC",
122
+ options,
123
+ });
124
+ }
125
+ deadLetters(options) {
126
+ return this.page({
127
+ from: `${this.table("dead_letters")} dead_letters
128
+ INNER JOIN ${this.table("messages")} messages ON messages.id = dead_letters.message_id`,
129
+ select: "dead_letters.id, dead_letters.message_id, dead_letters.instance_id, dead_letters.attempts, dead_letters.error, dead_letters.created_at_ms, dead_letters.retried_message_id, messages.actor_type, messages.actor_id, messages.operation, messages.delivery_mode, messages.arguments",
130
+ order: "dead_letters.created_at_ms DESC, dead_letters.id DESC",
131
+ }, options);
132
+ }
133
+ processes(options) {
134
+ return this.statusPage({
135
+ table: "processes",
136
+ statusColumn: "shutdown_state",
137
+ statuses: PROCESS_STATUSES,
138
+ order: "heartbeat_at_ms DESC, id DESC",
139
+ options,
140
+ });
141
+ }
142
+ async instance(id) {
143
+ return this.runtime.settings.database.connection(async (connection) => {
144
+ const instance = await connection.get(`SELECT * FROM ${this.table("instances")} WHERE id = ?`, [id]);
145
+ if (!instance)
146
+ return undefined;
147
+ const related = { instanceId: id, limit: 10 };
148
+ const [readyMessages, claimedMessages, recentMessages, reminders, effects, broadcasts, deadLetters,] = await Promise.all([
149
+ connection.all(`SELECT messages.*, membership.available_at_ms
150
+ FROM ${this.table("messages")} messages
151
+ INNER JOIN ${this.table("ready_messages")} membership ON membership.message_id = messages.id
152
+ WHERE messages.instance_id = ? ORDER BY messages.sequence ASC LIMIT ?`, [related.instanceId, related.limit]),
153
+ connection.all(`SELECT messages.*, membership.claimed_at_ms
154
+ FROM ${this.table("messages")} messages
155
+ INNER JOIN ${this.table("claimed_messages")} membership ON membership.message_id = messages.id
156
+ WHERE messages.instance_id = ? ORDER BY messages.sequence ASC LIMIT ?`, [related.instanceId, related.limit]),
157
+ connection.all(`SELECT * FROM ${this.table("messages")} WHERE instance_id = ?
158
+ ORDER BY sequence DESC LIMIT ?`, [related.instanceId, related.limit]),
159
+ connection.all(`SELECT * FROM ${this.table("reminders")} WHERE instance_id = ?
160
+ ORDER BY run_at_ms ASC LIMIT ?`, [related.instanceId, related.limit]),
161
+ connection.all(`SELECT * FROM ${this.table("effects")} WHERE instance_id = ?
162
+ ORDER BY available_at_ms DESC LIMIT ?`, [related.instanceId, related.limit]),
163
+ connection.all(`SELECT * FROM ${this.table("broadcasts")} WHERE instance_id = ?
164
+ ORDER BY available_at_ms DESC LIMIT ?`, [related.instanceId, related.limit]),
165
+ connection.all(`SELECT dead_letters.*, messages.actor_type, messages.actor_id, messages.operation,
166
+ messages.delivery_mode, messages.arguments
167
+ FROM ${this.table("dead_letters")} dead_letters
168
+ INNER JOIN ${this.table("messages")} messages ON messages.id = dead_letters.message_id
169
+ WHERE dead_letters.instance_id = ? ORDER BY dead_letters.created_at_ms DESC LIMIT ?`, [related.instanceId, related.limit]),
170
+ ]);
171
+ return Object.freeze({
172
+ instance: freezeRecord(instance),
173
+ readyMessages: freezeRecords(readyMessages),
174
+ claimedMessages: freezeRecords(claimedMessages),
175
+ recentMessages: freezeRecords(recentMessages),
176
+ reminders: freezeRecords(reminders),
177
+ effects: freezeRecords(effects),
178
+ broadcasts: freezeRecords(broadcasts),
179
+ deadLetters: freezeRecords(deadLetters),
180
+ });
181
+ });
182
+ }
183
+ message(id) {
184
+ return this.runtime.settings.database.connection(async (connection) => {
185
+ const record = await connection.get(`SELECT messages.*,
186
+ CASE WHEN ready.message_id IS NOT NULL THEN 'ready'
187
+ WHEN claimed.message_id IS NOT NULL THEN 'claimed'
188
+ WHEN messages.completed_at_ms IS NOT NULL THEN 'completed'
189
+ ELSE 'history' END AS membership
190
+ FROM ${this.table("messages")} messages
191
+ LEFT JOIN ${this.table("ready_messages")} ready ON ready.message_id = messages.id
192
+ LEFT JOIN ${this.table("claimed_messages")} claimed ON claimed.message_id = messages.id
193
+ WHERE messages.id = ?`, [id]);
194
+ return record ? freezeRecord(record) : undefined;
195
+ });
196
+ }
197
+ deadLetter(id) {
198
+ return this.runtime.settings.database.connection(async (connection) => {
199
+ const record = await connection.get(`SELECT dead_letters.*, messages.actor_type, messages.actor_id, messages.operation,
200
+ messages.delivery_mode, messages.arguments
201
+ FROM ${this.table("dead_letters")} dead_letters
202
+ INNER JOIN ${this.table("messages")} messages ON messages.id = dead_letters.message_id
203
+ WHERE dead_letters.id = ?`, [id]);
204
+ return record ? freezeRecord(record) : undefined;
205
+ });
206
+ }
207
+ async dashboard() {
208
+ return this.runtime.settings.database.connection(async (connection) => {
209
+ const [processes, deadLetters, instancesByType] = await Promise.all([
210
+ connection.all(`SELECT * FROM ${this.table("processes")} ORDER BY heartbeat_at_ms DESC LIMIT 10`),
211
+ connection.all(`SELECT dead_letters.*, messages.actor_type, messages.actor_id, messages.operation
212
+ FROM ${this.table("dead_letters")} dead_letters
213
+ INNER JOIN ${this.table("messages")} messages ON messages.id = dead_letters.message_id
214
+ ORDER BY dead_letters.created_at_ms DESC LIMIT 10`),
215
+ connection.all(`SELECT actor_type, COUNT(*) AS count FROM ${this.table("instances")}
216
+ GROUP BY actor_type ORDER BY count DESC LIMIT 12`),
217
+ ]);
218
+ return freezeRecord({
219
+ processes: freezeRecords(processes),
220
+ deadLetters: freezeRecords(deadLetters),
221
+ instancesByType: freezeRecords(instancesByType),
222
+ });
223
+ });
224
+ }
225
+ async setPaused(options) {
226
+ const result = await this.runtime.settings.database.transaction(async (connection) => {
227
+ const now = await connection.nowMilliseconds();
228
+ return connection.run(`UPDATE ${this.table("instances")} SET paused = ?, updated_at_ms = ? WHERE id = ?`, [options.paused, now, options.id]);
229
+ });
230
+ return result.changes > 0;
231
+ }
232
+ async healthy() {
233
+ await this.runtime.settings.database.connection((connection) => connection.get(`SELECT 1 AS healthy FROM ${this.table("schema_migrations")} LIMIT 1`));
234
+ }
235
+ async page(query, options) {
236
+ const perPage = boundedInteger(options.perPage, { fallback: 25, maximum: 200 });
237
+ return this.runtime.settings.database.connection(async (connection) => {
238
+ const where = query.where ? ` WHERE ${query.where}` : "";
239
+ const parameters = [...(query.parameters ?? [])];
240
+ const count = await connection.get(`SELECT COUNT(*) AS count FROM ${query.from}${where}`, parameters);
241
+ const total = numeric(count?.count);
242
+ const lastPage = Math.max(Math.ceil(total / perPage), 1);
243
+ const page = Math.min(boundedInteger(options.page, { fallback: 1, maximum: lastPage }), lastPage);
244
+ const records = await connection.all(`SELECT ${query.select ?? "*"} FROM ${query.from}${where} ORDER BY ${query.order} LIMIT ? OFFSET ?`, [...parameters, perPage, (page - 1) * perPage]);
245
+ return Object.freeze({
246
+ records: freezeRecords(records),
247
+ page,
248
+ perPage,
249
+ total,
250
+ lastPage,
251
+ });
252
+ });
253
+ }
254
+ statusPage(options) {
255
+ const status = options.statuses.includes(options.options.status ?? "")
256
+ ? options.options.status
257
+ : undefined;
258
+ const statusColumn = options.statusColumn ?? "status";
259
+ return this.page({
260
+ from: this.table(options.table),
261
+ ...(status ? { where: `${statusColumn} = ?`, parameters: [status] } : {}),
262
+ order: options.order,
263
+ }, options.options);
264
+ }
265
+ async grouped(options) {
266
+ const { connection, table, column, statuses } = options;
267
+ const rows = await connection.all(`SELECT ${column} AS status, COUNT(*) AS count FROM ${this.table(table)} GROUP BY ${column}`);
268
+ const counts = new Map(rows.map((row) => [row.status, numeric(row.count)]));
269
+ return freezeRecord(Object.fromEntries(statuses.map((status) => [status, counts.get(status) ?? 0])));
270
+ }
271
+ substringExpression(column) {
272
+ if (this.runtime.settings.database.family === "sqlite")
273
+ return `instr(${column}, ?) > 0`;
274
+ if (this.runtime.settings.database.family === "postgresql")
275
+ return `POSITION(? IN ${column}) > 0`;
276
+ return `LOCATE(?, ${column}) > 0`;
277
+ }
278
+ table(name) {
279
+ return this.runtime.repository.table(name);
280
+ }
281
+ }
282
+ function boundedInteger(value, options) {
283
+ const parsed = Number.parseInt(value ?? "", 10);
284
+ if (!Number.isSafeInteger(parsed) || parsed < 1)
285
+ return options.fallback;
286
+ return Math.min(parsed, options.maximum);
287
+ }
288
+ function numeric(value) {
289
+ if (typeof value === "number")
290
+ return value;
291
+ if (typeof value === "bigint")
292
+ return Number(value);
293
+ if (typeof value === "string")
294
+ return Number(value);
295
+ return 0;
296
+ }
297
+ function freezeRecord(record) {
298
+ return Object.freeze(record);
299
+ }
300
+ function freezeRecords(records) {
301
+ return Object.freeze(records.map(freezeRecord));
302
+ }
303
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/web/store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,82 @@
1
+ import type { IncomingMessage, ServerResponse } from "node:http";
2
+ import type { SolidObjectsRuntime } from "../runtime.js";
3
+ export interface DashboardSession {
4
+ read(key: string): string | undefined | Promise<string | undefined>;
5
+ write(key: string, value: string): void | Promise<void>;
6
+ }
7
+ export interface DashboardRequestContext {
8
+ readonly authorizationContext?: unknown;
9
+ readonly session?: DashboardSession;
10
+ }
11
+ export type DashboardAccess = "authorized" | "authorized-read-only" | "public-read-only";
12
+ export interface DashboardPolicy {
13
+ readonly action: string;
14
+ readonly resource: string;
15
+ }
16
+ export interface DashboardPage {
17
+ readonly title: string;
18
+ readonly content: string;
19
+ readonly status?: number;
20
+ }
21
+ export interface DashboardRenderInput {
22
+ readonly name: string;
23
+ readonly model: Readonly<Record<string, unknown>>;
24
+ readonly defaultHtml: string;
25
+ }
26
+ export type DashboardRenderer = (input: DashboardRenderInput) => string;
27
+ export interface DashboardRouteContext {
28
+ readonly request: Request;
29
+ readonly requestContext: DashboardRequestContext;
30
+ readonly params: Readonly<Record<string, string>>;
31
+ readonly runtime: SolidObjectsRuntime;
32
+ path(path: string): string;
33
+ render(page: DashboardPage): Response;
34
+ escape(value: unknown): string;
35
+ }
36
+ export interface DashboardRoute {
37
+ readonly method: "GET" | "HEAD" | "POST";
38
+ readonly path: string;
39
+ readonly policy: DashboardPolicy;
40
+ handle(context: DashboardRouteContext): DashboardPage | Response | Promise<DashboardPage | Response>;
41
+ }
42
+ export interface DashboardTab {
43
+ readonly label: string;
44
+ readonly path: string;
45
+ }
46
+ export interface DashboardExtension {
47
+ readonly tab?: DashboardTab;
48
+ readonly routes?: readonly DashboardRoute[];
49
+ readonly renderers?: Readonly<Record<string, DashboardRenderer>>;
50
+ readonly middleware?: readonly DashboardMiddleware[];
51
+ }
52
+ export interface DashboardMiddlewareInput {
53
+ readonly request: Request;
54
+ readonly requestContext: DashboardRequestContext;
55
+ }
56
+ export type DashboardMiddleware = (input: DashboardMiddlewareInput, next: () => Promise<Response>) => Promise<Response>;
57
+ export interface DashboardChartLibrary {
58
+ readonly url: string | null;
59
+ readonly integrity?: string | null;
60
+ }
61
+ export interface DashboardOptions {
62
+ readonly runtime: SolidObjectsRuntime;
63
+ readonly mountPath?: string;
64
+ readonly access?: DashboardAccess;
65
+ readonly chartLibrary?: DashboardChartLibrary;
66
+ readonly extensions?: readonly DashboardExtension[];
67
+ readonly middleware?: readonly DashboardMiddleware[];
68
+ }
69
+ export interface NodeDashboardRequestContextResolver {
70
+ (request: IncomingMessage): DashboardRequestContext | Promise<DashboardRequestContext>;
71
+ }
72
+ export interface NodeDashboardHandlerOptions {
73
+ readonly dashboard: SolidObjectsDashboardContract;
74
+ readonly resolveContext: NodeDashboardRequestContextResolver;
75
+ readonly maximumBodyBytes?: number;
76
+ }
77
+ export interface SolidObjectsDashboardContract {
78
+ readonly mountPath: string;
79
+ fetch(request: Request, context: DashboardRequestContext): Promise<Response>;
80
+ }
81
+ export type NodeDashboardHandler = (...argumentsValue: [IncomingMessage, ServerResponse, ((error?: unknown) => void)?]) => void;
82
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/web/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/web/types.ts"],"names":[],"mappings":""}
package/docs/api.md CHANGED
@@ -16,6 +16,11 @@ generic signatures; this index explains the supported role of every export.
16
16
  - `Actor`: base class providing `ref()`, `actorId`, `currentMessage`,
17
17
  `observables()`, `reject()`, `emit()`, `commitAction()`, `schedule()`,
18
18
  `sendTo()`, and protected lifecycle hooks.
19
+ - `broadcastValue(value)`: mark an observable so its changed value enters the
20
+ durable invalidation envelope.
21
+ - `broadcastInvalidation(value)`: compare the real observable value but put
22
+ only its name in the durable envelope when it changes.
23
+ - `ObservableBroadcast`: the immutable marker type returned by either helper.
19
24
  - `VERSION`: running package version.
20
25
  - `ActorClass`, `ActorReference`, `ActorMessageSender`, `ActorSnapshot`,
21
26
  `ActorOperationNames`, `ActorQueryNames`, `StagedOperations`, and
@@ -31,6 +36,25 @@ generic signatures; this index explains the supported role of every export.
31
36
  `PayloadBroadcasts`, and `PayloadBroadcastValue` describe actor-declared
32
37
  transactional work and typed personalized projections.
33
38
 
39
+ `observables()` returns a flat object. Unwrapped values are invalidation-only:
40
+ their real values participate in change detection, but only their names enter
41
+ the durable envelope. Use an explicit marker when wire behavior matters:
42
+
43
+ ```typescript
44
+ override observables(): Record<string, unknown> {
45
+ return {
46
+ version: broadcastValue(this.document.version),
47
+ sidebar: broadcastInvalidation(this.sidebarForCurrentState()),
48
+ }
49
+ }
50
+ ```
51
+
52
+ Both values must be JSON-compatible and are evaluated after each successful
53
+ turn. An invalidation-only value participates in change detection but is never
54
+ written to the broadcast outbox or invalidation envelope. The envelope carries
55
+ its name in `invalidations`, allowing component registries to refresh a
56
+ reauthorized endpoint without exposing the value.
57
+
34
58
  `MessageReference` does not retain an invocation's authorization context.
35
59
  Supply `authorizationContext` to each `status()`, `result()`, and `wait()` call;
36
60
  the runtime reauthorizes the persisted operation every time. Durable results
@@ -90,6 +114,10 @@ The manager types are `DeadLetter`; `ReminderPage`, `ReminderPageOptions`,
90
114
  `AdministrationOptions` carries the application-owned authorization context
91
115
  for administration calls.
92
116
 
117
+ The packaged `solid-objects quickstart` command is config-free and runs the
118
+ SQLite example shipped in the npm artifact. Every other CLI command loads the
119
+ application runtime configured through `--config`.
120
+
93
121
  `ProcessRecord.shutdownState` is `"running"`, `"draining"`, or `"stopped"`;
94
122
  there is no separate `running` field. `RetentionResult.count` means eligible
95
123
  rows for `preview()` and rows actually deleted for `prune()`.
@@ -124,6 +152,11 @@ rows for `preview()` and rows actually deleted for `prune()`.
124
152
  `DatabaseConnection`, `DatabaseFamily`, and `RunResult` support custom database
125
153
  and commit-action integration.
126
154
 
155
+ `BroadcastEvent.observables` contains changed value-broadcast projections.
156
+ `BroadcastEvent.invalidations` contains changed invalidation-only names. The
157
+ runtime always supplies the array; consumers should treat its absence from an
158
+ older or application-produced event as an empty array.
159
+
127
160
  ### Runtime extensions and manual workers
128
161
 
129
162
  `runtime.registerComponent(factory, { count = 1 })` adds application-owned
@@ -225,5 +258,35 @@ and `SyncTimeoutWaitingOn` type timeout diagnostics. See
225
258
  `ComponentRefreshFailure`, and `ComponentRegistryOptions`: framework-neutral
226
259
  refresh contract types.
227
260
 
261
+ `InvalidationEnvelope.observables` contains values and
262
+ `InvalidationEnvelope.invalidations` contains names without values. The
263
+ component registry reacts to names in either location.
264
+
228
265
  The wire format, trust boundary, revision rules, and component semantics are in
229
266
  [Browser protocol](browser-protocol.md).
267
+
268
+ ## `solid-objects/web`
269
+
270
+ - `createDashboard(options)` creates an immutable `SolidObjectsDashboard` with
271
+ a standard `fetch(request, context)` entry point.
272
+ - `createNodeDashboardHandler(options)` adapts the Fetch entry point to
273
+ `node:http` and Connect-compatible middleware.
274
+ - `DashboardOptions` selects the runtime, mount path, `DashboardAccess`, chart library,
275
+ `DashboardExtension` objects, and `DashboardMiddleware` functions.
276
+ - `DashboardRequestContext` supplies the existing administration authorization
277
+ context and an optional `DashboardSession`. Read/write access requires the
278
+ session so its `read()` and `write()` methods can hold the masked CSRF token
279
+ across requests; read-only modes do not create CSRF state.
280
+ - `DashboardRoute`, `DashboardRouteContext`, `DashboardPolicy`, `DashboardPage`,
281
+ and `DashboardTab` define extension pages. Every route requires a policy.
282
+ - `DashboardRenderer`, `DashboardRenderInput`, and `DashboardMiddlewareInput`
283
+ define immutable view overrides and middleware inputs.
284
+ - `DashboardChartLibrary` selects the CDN, a self-hosted script, or disabled
285
+ charts.
286
+ - `NodeDashboardHandler`, `NodeDashboardHandlerOptions`, and
287
+ `NodeDashboardRequestContextResolver` describe the Node adapter.
288
+ - `SolidObjectsDashboardContract` is the minimal Fetch contract accepted by the
289
+ Node adapter.
290
+
291
+ Mounting, authorization actions, CSRF behavior, pages, and extensions are in
292
+ [Operator dashboard](dashboard.md).
@@ -1,8 +1,9 @@
1
1
  # Architecture
2
2
 
3
- Solid Objects is a database-backed virtual actor runtime. An actor is addressed
4
- by `(actor type, actor id)`, processes one durable mailbox turn at a time, and
5
- persists JSON state between activations.
3
+ Solid Objects coordinates stateful TypeScript objects through database-backed
4
+ mailboxes. Each object, called an actor in the API, is addressed by
5
+ `(actor type, actor id)`, processes one durable turn at a time, and persists
6
+ JSON state between activations.
6
7
 
7
8
  A durable message envelope selects an actor `operation` and records its
8
9
  `delivery_mode` as `async`, `sync`, or `internal`. An operation is actor code;
@@ -59,10 +60,10 @@ than waiting for a connection or serialized SQLite slot it cannot release.
59
60
 
60
61
  PostgreSQL notifications are an opt-in latency layer. One event-driven client
61
62
  per runtime listens on role-specific channels before the worker checks durable
62
- state, which closes the listener-startup race without Ruby's connection per
63
- blocking thread. A notification advances a process-local role generation and
64
- wakes every matching waiter. Reconnection and notification loss fall back to
65
- the ordinary polling interval.
63
+ state, which closes the listener-startup race without holding a polling
64
+ connection per worker. A notification advances a process-local role generation
65
+ and wakes every matching waiter. Reconnection and notification loss fall back
66
+ to the ordinary polling interval.
66
67
 
67
68
  The optional Redis adapter provides the same role generations through Pub/Sub
68
69
  for deployments that already operate Redis. It keeps commands and subscriptions
@@ -94,6 +95,12 @@ actor subscriptions, replays a committed observable projection, and follows
94
95
  the durable broadcast outbox in revision order. The browser client applies the
95
96
  same incarnation and revision fence without importing Node APIs.
96
97
 
98
+ Each observable is compared using its computed JSON value. Value-broadcast
99
+ observables persist that changed value in the outbox; invalidation-only
100
+ observables persist only the changed name. This lets a component registry
101
+ refresh a reauthorized view at the same dependency granularity without storing
102
+ or sending private projection values.
103
+
97
104
  Actors may also declare static personalized payload projections. Each requested
98
105
  name is reauthorized as a query and evaluated against a fresh actor hydrated
99
106
  from committed state under that subscriber's session context. Payload names use
@@ -108,6 +115,5 @@ broadcast events through an application-owned shared transport and calls
108
115
  another broker optional for a single Node process while making the
109
116
  cross-process boundary explicit.
110
117
 
111
- This package ports the Solid Objects programming model. It does not share a
112
- database schema or runtime protocol with the Ruby gem and does not reproduce
113
- Cloudflare's placement or edge-runtime guarantees.
118
+ Solid Objects provides database-backed state coordination for Node.js. It does
119
+ not reproduce Cloudflare's placement or edge-runtime guarantees.