@empyria/restate 0.1.3 → 0.1.4
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 +7 -6
- package/index.js +2 -0
- package/lib/Admin.js +0 -16
- package/lib/Pubsub.js +51 -0
- package/package.json +2 -1
- package/test/Admin.test.js +0 -13
- package/test/Pubsub.test.js +51 -0
package/README.md
CHANGED
|
@@ -43,12 +43,13 @@ import { checkServiceHandler } from '@empyria/restate/lib/Admin.js'
|
|
|
43
43
|
|
|
44
44
|
## Modules
|
|
45
45
|
|
|
46
|
-
| Module | Purpose
|
|
47
|
-
| ---------------------------------------- |
|
|
48
|
-
| [lib/Admin.js](./lib/Admin.js) | Restate Admin API client — service/handler discovery, deployment registration and teardown, workflow/message dispatch, `defineWorkflow`/`defineService`/`defineObject
|
|
49
|
-
| [lib/Caller.js](./lib/Caller.js) | `CallerServiceDef` — a generic Restate service that dispatches a call to any handler on any other service by name, for when the target isn't known at compile time.
|
|
50
|
-
| [lib/Cron.js](./lib/Cron.js) | `cronJobInitiator`/`cronJob` — Restate services implementing cron-driven, replay-safe recurring job scheduling on top of `croner`.
|
|
51
|
-
| [lib/
|
|
46
|
+
| Module | Purpose |
|
|
47
|
+
| ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
48
|
+
| [lib/Admin.js](./lib/Admin.js) | Restate Admin API client — service/handler discovery, deployment registration and teardown, workflow/message dispatch, `defineWorkflow`/`defineService`/`defineObject` construct definers, and `setupRestate` for wiring up an endpoint with health checks and graceful shutdown. |
|
|
49
|
+
| [lib/Caller.js](./lib/Caller.js) | `CallerServiceDef` — a generic Restate service that dispatches a call to any handler on any other service by name, for when the target isn't known at compile time. |
|
|
50
|
+
| [lib/Cron.js](./lib/Cron.js) | `cronJobInitiator`/`cronJob` — Restate services implementing cron-driven, replay-safe recurring job scheduling on top of `croner`. |
|
|
51
|
+
| [lib/Pubsub.js](./lib/Pubsub.js) | `definePubsub` (register a pubsub Virtual Object on your endpoint), `pubsubPublisher` (publish into it from inside a handler, in-process), `pubsubClient` (publish/pull/subscribe to it from anywhere else, over the network) — thin wrappers around `@restatedev/pubsub`/`@restatedev/pubsub-client`. |
|
|
52
|
+
| [lib/Validation.js](./lib/Validation.js) | `withValidation` — wraps a workflow handler with input/output JSON schema validation via `@empyria/common`. |
|
|
52
53
|
|
|
53
54
|
There is no official Restate Admin API client (verified directly against the published
|
|
54
55
|
`@restatedev/restate-sdk`/`-clients` packages) — `lib/Admin.js` is a hand-written wrapper kept
|
package/index.js
CHANGED
package/lib/Admin.js
CHANGED
|
@@ -6,7 +6,6 @@ import { setTimeout as sleep } from 'timers/promises'
|
|
|
6
6
|
|
|
7
7
|
import * as restate from '@restatedev/restate-sdk'
|
|
8
8
|
import * as clients from '@restatedev/restate-sdk-clients'
|
|
9
|
-
import { createPubsubObject } from '@restatedev/pubsub'
|
|
10
9
|
|
|
11
10
|
export { restate, clients }
|
|
12
11
|
|
|
@@ -438,21 +437,6 @@ export function defineObject(def) {
|
|
|
438
437
|
return restate.object(def)
|
|
439
438
|
}
|
|
440
439
|
|
|
441
|
-
/**
|
|
442
|
-
* Typed wrapper around `@restatedev/pubsub`'s `createPubsubObject`; see {@link defineWorkflow}.
|
|
443
|
-
* Unlike `defineWorkflow`/`defineService`/`defineObject`, there's no handler map to write —
|
|
444
|
-
* `createPubsubObject` returns a complete `VirtualObjectDefinition` on its own (pull/publish/
|
|
445
|
-
* subscribe/truncate), so this exists purely for the same "one `define*` per Restate construct"
|
|
446
|
-
* naming consistency, not to add behavior. Register the result alongside your other
|
|
447
|
-
* services/objects on the same endpoint (see `setupRestate`'s `services` param) — that's what
|
|
448
|
-
* makes the endpoint's ingress serve `/pubsub/<topic>` as an SSE stream for it.
|
|
449
|
-
* @param {Parameters<typeof createPubsubObject>[0]} name
|
|
450
|
-
* @param {Parameters<typeof createPubsubObject>[1]} [options]
|
|
451
|
-
*/
|
|
452
|
-
export function definePubsub(name, options) {
|
|
453
|
-
return createPubsubObject(name, options)
|
|
454
|
-
}
|
|
455
|
-
|
|
456
440
|
/**
|
|
457
441
|
* Starts the HTTP/2 Restate endpoint, a health-check HTTP server, registers
|
|
458
442
|
* the deployment with the admin API, and wires SIGTERM/SIGINT for graceful
|
package/lib/Pubsub.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { createPubsubObject, createPubsubPublisher } from '@restatedev/pubsub'
|
|
2
|
+
import { createPubsubClient } from '@restatedev/pubsub-client'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Typed wrapper around `@restatedev/pubsub`'s `createPubsubObject`; see `Admin.js`'s
|
|
6
|
+
* `defineWorkflow`/`defineService`/`defineObject` for the same "typed wrapper, plain passthrough
|
|
7
|
+
* in JS" pattern this follows. Unlike those three, there's no handler map to write —
|
|
8
|
+
* `createPubsubObject` returns a complete `VirtualObjectDefinition` on its own (pull/publish/
|
|
9
|
+
* subscribe/truncate), so this exists purely for the same "one `define*` per Restate construct"
|
|
10
|
+
* naming consistency, not to add behavior.
|
|
11
|
+
*
|
|
12
|
+
* Register the result alongside your other services/objects on the same endpoint (see
|
|
13
|
+
* `setupRestate`'s `services` param — Admin.js) — that registration is what lets Restate route to
|
|
14
|
+
* it at all. Restate's own ingress does NOT serve a ready-made SSE route for it, though: reaching
|
|
15
|
+
* it from a browser needs an app-owned relay built on {@link pubsubClient}'s own `.sse()` (or a
|
|
16
|
+
* hand-rolled loop over `.pull()`, e.g. to log each message before forwarding it) — confirmed
|
|
17
|
+
* directly against `@restatedev/pubsub-client`'s compiled source, 2026-08-24, after an earlier,
|
|
18
|
+
* wrong assumption that this was automatic.
|
|
19
|
+
* @param {Parameters<typeof createPubsubObject>[0]} name
|
|
20
|
+
* @param {Parameters<typeof createPubsubObject>[1]} [options]
|
|
21
|
+
*/
|
|
22
|
+
export function definePubsub(name, options) {
|
|
23
|
+
return createPubsubObject(name, options)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Typed wrapper around `@restatedev/pubsub`'s `createPubsubPublisher` — the in-process publish
|
|
28
|
+
* path. Call the returned function from inside any handler that already has `ctx` and shares a
|
|
29
|
+
* Restate endpoint with the pubsub object it targets (both registered via the same `services`
|
|
30
|
+
* list passed to `setupRestate` — see {@link definePubsub}). Under the hood it's
|
|
31
|
+
* `ctx.objectSendClient({name}, topic).publish(message)` — a real Restate call, already covered
|
|
32
|
+
* by the invocation's own journal, so callers don't need to wrap it in an extra `ctx.run()`
|
|
33
|
+
* themselves (unlike a plain `fetch()`-based call, e.g. `sendMessageAsync` in Admin.js, which
|
|
34
|
+
* does).
|
|
35
|
+
* @param {Parameters<typeof createPubsubPublisher>[0]} name
|
|
36
|
+
*/
|
|
37
|
+
export function pubsubPublisher(name) {
|
|
38
|
+
return createPubsubPublisher(name)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Typed wrapper around `@restatedev/pubsub-client`'s `createPubsubClient` — the out-of-process
|
|
43
|
+
* counterpart to {@link pubsubPublisher}/{@link definePubsub}: a network client (`pull`/`sse`/
|
|
44
|
+
* `publish`/`truncate`) for anything that ISN'T a handler on the same Restate endpoint as the
|
|
45
|
+
* pubsub object — a separate process relaying to a browser, an external consumer, ... — reaching
|
|
46
|
+
* it over a plain ingress URL instead of `ctx.objectSendClient`.
|
|
47
|
+
* @param {Parameters<typeof createPubsubClient>[0]} pubsubOptions
|
|
48
|
+
*/
|
|
49
|
+
export function pubsubClient(pubsubOptions) {
|
|
50
|
+
return createPubsubClient(pubsubOptions)
|
|
51
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@empyria/restate",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Restate.dev helpers for the Empyria nanoservice framework",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Imre Fazekas <imre.fazekas@icloud.com>",
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"@empyria/classification": "0.1.2",
|
|
28
28
|
"@empyria/common": "0.1.2",
|
|
29
29
|
"@restatedev/pubsub": "0.3.2",
|
|
30
|
+
"@restatedev/pubsub-client": "0.3.2",
|
|
30
31
|
"@restatedev/restate-sdk": "1.16.8",
|
|
31
32
|
"@restatedev/restate-sdk-clients": "1.16.8",
|
|
32
33
|
"@restatedev/restate-sdk-core": "1.16.8",
|
package/test/Admin.test.js
CHANGED
|
@@ -20,7 +20,6 @@ import {
|
|
|
20
20
|
defineWorkflow,
|
|
21
21
|
defineService,
|
|
22
22
|
defineObject,
|
|
23
|
-
definePubsub,
|
|
24
23
|
createRestateAdmin,
|
|
25
24
|
restate,
|
|
26
25
|
clients,
|
|
@@ -456,18 +455,6 @@ describe('defineWorkflow / defineService / defineObject', () => {
|
|
|
456
455
|
})
|
|
457
456
|
})
|
|
458
457
|
|
|
459
|
-
describe('definePubsub', () => {
|
|
460
|
-
test('is a passthrough to createPubsubObject, name and all', () => {
|
|
461
|
-
const pubsub = definePubsub('pubsub')
|
|
462
|
-
expect(pubsub.name).toBe('pubsub')
|
|
463
|
-
})
|
|
464
|
-
|
|
465
|
-
test('forwards options (e.g. pullTimeout) to createPubsubObject', () => {
|
|
466
|
-
const pubsub = definePubsub('pubsub', { pullTimeout: { seconds: 30 } })
|
|
467
|
-
expect(pubsub.name).toBe('pubsub')
|
|
468
|
-
})
|
|
469
|
-
})
|
|
470
|
-
|
|
471
458
|
describe('createRestateAdmin', () => {
|
|
472
459
|
test('binds restateAdminURL/restateURL across its methods', async () => {
|
|
473
460
|
globalThis.fetch = mock(async (url) => {
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { describe, test, expect } from 'bun:test'
|
|
2
|
+
import { definePubsub, pubsubPublisher, pubsubClient } from '../lib/Pubsub.js'
|
|
3
|
+
|
|
4
|
+
describe('definePubsub', () => {
|
|
5
|
+
test('is a passthrough to createPubsubObject, name and all', () => {
|
|
6
|
+
const pubsub = definePubsub('pubsub')
|
|
7
|
+
expect(pubsub.name).toBe('pubsub')
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
test('forwards options (e.g. pullTimeout) to createPubsubObject', () => {
|
|
11
|
+
const pubsub = definePubsub('pubsub', { pullTimeout: { seconds: 30 } })
|
|
12
|
+
expect(pubsub.name).toBe('pubsub')
|
|
13
|
+
})
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
describe('pubsubPublisher', () => {
|
|
17
|
+
test('returns a function that publishes via ctx.objectSendClient', () => {
|
|
18
|
+
let capturedTarget
|
|
19
|
+
let capturedKey
|
|
20
|
+
let capturedMessage
|
|
21
|
+
const fakeCtx = {
|
|
22
|
+
objectSendClient: (target, key) => {
|
|
23
|
+
capturedTarget = target
|
|
24
|
+
capturedKey = key
|
|
25
|
+
return {
|
|
26
|
+
publish: (message) => {
|
|
27
|
+
capturedMessage = message
|
|
28
|
+
},
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const publish = pubsubPublisher('pubsub')
|
|
34
|
+
publish(fakeCtx, 'agent-service', { event: 'ask-triggered' })
|
|
35
|
+
|
|
36
|
+
expect(capturedTarget).toEqual({ name: 'pubsub' })
|
|
37
|
+
expect(capturedKey).toBe('agent-service')
|
|
38
|
+
expect(capturedMessage).toEqual({ event: 'ask-triggered' })
|
|
39
|
+
})
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
describe('pubsubClient', () => {
|
|
43
|
+
test('is a passthrough to createPubsubClient exposing pull/sse/publish/truncate', () => {
|
|
44
|
+
const client = pubsubClient({ url: 'http://localhost:8080', name: 'pubsub' })
|
|
45
|
+
|
|
46
|
+
expect(typeof client.pull).toBe('function')
|
|
47
|
+
expect(typeof client.sse).toBe('function')
|
|
48
|
+
expect(typeof client.publish).toBe('function')
|
|
49
|
+
expect(typeof client.truncate).toBe('function')
|
|
50
|
+
})
|
|
51
|
+
})
|