solid-objects 0.16.1 → 0.17.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/CHANGELOG.md +84 -0
- package/README.md +11 -2
- package/dist/actor-runtime.d.ts +7 -0
- package/dist/actor-runtime.d.ts.map +1 -1
- package/dist/actor.d.ts +1 -0
- package/dist/actor.d.ts.map +1 -1
- package/dist/actor.js +3 -0
- package/dist/actor.js.map +1 -1
- package/dist/cloudflare/engine.d.ts +1 -0
- package/dist/cloudflare/engine.d.ts.map +1 -1
- package/dist/cloudflare/engine.js +118 -15
- package/dist/cloudflare/engine.js.map +1 -1
- package/dist/cloudflare/records.d.ts +2 -0
- package/dist/cloudflare/records.d.ts.map +1 -1
- package/dist/cloudflare/runtime.d.ts +7 -0
- package/dist/cloudflare/runtime.d.ts.map +1 -1
- package/dist/cloudflare/runtime.js +13 -0
- package/dist/cloudflare/runtime.js.map +1 -1
- package/dist/configuration.d.ts +10 -0
- package/dist/configuration.d.ts.map +1 -1
- package/dist/configuration.js.map +1 -1
- package/dist/diagnostics.d.ts +37 -0
- package/dist/diagnostics.d.ts.map +1 -0
- package/dist/diagnostics.js +73 -0
- package/dist/diagnostics.js.map +1 -0
- package/dist/doctor.js +1 -1
- package/dist/doctor.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/realtime.d.ts.map +1 -1
- package/dist/realtime.js +14 -1
- package/dist/realtime.js.map +1 -1
- package/dist/reference.d.ts +11 -0
- package/dist/reference.d.ts.map +1 -1
- package/dist/reference.js +22 -0
- package/dist/reference.js.map +1 -1
- package/dist/repository.d.ts +1 -1
- package/dist/repository.d.ts.map +1 -1
- package/dist/repository.js +7 -6
- package/dist/repository.js.map +1 -1
- package/dist/runtime.d.ts +8 -0
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +108 -32
- package/dist/runtime.js.map +1 -1
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +20 -5
- package/dist/schema.js.map +1 -1
- package/dist/serialization.js +3 -5
- package/dist/serialization.js.map +1 -1
- package/dist/telemetry.d.ts +22 -0
- package/dist/telemetry.d.ts.map +1 -0
- package/dist/telemetry.js +126 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/transmit.js +2 -2
- package/dist/transmit.js.map +1 -1
- package/dist/turn.js +2 -2
- package/dist/turn.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/wake-up-notification.d.ts.map +1 -1
- package/dist/wake-up-notification.js +9 -1
- package/dist/wake-up-notification.js.map +1 -1
- package/docs/agents.md +300 -0
- package/docs/api.md +25 -3
- package/docs/authorization.md +6 -0
- package/docs/comparisons.md +36 -0
- package/docs/observability.md +192 -0
- package/docs/parity.md +85 -72
- package/docs/releasing.md +12 -2
- package/docs/virtual-actors.md +242 -0
- package/examples/ticket-sale.ts +57 -0
- package/package.json +8 -5
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
# Virtual actors in TypeScript and Node.js
|
|
2
|
+
|
|
3
|
+
## Short answer
|
|
4
|
+
|
|
5
|
+
Yes. Solid Objects is a SQL-backed virtual actor library for TypeScript and
|
|
6
|
+
Node.js. The npm package is `solid-objects`. It gives each actor a stable
|
|
7
|
+
identity, durable state, ordered operations, and automatic activation.
|
|
8
|
+
|
|
9
|
+
The actor runtime runs in your Node.js processes. State and mailboxes live in
|
|
10
|
+
SQLite, PostgreSQL, or MySQL. It needs no broker, no daemon, no Cloudflare
|
|
11
|
+
account, and no new datastore. Redis is optional and only shortens wake-up
|
|
12
|
+
latency.
|
|
13
|
+
|
|
14
|
+
Solid Objects is a pre-1.0 release. Read
|
|
15
|
+
[Compatibility and maturity](#compatibility-and-maturity) before you choose it.
|
|
16
|
+
|
|
17
|
+
## What a virtual actor is
|
|
18
|
+
|
|
19
|
+
A virtual actor is a logical object that always exists by name. The caller
|
|
20
|
+
does not create it, start it, or stop it. The runtime loads it when a message
|
|
21
|
+
arrives and releases it when it is idle. Microsoft Orleans made this model
|
|
22
|
+
known as "virtual actors".
|
|
23
|
+
|
|
24
|
+
Solid Objects implements four properties of that model:
|
|
25
|
+
|
|
26
|
+
| Property | What it means | How Solid Objects does it |
|
|
27
|
+
| -------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
|
28
|
+
| Stable identity | An actor is addressed by type and ID, for example one room for each game. | `Room.ref("room-7")` returns a reference. The reference does not load the actor. |
|
|
29
|
+
| Automatic activation | The first message activates the actor. An idle actor is released. | A process claims a fenced activation lease when work arrives. A worker releases it after `idleDeactivationTimeoutMilliseconds`. |
|
|
30
|
+
| Durable state | State survives process exits and deploys. | Enumerable public fields are a JSON document in SQL. |
|
|
31
|
+
| Ordered turns | One identity runs one operation at a time, in a fixed order. | Each call is a durable mailbox message with a per-actor sequence number. |
|
|
32
|
+
|
|
33
|
+
Different identities run concurrently across processes. One identity is a
|
|
34
|
+
serialization point on purpose.
|
|
35
|
+
|
|
36
|
+
## A small example
|
|
37
|
+
|
|
38
|
+
This program holds one ticket for a buyer and releases the hold after ten
|
|
39
|
+
minutes. It uses the SQLite driver that Node.js includes. Save it as
|
|
40
|
+
`ticket-sale.mts`, so that Node.js loads it as an ES module:
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
import { Actor, configure } from "solid-objects"
|
|
44
|
+
import { sqlite } from "solid-objects/database/sqlite"
|
|
45
|
+
|
|
46
|
+
const HOLD_MILLISECONDS = 10 * 60 * 1000
|
|
47
|
+
|
|
48
|
+
export class TicketSale extends Actor {
|
|
49
|
+
static override readonly actorType = "TicketSale"
|
|
50
|
+
|
|
51
|
+
available = 1
|
|
52
|
+
holds: Record<string, number> = {}
|
|
53
|
+
|
|
54
|
+
hold({ buyer }: { buyer: string }): { held: boolean; available: number } {
|
|
55
|
+
if (this.available === 0 || Object.hasOwn(this.holds, buyer)) {
|
|
56
|
+
return { held: false, available: this.available }
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
this.available -= 1
|
|
60
|
+
this.holds = { ...this.holds, [buyer]: Date.now() }
|
|
61
|
+
this.schedule({ at: new Date(Date.now() + HOLD_MILLISECONDS), key: buyer }).expire({ buyer })
|
|
62
|
+
return { held: true, available: this.available }
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
expire({ buyer }: { buyer: string }): number {
|
|
66
|
+
if (!Object.hasOwn(this.holds, buyer)) return this.available
|
|
67
|
+
|
|
68
|
+
const remainingHolds = { ...this.holds }
|
|
69
|
+
delete remainingHolds[buyer]
|
|
70
|
+
this.holds = remainingHolds
|
|
71
|
+
this.available += 1
|
|
72
|
+
return this.available
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const runtime = configure({
|
|
77
|
+
database: sqlite({ path: process.env.TICKET_DATABASE ?? "tickets.sqlite3" }),
|
|
78
|
+
authorizeMessage: () => true,
|
|
79
|
+
authorizeQuery: () => true,
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
await runtime.install()
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
const sale = TicketSale.ref("event-42")
|
|
86
|
+
|
|
87
|
+
if (process.argv[2] === "work") {
|
|
88
|
+
const controller = new AbortController()
|
|
89
|
+
process.once("SIGINT", () => controller.abort())
|
|
90
|
+
process.once("SIGTERM", () => controller.abort())
|
|
91
|
+
await runtime.run(controller.signal)
|
|
92
|
+
} else {
|
|
93
|
+
const buyers = process.argv.length > 3 ? process.argv.slice(3) : ["ada", "grace"]
|
|
94
|
+
const results = await Promise.all(buyers.map((buyer) => sale.hold({ buyer })))
|
|
95
|
+
console.log(JSON.stringify(results))
|
|
96
|
+
}
|
|
97
|
+
} finally {
|
|
98
|
+
await runtime.close()
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Run the background roles in one terminal. Place two concurrent holds in a
|
|
103
|
+
second terminal:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
npm install solid-objects
|
|
107
|
+
node ticket-sale.mts work
|
|
108
|
+
node ticket-sale.mts hold
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
The two holds enter the same mailbox and commit one at a time, so only one
|
|
112
|
+
buyer gets the ticket. The hold and its reminder commit in one transaction. If
|
|
113
|
+
the worker stops, the reminder stays in `tickets.sqlite3`. It runs when the
|
|
114
|
+
worker starts again.
|
|
115
|
+
|
|
116
|
+
The authorization callbacks above allow every caller. Use them only for a
|
|
117
|
+
local example. The package release check runs this file against the packed
|
|
118
|
+
npm tarball. The source is [`examples/ticket-sale.ts`](../examples/ticket-sale.ts).
|
|
119
|
+
|
|
120
|
+
For more setup, use one of these guides:
|
|
121
|
+
|
|
122
|
+
- `npx solid-objects quickstart --yes` runs a packaged proof of concurrent
|
|
123
|
+
calls to one identity.
|
|
124
|
+
- [The README](../README.md#installation) has the installation steps.
|
|
125
|
+
- [The agent guide](agents.md) gives setup and verification steps for coding
|
|
126
|
+
agents.
|
|
127
|
+
|
|
128
|
+
## When to use it
|
|
129
|
+
|
|
130
|
+
Solid Objects is a good candidate when most of these conditions are true:
|
|
131
|
+
|
|
132
|
+
- Concurrent requests can change the same room, cart, account, device,
|
|
133
|
+
document, or session.
|
|
134
|
+
- Each identity needs its own ordering boundary and durable mailbox.
|
|
135
|
+
- Work must happen later or continue after a Node.js process exits.
|
|
136
|
+
- A state change must stage reminders, effects, messages to other actors, or
|
|
137
|
+
realtime updates in the same commit.
|
|
138
|
+
- The application already runs SQLite, PostgreSQL, or MySQL and should keep
|
|
139
|
+
durable coordination there.
|
|
140
|
+
|
|
141
|
+
## When to use something else
|
|
142
|
+
|
|
143
|
+
Do not use an actor when a simpler tool enforces the invariant:
|
|
144
|
+
|
|
145
|
+
- One short transaction, a constraint, or `SELECT ... FOR UPDATE` is enough.
|
|
146
|
+
- The work is CPU-intensive. Use `worker_threads` or a worker pool. An actor
|
|
147
|
+
serializes work. It does not add CPU parallelism.
|
|
148
|
+
- One global identity must accept more writes than one sequential mailbox can
|
|
149
|
+
commit, for example a request-path rate limiter.
|
|
150
|
+
- The state is a large document or a relational dataset.
|
|
151
|
+
- The operation must change two actor identities in one atomic transaction.
|
|
152
|
+
Solid Objects has no cross-actor transactions.
|
|
153
|
+
- You need exactly-once calls to an external API. No actor library can promise
|
|
154
|
+
that through every network failure. Solid Objects gives at-least-once
|
|
155
|
+
delivery and stable effect IDs for idempotency keys.
|
|
156
|
+
- You need replay of named workflow steps from a step log. That is a durable
|
|
157
|
+
execution engine, not an actor.
|
|
158
|
+
- State must be placed automatically near clients at the network edge.
|
|
159
|
+
|
|
160
|
+
[Choosing Solid Objects](fit.md) has the full list.
|
|
161
|
+
|
|
162
|
+
## How it compares
|
|
163
|
+
|
|
164
|
+
This table compares coordination models for a Node.js application. It does
|
|
165
|
+
not rank the projects. Facts about other projects were checked on
|
|
166
|
+
October 7, 2026, against the sources in [System comparisons](comparisons.md).
|
|
167
|
+
|
|
168
|
+
| Approach | Unit of order | Durable state | Delayed work | Extra service |
|
|
169
|
+
| ------------------------------------------ | -------------------------------- | ------------------------------------------------- | ---------------------------------------------------- | ------------------------------------------------------------------------------------------ |
|
|
170
|
+
| SQL transaction or row lock | Rows in one transaction | Application tables | None | No |
|
|
171
|
+
| Job queue, such as BullMQ or pg-boss | A job or a queue | Owned by the application | Scheduled jobs | Redis for BullMQ; PostgreSQL for pg-boss |
|
|
172
|
+
| `worker_threads` or a pool such as Piscina | None | None | None | No |
|
|
173
|
+
| Solid Objects | Actor class and ID | JSON state in SQLite, PostgreSQL, or MySQL | Durable per-actor reminders | No |
|
|
174
|
+
| Cloudflare Durable Objects | Object class and ID | Per-object storage | One alarm for each object | Cloudflare's network. The open-source `workerd` runtime hosts objects on one instance only |
|
|
175
|
+
| Dapr actors (`@dapr/dapr`) | Actor type and ID | A transactional Dapr state store | Durable reminders through the Dapr Scheduler service | A Dapr sidecar, plus the placement and Scheduler services |
|
|
176
|
+
| Rivet Actors | Actor key | Actor state, plus per-actor SQLite and KV storage | Scheduled actions | The Rivet Engine, self-hosted or Rivet Cloud |
|
|
177
|
+
| Restate virtual objects | Object key, one writer at a time | Restate's state store | Durable timers | A Restate server |
|
|
178
|
+
| DBOS | A workflow and its steps | Checkpoints in PostgreSQL | Durable sleeps | No server; PostgreSQL is required |
|
|
179
|
+
| Temporal | A workflow execution | Temporal event history | Durable timers | A Temporal Service, self-hosted or Temporal Cloud |
|
|
180
|
+
|
|
181
|
+
[System comparisons](comparisons.md) has the full table, with primary
|
|
182
|
+
references for each row.
|
|
183
|
+
|
|
184
|
+
### Orleans concept map
|
|
185
|
+
|
|
186
|
+
Orleans is the reference design for virtual actors on .NET. Solid Objects
|
|
187
|
+
uses the same programming model on a SQL database. It does not copy the
|
|
188
|
+
Orleans cluster, placement, or feature set.
|
|
189
|
+
|
|
190
|
+
| Orleans | Solid Objects | Difference |
|
|
191
|
+
| ---------------------------- | ---------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
192
|
+
| Grain class | `Actor` subclass with a static `actorType` | None in concept |
|
|
193
|
+
| Grain identity (key) | Actor type and actor ID | None in concept |
|
|
194
|
+
| Activation on first call | Activation lease on first claimed message | Solid Objects fences each activation with a database generation |
|
|
195
|
+
| Turn-based execution | Ordered mailbox, one turn at a time | Orleans can enable reentrancy. Solid Objects turns for one identity never interleave |
|
|
196
|
+
| Grain persistence | Public fields stored as JSON in SQL | An Orleans grain calls `WriteStateAsync`. Solid Objects persists state with the turn that changed it |
|
|
197
|
+
| Reminders | `schedule()` | Both are durable. Orleans skips a tick that falls due while the cluster is down. A due Solid Objects reminder runs when a runtime process starts. Solid Objects has no non-durable timers |
|
|
198
|
+
| Silos and cluster membership | Any Node.js process that calls `runtime.run(signal)` | Solid Objects has no placement, directory, or cluster membership. The database is the coordination point |
|
|
199
|
+
| Streams | Observables and realtime sessions | Solid Objects publishes committed revisions through an application-owned transport |
|
|
200
|
+
|
|
201
|
+
Solid Objects delivery is at least once. Write each operation so that it can
|
|
202
|
+
run again without harm.
|
|
203
|
+
|
|
204
|
+
## Guarantees and boundaries
|
|
205
|
+
|
|
206
|
+
- Calls are durably ordered per identity. Different identities can run
|
|
207
|
+
concurrently.
|
|
208
|
+
- Delivery is at least once, not exactly once. An operation can start again
|
|
209
|
+
after a crash or a lost lease.
|
|
210
|
+
- Ordered turns do not cancel stale JavaScript. Fencing stops a stale
|
|
211
|
+
activation from a commit, but that code can continue to run.
|
|
212
|
+
- External effects can run more than once. Use the stable effect ID, or
|
|
213
|
+
another durable key, as the idempotency key at the provider.
|
|
214
|
+
- There are no transactions across actor identities, and there is no replay of
|
|
215
|
+
durable function steps.
|
|
216
|
+
- Reminders, effects, and realtime delivery need a process that calls
|
|
217
|
+
`runtime.run(signal)`. When no process runs, committed work waits in SQL.
|
|
218
|
+
The package does not supply a hosted worker.
|
|
219
|
+
- One hot identity is sequential. The core package is not a high-throughput
|
|
220
|
+
request-path rate limiter.
|
|
221
|
+
- The guarantees apply only to changes made through the actor APIs. Actor
|
|
222
|
+
fencing does not protect direct writes to the same data or other external
|
|
223
|
+
requests.
|
|
224
|
+
|
|
225
|
+
The [correctness contract](correctness.md) states each guarantee and its
|
|
226
|
+
limits.
|
|
227
|
+
|
|
228
|
+
## Compatibility and maturity
|
|
229
|
+
|
|
230
|
+
These runtimes have different compatibility statements:
|
|
231
|
+
|
|
232
|
+
| Surface | Status |
|
|
233
|
+
| ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
234
|
+
| Node.js SQL runtime | Node.js 24.4 or newer, ESM only, TypeScript 5.9 or newer. SQLite through `node:sqlite`, PostgreSQL 14 or newer, MySQL 8.0 or newer with InnoDB |
|
|
235
|
+
| Browser client (`solid-objects/browser`) | A subscription client for a server runtime. It is not the SQL actor runtime |
|
|
236
|
+
| Browser runtime (`solid-objects/browser/host`) | The actor runtime in a browser module worker on SQLite WASM. Tested in Chromium |
|
|
237
|
+
| Cloudflare backend (`solid-objects/cloudflare`) | Experimental. It runs the actor API on Cloudflare Durable Objects, with different capability limits |
|
|
238
|
+
|
|
239
|
+
The package is pre-1.0. It has one deployed first-party reference
|
|
240
|
+
application, no measured scale, and no known third-party production use.
|
|
241
|
+
[Supported versions](support.md) lists the CI matrix. For Ruby on Rails, use
|
|
242
|
+
the [solid_objects](https://github.com/cardmagic/solid-objects-ruby) gem.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Actor, configure } from "solid-objects"
|
|
2
|
+
import { sqlite } from "solid-objects/database/sqlite"
|
|
3
|
+
|
|
4
|
+
const HOLD_MILLISECONDS = 10 * 60 * 1000
|
|
5
|
+
|
|
6
|
+
export class TicketSale extends Actor {
|
|
7
|
+
static override readonly actorType = "TicketSale"
|
|
8
|
+
|
|
9
|
+
available = 1
|
|
10
|
+
holds: Record<string, number> = {}
|
|
11
|
+
|
|
12
|
+
hold({ buyer }: { buyer: string }): { held: boolean; available: number } {
|
|
13
|
+
if (this.available === 0 || Object.hasOwn(this.holds, buyer)) {
|
|
14
|
+
return { held: false, available: this.available }
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
this.available -= 1
|
|
18
|
+
this.holds = { ...this.holds, [buyer]: Date.now() }
|
|
19
|
+
this.schedule({ at: new Date(Date.now() + HOLD_MILLISECONDS), key: buyer }).expire({ buyer })
|
|
20
|
+
return { held: true, available: this.available }
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
expire({ buyer }: { buyer: string }): number {
|
|
24
|
+
if (!Object.hasOwn(this.holds, buyer)) return this.available
|
|
25
|
+
|
|
26
|
+
const remainingHolds = { ...this.holds }
|
|
27
|
+
delete remainingHolds[buyer]
|
|
28
|
+
this.holds = remainingHolds
|
|
29
|
+
this.available += 1
|
|
30
|
+
return this.available
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const runtime = configure({
|
|
35
|
+
database: sqlite({ path: process.env.TICKET_DATABASE ?? "tickets.sqlite3" }),
|
|
36
|
+
authorizeMessage: () => true,
|
|
37
|
+
authorizeQuery: () => true,
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
await runtime.install()
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
const sale = TicketSale.ref("event-42")
|
|
44
|
+
|
|
45
|
+
if (process.argv[2] === "work") {
|
|
46
|
+
const controller = new AbortController()
|
|
47
|
+
process.once("SIGINT", () => controller.abort())
|
|
48
|
+
process.once("SIGTERM", () => controller.abort())
|
|
49
|
+
await runtime.run(controller.signal)
|
|
50
|
+
} else {
|
|
51
|
+
const buyers = process.argv.length > 3 ? process.argv.slice(3) : ["ada", "grace"]
|
|
52
|
+
const results = await Promise.all(buyers.map((buyer) => sale.hold({ buyer })))
|
|
53
|
+
console.log(JSON.stringify(results))
|
|
54
|
+
}
|
|
55
|
+
} finally {
|
|
56
|
+
await runtime.close()
|
|
57
|
+
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "solid-objects",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.17.1",
|
|
4
|
+
"description": "SQL-backed virtual actor library for TypeScript and Node.js, with durable state, ordered operations, and automatic activation on SQLite, PostgreSQL, or MySQL",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "Lucas Carlson",
|
|
8
|
+
"homepage": "https://solidobjects.dev/js",
|
|
8
9
|
"keywords": [
|
|
9
10
|
"concurrency",
|
|
10
11
|
"durable-state",
|
|
@@ -16,7 +17,9 @@
|
|
|
16
17
|
"state-management",
|
|
17
18
|
"typescript",
|
|
18
19
|
"actors",
|
|
19
|
-
"durable-objects"
|
|
20
|
+
"durable-objects",
|
|
21
|
+
"virtual-actors",
|
|
22
|
+
"actor-model"
|
|
20
23
|
],
|
|
21
24
|
"repository": {
|
|
22
25
|
"type": "git",
|
|
@@ -115,8 +118,8 @@
|
|
|
115
118
|
"test": "vitest run",
|
|
116
119
|
"test:browser": "pnpm run build && playwright test",
|
|
117
120
|
"test:coverage": "vitest run --coverage",
|
|
118
|
-
"test:postgresql": "vitest run test/postgresql.test.ts test/effect-recovery.test.ts test/instance-retention.test.ts",
|
|
119
|
-
"test:mysql": "vitest run test/mysql.test.ts test/effect-recovery.test.ts test/instance-retention.test.ts",
|
|
121
|
+
"test:postgresql": "vitest run test/postgresql.test.ts test/effect-recovery.test.ts test/instance-retention.test.ts test/transmit-ordering.test.ts",
|
|
122
|
+
"test:mysql": "vitest run test/mysql.test.ts test/effect-recovery.test.ts test/instance-retention.test.ts test/transmit-ordering.test.ts",
|
|
120
123
|
"test:package": "node scripts/release-artifact-smoke.mjs",
|
|
121
124
|
"test:recovery": "pnpm run build && node examples/failure-recovery/demo.ts",
|
|
122
125
|
"test:at-least-once": "pnpm run build && node examples/at-least-once/demo.ts",
|