@empyria/restate 0.4.16 → 0.4.17
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/AGENTS.md +6 -0
- package/README.md +5 -0
- package/lib/Admin.js +35 -14
- package/package.json +1 -1
- package/test/Admin.test.js +26 -0
package/AGENTS.md
CHANGED
|
@@ -79,6 +79,12 @@ may observe an already-mocked value. See `test/Admin.test.js`.
|
|
|
79
79
|
same address registers again, but only `force: true` updates its handler list. Check both against a live server when
|
|
80
80
|
touching them.
|
|
81
81
|
|
|
82
|
+
`setupRestate` registers the single complete `serviceURL` it is given (scheme included — never assemble a URL from
|
|
83
|
+
host + port, the scheme and any proxy mapping are the caller's to state). `port` is only the listen port. `host` is a
|
|
84
|
+
deprecated fallback (`http://host:port`) kept for consumers pinned to older call sites; the URL decision is
|
|
85
|
+
`resolveServiceURL` (unit tested). `createRestateAdmin().setupRestate` must forward every option to `setupRestate` — it
|
|
86
|
+
once dropped `keepDeploymentOnShutdown`/`forceRegistration`/`registrationCallbackFn`.
|
|
87
|
+
|
|
82
88
|
`setupRestate` (in `lib/Admin.js`) is not unit tested: it binds real HTTP/2 + health-check
|
|
83
89
|
ports, installs process-wide `SIGTERM`/`SIGINT` handlers, and its returned `forceClose` calls
|
|
84
90
|
`process.exit()` — none of which are safe to exercise in-process in a test run.
|
package/README.md
CHANGED
|
@@ -124,6 +124,11 @@ Cron expressions are `croner` patterns (five fields, or six with seconds for tes
|
|
|
124
124
|
|
|
125
125
|
### An endpoint that does not register itself
|
|
126
126
|
|
|
127
|
+
Tell `setupRestate` where Restate reaches the endpoint with one complete URL, scheme included:
|
|
128
|
+
`setupRestate({ restateAdminURL, port: 9080, serviceURL: 'https://agent-service.internal:9080', services })`. `port` is only
|
|
129
|
+
what the process listens on; `serviceURL` is what gets registered (it may differ behind a proxy). Without `serviceURL`
|
|
130
|
+
nothing is registered. The old `host` option still works (registers `http://<host>:<port>`) but is deprecated.
|
|
131
|
+
|
|
127
132
|
`setupRestate` serves, registers the deployment, and deletes it again on SIGINT/SIGTERM. Invocations in flight are
|
|
128
133
|
pinned to their deployment, so deleting it strands them: a restarted process cannot finish them and they can only be
|
|
129
134
|
killed. For a process restarted at the same address, pass `keepDeploymentOnShutdown: true` and `forceRegistration: true`:
|
package/lib/Admin.js
CHANGED
|
@@ -592,8 +592,12 @@ export function defineObject(def) {
|
|
|
592
592
|
* that was removed or renamed cannot resume.
|
|
593
593
|
* @param {Object} params
|
|
594
594
|
* @param {string} params.restateAdminURL
|
|
595
|
-
* @param {
|
|
596
|
-
* @param {
|
|
595
|
+
* @param {number} params.port Port this endpoint listens on (the bind, not the advertised address).
|
|
596
|
+
* @param {string} [params.serviceURL] Complete URL Restate uses to reach this endpoint, scheme included
|
|
597
|
+
* (e.g. `https://agent-service.internal:9080`). Registration happens only when it is set — the scheme and
|
|
598
|
+
* any proxy/port mapping are the caller's to state, never guessed here.
|
|
599
|
+
* @param {string} [params.host] @deprecated Use `serviceURL`. Only when `serviceURL` is absent: registers as
|
|
600
|
+
* `http://<host>:<port>` (plain HTTP is assumed).
|
|
597
601
|
* @param {number} [params.healthPort] - Defaults to `port + 1`.
|
|
598
602
|
* @param {Parameters<typeof restate.createEndpointHandler>[0]['services']} [params.services]
|
|
599
603
|
* @param {() => void} [params.shutdownCallbackFn]
|
|
@@ -605,6 +609,7 @@ export function defineObject(def) {
|
|
|
605
609
|
*/
|
|
606
610
|
export async function setupRestate({
|
|
607
611
|
restateAdminURL,
|
|
612
|
+
serviceURL: serviceURLParam,
|
|
608
613
|
host,
|
|
609
614
|
port,
|
|
610
615
|
healthPort = port + 1,
|
|
@@ -684,9 +689,8 @@ export async function setupRestate({
|
|
|
684
689
|
|
|
685
690
|
server.listen(port, () => console.log(`Server running on ${port}`))
|
|
686
691
|
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
692
|
+
const serviceURL = resolveServiceURL({ serviceURL: serviceURLParam, host, port })
|
|
693
|
+
if (serviceURL) {
|
|
690
694
|
/**
|
|
691
695
|
* @param {Object} params
|
|
692
696
|
* @param {string} params.restateAdminURL
|
|
@@ -771,6 +775,31 @@ export async function setupRestate({
|
|
|
771
775
|
})
|
|
772
776
|
}
|
|
773
777
|
|
|
778
|
+
/**
|
|
779
|
+
* The URL `setupRestate` registers with Restate: `serviceURL` as given (validated as an absolute http(s) URL),
|
|
780
|
+
* else the deprecated `host` + `port` pair as `http://host:port`, else `undefined` (no registration).
|
|
781
|
+
* @param {{serviceURL?: string, host?: string, port?: number}} params
|
|
782
|
+
* @returns {string|undefined}
|
|
783
|
+
* @throws {Error} If `serviceURL` is not an absolute http(s) URL
|
|
784
|
+
*/
|
|
785
|
+
export function resolveServiceURL({ serviceURL, host, port }) {
|
|
786
|
+
if (serviceURL) {
|
|
787
|
+
let url
|
|
788
|
+
try {
|
|
789
|
+
url = new URL(serviceURL)
|
|
790
|
+
} catch {
|
|
791
|
+
throw new Error(
|
|
792
|
+
`serviceURL must be a complete URL including http:// or https://, got '${serviceURL}'`,
|
|
793
|
+
)
|
|
794
|
+
}
|
|
795
|
+
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
|
|
796
|
+
throw new Error(`serviceURL must use http or https, got '${serviceURL}'`)
|
|
797
|
+
}
|
|
798
|
+
return serviceURL
|
|
799
|
+
}
|
|
800
|
+
return host && port ? `http://${host}:${port}` : undefined
|
|
801
|
+
}
|
|
802
|
+
|
|
774
803
|
/**
|
|
775
804
|
* Whether `setupRestate`'s shutdown removes the deployment: only a real one (registered, not `'NA'`), and not when the
|
|
776
805
|
* caller keeps it for the next start.
|
|
@@ -885,15 +914,7 @@ export function createRestateAdmin({ restateAdminURL, restateURL }) {
|
|
|
885
914
|
listHandlers: ({ name }) => listHandlers({ restateAdminURL, name }),
|
|
886
915
|
|
|
887
916
|
// Management queries
|
|
888
|
-
setupRestate: (
|
|
889
|
-
setupRestate({
|
|
890
|
-
restateAdminURL,
|
|
891
|
-
host,
|
|
892
|
-
port,
|
|
893
|
-
healthPort: healthPort ?? port + 1,
|
|
894
|
-
services,
|
|
895
|
-
shutdownCallbackFn,
|
|
896
|
-
}),
|
|
917
|
+
setupRestate: (params) => setupRestate({ ...params, restateAdminURL }),
|
|
897
918
|
registerDeployment: ({ serviceURL }) => registerDeployment({ restateAdminURL, serviceURL }),
|
|
898
919
|
|
|
899
920
|
submitWorkflow: ({ name, key, payload }) =>
|
package/package.json
CHANGED
package/test/Admin.test.js
CHANGED
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
sendMessageAsyncWithDiscovery,
|
|
22
22
|
registerDeployment,
|
|
23
23
|
shouldDeleteOnShutdown,
|
|
24
|
+
resolveServiceURL,
|
|
24
25
|
sendMessageAsynSafe,
|
|
25
26
|
defineWorkflow,
|
|
26
27
|
defineService,
|
|
@@ -542,6 +543,31 @@ describe('registerDeployment', () => {
|
|
|
542
543
|
})
|
|
543
544
|
})
|
|
544
545
|
|
|
546
|
+
describe('resolveServiceURL', () => {
|
|
547
|
+
test('uses a complete serviceURL as given, scheme included', () => {
|
|
548
|
+
expect(
|
|
549
|
+
resolveServiceURL({ serviceURL: 'https://svc.example.com:9443/base', port: 9080 }),
|
|
550
|
+
).toBe('https://svc.example.com:9443/base')
|
|
551
|
+
})
|
|
552
|
+
|
|
553
|
+
test('serviceURL wins over the deprecated host', () => {
|
|
554
|
+
expect(resolveServiceURL({ serviceURL: 'https://a', host: 'b', port: 1 })).toBe('https://a')
|
|
555
|
+
})
|
|
556
|
+
|
|
557
|
+
test('falls back to http://host:port for the deprecated host', () => {
|
|
558
|
+
expect(resolveServiceURL({ host: 'svc', port: 9080 })).toBe('http://svc:9080')
|
|
559
|
+
})
|
|
560
|
+
|
|
561
|
+
test('returns undefined (no registration) when nothing is given', () => {
|
|
562
|
+
expect(resolveServiceURL({ port: 9080 })).toBeUndefined()
|
|
563
|
+
})
|
|
564
|
+
|
|
565
|
+
test('rejects a URL without a scheme or with a non-http scheme', () => {
|
|
566
|
+
expect(() => resolveServiceURL({ serviceURL: 'svc:9080' })).toThrow(/complete URL|http/)
|
|
567
|
+
expect(() => resolveServiceURL({ serviceURL: 'ftp://svc' })).toThrow(/http or https/)
|
|
568
|
+
})
|
|
569
|
+
})
|
|
570
|
+
|
|
545
571
|
describe('shouldDeleteOnShutdown', () => {
|
|
546
572
|
test('deletes a registered deployment by default', () => {
|
|
547
573
|
expect(shouldDeleteOnShutdown({ deploymentId: 'dep-1' })).toBe(true)
|