solid-objects 0.14.7 → 0.14.9
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/CHANGELOG.md +19 -0
- package/README.md +8 -10
- package/dist/actor.d.ts +11 -8
- package/dist/actor.d.ts.map +1 -1
- package/dist/actor.js +0 -1
- package/dist/actor.js.map +1 -1
- package/dist/cloudflare/engine.d.ts.map +1 -1
- package/dist/cloudflare/engine.js +5 -6
- package/dist/cloudflare/engine.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/reference.d.ts +4 -1
- package/dist/reference.d.ts.map +1 -1
- package/dist/reference.js.map +1 -1
- package/dist/repository.d.ts +3 -0
- package/dist/repository.d.ts.map +1 -1
- package/dist/repository.js +112 -26
- package/dist/repository.js.map +1 -1
- package/dist/types.d.ts +14 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/api.md +120 -4
- package/docs/parity.md +14 -2
- package/package.json +3 -3
package/docs/api.md
CHANGED
|
@@ -54,8 +54,11 @@ authorization, capability boundaries, and release validation.
|
|
|
54
54
|
- `reference.live`: read-only live signals for an actor, enabled by the
|
|
55
55
|
`solid-objects/signals` entry point documented below.
|
|
56
56
|
- `ActorClass`, `ActorReference`, `ActorMessageSender`, `ActorSnapshot`,
|
|
57
|
-
`ActorOperationNames`, `ActorQueryNames`, `StagedOperations`,
|
|
58
|
-
`ScheduledOperations`: inferred actor-class and
|
|
57
|
+
`ActorOperationNames`, `ActorQueryNames`, `StagedOperations`,
|
|
58
|
+
`ScheduledOperationsFor`, and `ScheduledOperations`: inferred actor-class and
|
|
59
|
+
fluent-dispatch types, plus the legacy dynamic scheduling map.
|
|
60
|
+
- `EffectOptions`: effect arguments and independently checked success/failure
|
|
61
|
+
callback names. Effect names themselves belong to the runtime's global registry.
|
|
59
62
|
- `SnapshotWithIncarnation`: the `{ snapshot, instanceId, revision,
|
|
60
63
|
createdAtMs }` shape returned by `SolidObjectsRuntime.snapshotWithIncarnation`.
|
|
61
64
|
- `MessageReference`: immutable durable message identity with `id`,
|
|
@@ -69,6 +72,10 @@ createdAtMs }` shape returned by `SolidObjectsRuntime.snapshotWithIncarnation`.
|
|
|
69
72
|
`PayloadBroadcasts`, and `PayloadBroadcastValue` describe actor-declared
|
|
70
73
|
transactional work and typed personalized projections.
|
|
71
74
|
|
|
75
|
+
`EffectFailurePayload<Arguments>`, `EffectSuccessPayload<Arguments, Result>`,
|
|
76
|
+
and `SerializedError` describe effect callback messages. They are also exported
|
|
77
|
+
from the browser-safe `solid-objects/core` entry point.
|
|
78
|
+
|
|
72
79
|
`observables()` returns a flat object. Unwrapped values are invalidation-only:
|
|
73
80
|
their real values participate in change detection, but only their names enter
|
|
74
81
|
the durable envelope. Use an explicit marker when wire behavior matters:
|
|
@@ -126,7 +133,7 @@ operation. If you arm one alarm per queued item, only the last one remains:
|
|
|
126
133
|
// Wrong. Every entry overwrites the previous entry's alarm.
|
|
127
134
|
add({ entry }: { entry: Entry }): void {
|
|
128
135
|
this.entries = [...this.entries, entry]
|
|
129
|
-
this.schedule({ at: new Date(entry.waitUntil) }).deliver
|
|
136
|
+
this.schedule({ at: new Date(entry.waitUntil) }).deliver()
|
|
130
137
|
}
|
|
131
138
|
```
|
|
132
139
|
|
|
@@ -136,7 +143,7 @@ own identifier for the item and names that item's alarm, so each item gets one:
|
|
|
136
143
|
```typescript
|
|
137
144
|
add({ entry }: { entry: Entry }): void {
|
|
138
145
|
this.entries = [...this.entries, entry]
|
|
139
|
-
this.schedule({ at: new Date(entry.waitUntil), key: entry.id }).deliver
|
|
146
|
+
this.schedule({ at: new Date(entry.waitUntil), key: entry.id }).deliver()
|
|
140
147
|
}
|
|
141
148
|
```
|
|
142
149
|
|
|
@@ -155,6 +162,115 @@ row per item. It also cannot strand an entry when the runtime coalesces an
|
|
|
155
162
|
occurrence. Prefer it for a large queue of interchangeable items. Prefer `key`
|
|
156
163
|
when one item needs an alarm that you can move on its own.
|
|
157
164
|
|
|
165
|
+
### Typing your onFailure handler
|
|
166
|
+
|
|
167
|
+
An effect callback is an ordinary actor operation. Its payload always includes
|
|
168
|
+
the stable `effectId` and the original serialized `arguments`, including `{}`
|
|
169
|
+
when the effect was emitted without arguments. Use the exported types when a
|
|
170
|
+
watchdog or failure handler needs to correlate work with the current generation:
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
import { Actor, type EffectFailurePayload, type EffectSuccessPayload } from "solid-objects"
|
|
174
|
+
|
|
175
|
+
type RunArguments = { generation: number }
|
|
176
|
+
|
|
177
|
+
class ChatRun extends Actor {
|
|
178
|
+
static override readonly actorType = "ChatRun"
|
|
179
|
+
generation = 0
|
|
180
|
+
status = "idle"
|
|
181
|
+
reply = ""
|
|
182
|
+
|
|
183
|
+
start(): void {
|
|
184
|
+
this.emit("run_model", {
|
|
185
|
+
arguments: { generation: ++this.generation },
|
|
186
|
+
onSuccess: "finishTurn",
|
|
187
|
+
onFailure: "failTurn",
|
|
188
|
+
})
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
failTurn({ arguments: original, error }: EffectFailurePayload<RunArguments>): void {
|
|
192
|
+
if (original.generation !== this.generation) return
|
|
193
|
+
this.status = `${error.name}: ${error.message}`
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
finishTurn({
|
|
197
|
+
arguments: original,
|
|
198
|
+
result,
|
|
199
|
+
}: EffectSuccessPayload<RunArguments, { reply: string }>): void {
|
|
200
|
+
if (original.generation !== this.generation) return
|
|
201
|
+
this.status = "finished"
|
|
202
|
+
this.reply = result.reply
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Failure payloads contain `error: SerializedError`, with string `name` and
|
|
208
|
+
`message` fields. They do not include a stack or cause. Success payloads contain
|
|
209
|
+
`result`, which can be any JSON value; an undefined effect return becomes
|
|
210
|
+
`null`. The default argument type is `JsonObject` and the default success result
|
|
211
|
+
type is `JsonValue`. Declare argument shapes with a JSON-compatible type alias.
|
|
212
|
+
|
|
213
|
+
These types describe the SQL and Cloudflare callback envelopes. Error messages
|
|
214
|
+
for non-Error throws retain each backend's existing serialization behavior.
|
|
215
|
+
The generic parameters express your application's contract; they do not add
|
|
216
|
+
runtime validation or infer types from `registerEffect()`. Keep registered
|
|
217
|
+
effect results and the handler's declared argument/result types in agreement.
|
|
218
|
+
|
|
219
|
+
### Typed operation references
|
|
220
|
+
|
|
221
|
+
`schedule` and `transmit` infer this actor's operation names and arguments, including
|
|
222
|
+
inside actor methods and for inherited application operations. The returned
|
|
223
|
+
`ScheduledOperationsFor<ActorType>` values return `void` and preserve required,
|
|
224
|
+
optional, and zero-argument operation signatures. No non-null assertion is needed:
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
class ChatRun extends Actor {
|
|
228
|
+
generation = 0
|
|
229
|
+
status = "idle"
|
|
230
|
+
|
|
231
|
+
start({ generation }: { generation: number }): void {
|
|
232
|
+
this.generation = generation
|
|
233
|
+
this.schedule({ at: new Date(Date.now() + 60_000), key: "watchdog" }).recoverIfStuck({
|
|
234
|
+
generation,
|
|
235
|
+
})
|
|
236
|
+
this.emit("run_model", { arguments: { generation }, onFailure: "failTurn" })
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
recoverIfStuck({ generation }: { generation: number }): void {
|
|
240
|
+
if (generation !== this.generation) return
|
|
241
|
+
this.status = "recovering"
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
failTurn({ error }: { error: { message: string } }): void {
|
|
245
|
+
this.status = error.message
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Misspelled operations/callbacks, state properties, queries, and Actor infrastructure
|
|
251
|
+
are rejected. `emit` checks each callback independently: widening one callback to
|
|
252
|
+
`string` does not disable literal checking of the other. A deliberately widened
|
|
253
|
+
`string` callback retains runtime validation. Object properties can also widen to
|
|
254
|
+
`string`; preserve literals with `as const` or specialize `EffectOptions` to keep
|
|
255
|
+
static checking when options are stored in a variable. Effect and commit-action names remain
|
|
256
|
+
strings because their registries are runtime-wide; inferring registered names needs
|
|
257
|
+
a separate registry typing design.
|
|
258
|
+
|
|
259
|
+
For deliberately dynamic scheduling, retain the exported legacy map explicitly:
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
const dynamicActor: Actor = this
|
|
263
|
+
const operations: ScheduledOperations = dynamicActor.schedule({ at: deadline })
|
|
264
|
+
operations[operationName]!({ generation })
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
This opts out of operation-name and argument inference and retains the existing
|
|
268
|
+
runtime operation checks. Direct calls, queries, and `sendTo` keep their inference.
|
|
269
|
+
Subclasses that override `schedule` or `transmit` with an explicit legacy
|
|
270
|
+
`ScheduledOperations` return annotation must update their override signatures to
|
|
271
|
+
match the generic Actor methods. This is a compile-time compatibility change;
|
|
272
|
+
runtime scheduling and transmission behavior are unchanged.
|
|
273
|
+
|
|
158
274
|
### Runtime managers
|
|
159
275
|
|
|
160
276
|
Every manager below is available as a property on `SolidObjectsRuntime`; the
|
package/docs/parity.md
CHANGED
|
@@ -29,6 +29,13 @@ such boundary between a gem and its dependents.
|
|
|
29
29
|
|
|
30
30
|
## Status vocabulary
|
|
31
31
|
|
|
32
|
+
Operation-reference typing is runtime-specific: TypeScript infers scheduled and
|
|
33
|
+
transmitted operations from the concrete receiver, and checks literal effect
|
|
34
|
+
callback names. Ruby offers opt-in RBS generation from declared application types
|
|
35
|
+
in [solid-objects-ruby#66](https://github.com/cardmagic/solid-objects-ruby/pull/66).
|
|
36
|
+
Both preserve runtime operation validation and global effect/commit-action names;
|
|
37
|
+
this does not imply automatic TypeScript-style inference in Ruby.
|
|
38
|
+
|
|
32
39
|
- **Native**: the TypeScript runtime provides the capability in a Node-native
|
|
33
40
|
shape.
|
|
34
41
|
- **Partial**: the core exists, but an important Ruby guarantee or operational
|
|
@@ -48,13 +55,13 @@ is needed for the JavaScript stale-result race fix.
|
|
|
48
55
|
| ------------------------------------------------------------------------------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
49
56
|
| Actor registry, durable identity, JSON state, and adjacent state migrations | Native | Ordinary classes, static actor types, inferred state, explicit migrations, and isolated runtime context across every actor-instance callback. |
|
|
50
57
|
| Fluent committed calls and background delivery | Native | `await reference.operation()` and `reference.send.operation()`. |
|
|
51
|
-
| Ordered mailbox, sequence allocation, idempotency, retries, dead letters, leases, renewal, and fenced commits | Native | Relational ready/claimed membership tables, distinct generated request IDs and caller idempotency keys, durable history,
|
|
58
|
+
| Ordered mailbox, sequence allocation, idempotency, retries, dead letters, leases, renewal, and fenced commits | Native | Relational ready/claimed membership tables, distinct generated request IDs and caller idempotency keys, durable history, adapter-appropriate sequence locking, and PostgreSQL/MySQL row locks held from fence validation through commit. |
|
|
52
59
|
| Domain rejection and strict poison ordering | Native | Rejections accept JavaScript identifier-style codes and roll back without retry; invalid codes fail terminally, while retryable failures block later operations until completion or dead-lettering. |
|
|
53
60
|
| Bounded activation passes and hot-actor fairness | Native | Configurable turn-count and elapsed-time budgets bound each pass, then move only that actor's already-due memberships behind actors already waiting. |
|
|
54
61
|
| Bounded claim candidate scan | Native | A configurable ordered scan continues to another ready actor when a worker loses the first candidate's lease race. |
|
|
55
62
|
| Backpressure and payload caps | Partial | Serialization enforces a shared maximum JSON nesting depth, raising `InvalidPayload`, and an optional caller-supplied `maxBytes` limit, raising `PayloadTooLarge`; reminder names are bounded to 255 characters. Distributed per-actor rate limits and global admission control do not exist yet, matching the open Ruby roadmap item. |
|
|
56
63
|
| Idle activation cache | Native | Long-running workers retain hydrated actors under renewable fenced leases, restore public state after failed turns, and release on timeout, fairness yield, lease loss, or shutdown. |
|
|
57
|
-
| Transactional effects and outcome operations | Native | At-least-once handlers receive immutable stable effect, attempt, source-message, and actor identity; success and failure operations also receive the originally staged arguments for correlation.
|
|
64
|
+
| Transactional effects and outcome operations | Native | At-least-once handlers receive immutable stable effect, attempt, source-message, and actor identity; success and failure operations also receive the originally staged arguments for correlation. Typed callback envelopes are exported. |
|
|
58
65
|
| Actor-to-actor delivery | Native | `sendTo(reference).operation()` stages delivery in the source actor commit. |
|
|
59
66
|
| One-shot and recurring reminders | Native | Scheduling, replacement events, catch-up policy, stale-claim recovery, pausing, authorized inspection, and idempotent resume are implemented. |
|
|
60
67
|
| Same-database commit actions | Native | Registered actions receive source-message identity, mailbox sequence, activation generation, and the fenced transaction connection. |
|
|
@@ -65,6 +72,11 @@ is needed for the JavaScript stale-result race fix.
|
|
|
65
72
|
| Result recovery and sync timeout diagnostics | Native | Status, result, and wait reauthorize the stored operation; terminal failure raises structured `MessageFailed`; whole-call adapter deadlines distinguish enqueue, wait, database, activation, and mailbox blockers. |
|
|
66
73
|
| Result lookup by request ID | Planned | This is also an open Ruby roadmap item and will be implemented in both runtimes when its authorization shape is settled. |
|
|
67
74
|
|
|
75
|
+
Effect callback envelopes are typed with `EffectFailurePayload`,
|
|
76
|
+
`EffectSuccessPayload`, and `SerializedError` in both SQL and Cloudflare.
|
|
77
|
+
Ruby RBS contracts are tracked in cardmagic/solid-objects-ruby#64 and preserve
|
|
78
|
+
Ruby field names; this does not change runtime delivery semantics.
|
|
79
|
+
|
|
68
80
|
## Operations
|
|
69
81
|
|
|
70
82
|
| Capability | Status | TypeScript shape or remaining work |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "solid-objects",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.9",
|
|
4
4
|
"description": "Race-free realtime state per application identity, backed by your SQL database",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -136,14 +136,14 @@
|
|
|
136
136
|
"@types/node": "^24.0.0",
|
|
137
137
|
"@types/pg": "^8.21.0",
|
|
138
138
|
"@types/ws": "^8.18.1",
|
|
139
|
-
"@vitest/coverage-v8": "^4.1.
|
|
139
|
+
"@vitest/coverage-v8": "^4.1.11",
|
|
140
140
|
"mysql2": "^3.23.3",
|
|
141
141
|
"pg": "^8.23.0",
|
|
142
142
|
"prettier": "^3.9.6",
|
|
143
143
|
"redis": "^6.2.1",
|
|
144
144
|
"signal-polyfill": "^0.2.2",
|
|
145
145
|
"typescript": "^5.9.0",
|
|
146
|
-
"vitest": "^4.1.
|
|
146
|
+
"vitest": "^4.1.11",
|
|
147
147
|
"wrangler": "^4.129.0",
|
|
148
148
|
"ws": "^8.21.3"
|
|
149
149
|
},
|