ocel 0.0.1-alpha.0 → 0.0.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/LICENSE +202 -0
- package/NOTICE +2 -0
- package/README.md +98 -0
- package/dist/binding/binding.d.ts +22 -0
- package/dist/binding/binding.js +60 -0
- package/dist/binding/unprovisioned.d.ts +17 -0
- package/dist/binding/unprovisioned.js +25 -0
- package/dist/bucket/bucket-client.d.ts +4 -0
- package/dist/bucket/bucket-client.js +5 -0
- package/dist/bucket/bucket-context.d.ts +8 -0
- package/dist/bucket/bucket-context.js +10 -0
- package/dist/bucket/bucket.d.ts +60 -0
- package/dist/bucket/bucket.js +91 -0
- package/dist/bucket/client.d.ts +49 -0
- package/dist/bucket/client.js +95 -0
- package/dist/bucket/errors.d.ts +12 -0
- package/dist/bucket/errors.js +20 -0
- package/dist/bucket/express.d.ts +10 -0
- package/dist/bucket/express.js +24 -0
- package/dist/bucket/hono.d.ts +18 -0
- package/dist/bucket/hono.js +13 -0
- package/dist/bucket/index.d.ts +8 -0
- package/dist/bucket/index.js +6 -0
- package/dist/bucket/keys.d.ts +5 -0
- package/dist/bucket/keys.js +24 -0
- package/dist/bucket/metadata.d.ts +6 -0
- package/dist/bucket/metadata.js +6 -0
- package/dist/bucket/next.d.ts +15 -0
- package/dist/bucket/next.js +11 -0
- package/dist/bucket/objects.d.ts +130 -0
- package/dist/bucket/objects.js +350 -0
- package/dist/bucket/route.d.ts +13 -0
- package/dist/bucket/route.js +247 -0
- package/dist/bucket/server-only.js +5 -0
- package/dist/bucket/types.d.ts +63 -0
- package/dist/bucket/uploader.d.ts +3 -0
- package/dist/bucket/uploader.js +6 -0
- package/dist/build-env.d.ts +17 -0
- package/dist/build-env.js +41 -0
- package/dist/config.d.ts +30 -0
- package/dist/config.js +9 -0
- package/dist/declaration/callsite.d.ts +15 -0
- package/dist/declaration/callsite.js +43 -0
- package/dist/declaration/component.d.ts +4 -0
- package/dist/declaration/component.js +1 -0
- package/dist/declaration/defer.d.ts +5 -0
- package/dist/declaration/defer.js +9 -0
- package/dist/delivery/duration.d.ts +13 -0
- package/dist/delivery/duration.js +36 -0
- package/dist/delivery/lane.d.ts +4 -0
- package/dist/delivery/lane.js +15 -0
- package/dist/delivery/payload.d.ts +6 -0
- package/dist/delivery/payload.js +14 -0
- package/dist/delivery/retry.d.ts +16 -0
- package/dist/delivery/retry.js +10 -0
- package/dist/delivery/schema.d.ts +10 -0
- package/dist/delivery/schema.js +20 -0
- package/dist/dns.d.ts +11 -0
- package/dist/dns.js +4 -0
- package/dist/edge.d.ts +4 -0
- package/dist/edge.js +4 -0
- package/dist/env/access.d.ts +22 -0
- package/dist/env/access.js +49 -0
- package/dist/env/client.d.ts +5 -0
- package/dist/env/client.js +10 -0
- package/dist/env/declare.d.ts +2 -0
- package/dist/env/declare.js +83 -0
- package/dist/env/definition.d.ts +46 -0
- package/dist/env/definition.js +164 -0
- package/dist/env/deployment.d.ts +23 -0
- package/dist/env/deployment.js +13 -0
- package/dist/env/edge.d.ts +10 -0
- package/dist/env/edge.js +30 -0
- package/dist/env/errors.d.ts +9 -0
- package/dist/env/errors.js +9 -0
- package/dist/env/file.d.ts +1 -0
- package/dist/env/file.js +27 -0
- package/dist/env/index.d.ts +10 -0
- package/dist/env/index.js +63 -0
- package/dist/env/live.d.ts +3 -0
- package/dist/env/live.js +12 -0
- package/dist/env/schema.d.ts +27 -0
- package/dist/env/schema.js +43 -0
- package/dist/env/scope.d.ts +7 -0
- package/dist/env/scope.js +45 -0
- package/dist/env/standard.d.ts +10 -0
- package/dist/env/standard.js +21 -0
- package/dist/env/value.d.ts +5 -0
- package/dist/env/value.js +26 -0
- package/dist/env-source.d.ts +17 -0
- package/dist/env-source.js +16 -0
- package/dist/gen/proto/app/bucket/v1/bucket_pb.d.ts +911 -0
- package/dist/gen/proto/app/bucket/v1/bucket_pb.js +253 -0
- package/dist/gen/proto/app/realtime/v1/realtime_pb.d.ts +50 -0
- package/dist/gen/proto/app/realtime/v1/realtime_pb.js +23 -0
- package/dist/gen/proto/app/resources/v1/resources_pb.d.ts +554 -0
- package/dist/gen/proto/app/resources/v1/resources_pb.js +214 -0
- package/dist/gen/proto/app/resources/v1/variables_pb.d.ts +231 -0
- package/dist/gen/proto/app/resources/v1/variables_pb.js +101 -0
- package/dist/gen/proto/app/task/v1/task_pb.d.ts +498 -0
- package/dist/gen/proto/app/task/v1/task_pb.js +151 -0
- package/dist/gen/proto/app/topic/v1/topic_pb.d.ts +456 -0
- package/dist/gen/proto/app/topic/v1/topic_pb.js +125 -0
- package/dist/gen/proto/buf/validate/validate_pb.d.ts +4833 -0
- package/dist/gen/proto/buf/validate/validate_pb.js +356 -0
- package/dist/gen/proto/common/bindings/v1/bindings_pb.d.ts +378 -0
- package/dist/gen/proto/common/bindings/v1/bindings_pb.js +140 -0
- package/dist/generated/config.d.ts +428 -0
- package/dist/generated/config.js +2 -0
- package/dist/kv/entries.d.ts +193 -0
- package/dist/kv/entries.js +325 -0
- package/dist/kv/errors.d.ts +12 -0
- package/dist/kv/errors.js +14 -0
- package/dist/kv/index.d.ts +6 -0
- package/dist/kv/index.js +3 -0
- package/dist/kv/ioredis.d.ts +2 -0
- package/dist/kv/ioredis.js +20 -0
- package/dist/kv/kv.d.ts +84 -0
- package/dist/kv/kv.js +177 -0
- package/dist/kv/pattern.d.ts +27 -0
- package/dist/kv/pattern.js +73 -0
- package/dist/kv/ttl.d.ts +22 -0
- package/dist/kv/ttl.js +21 -0
- package/dist/postgres/index.d.ts +21 -2
- package/dist/postgres/index.js +73 -2
- package/dist/postgres/pg.d.ts +5 -4
- package/dist/postgres/pg.js +16 -12
- package/dist/providers/aws/dns.d.ts +11 -0
- package/dist/providers/aws/dns.js +4 -0
- package/dist/providers/aws/edge.d.ts +6 -0
- package/dist/providers/aws/edge.js +8 -0
- package/dist/providers/aws/index.d.ts +4 -0
- package/dist/providers/aws/index.js +4 -0
- package/dist/providers/gcp/edge.d.ts +14 -0
- package/dist/providers/gcp/edge.js +14 -0
- package/dist/providers/gcp/index.d.ts +9 -0
- package/dist/providers/gcp/index.js +9 -0
- package/dist/providers/vps/index.d.ts +4 -0
- package/dist/providers/vps/index.js +4 -0
- package/dist/realtime/client-error.d.ts +30 -0
- package/dist/realtime/client-error.js +17 -0
- package/dist/realtime/client-event-socket.d.ts +8 -0
- package/dist/realtime/client-event-socket.js +169 -0
- package/dist/realtime/client-transport.d.ts +29 -0
- package/dist/realtime/client-transport.js +13 -0
- package/dist/realtime/client.d.ts +150 -0
- package/dist/realtime/client.js +345 -0
- package/dist/realtime/express.d.ts +18 -0
- package/dist/realtime/express.js +83 -0
- package/dist/realtime/handler.d.ts +56 -0
- package/dist/realtime/handler.js +287 -0
- package/dist/realtime/hono.d.ts +19 -0
- package/dist/realtime/hono.js +24 -0
- package/dist/realtime/index.d.ts +2 -0
- package/dist/realtime/index.js +2 -0
- package/dist/realtime/next.d.ts +26 -0
- package/dist/realtime/next.js +16 -0
- package/dist/realtime/realtime.d.ts +181 -0
- package/dist/realtime/realtime.js +205 -0
- package/dist/realtime/token.d.ts +20 -0
- package/dist/realtime/token.js +45 -0
- package/dist/realtime/transport.d.ts +11 -0
- package/dist/realtime/transport.js +15 -0
- package/dist/realtime/wire.d.ts +30 -0
- package/dist/realtime/wire.js +104 -0
- package/dist/runtime/dev-server.d.ts +8 -0
- package/dist/runtime/dev-server.js +8 -0
- package/dist/runtime/rpc.d.ts +23 -0
- package/dist/runtime/rpc.js +24 -0
- package/dist/runtime/transport.d.ts +11 -0
- package/dist/runtime/transport.js +35 -0
- package/dist/task/errors.d.ts +7 -0
- package/dist/task/errors.js +7 -0
- package/dist/task/hooks.d.ts +57 -0
- package/dist/task/hooks.js +1 -0
- package/dist/task/index.d.ts +9 -0
- package/dist/task/index.js +4 -0
- package/dist/task/runs.d.ts +74 -0
- package/dist/task/runs.js +82 -0
- package/dist/task/task.d.ts +117 -0
- package/dist/task/task.js +109 -0
- package/dist/topic/dead-letters.d.ts +53 -0
- package/dist/topic/dead-letters.js +62 -0
- package/dist/topic/index.d.ts +8 -0
- package/dist/topic/index.js +4 -0
- package/dist/topic/topic.d.ts +85 -0
- package/dist/topic/topic.js +117 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +2 -0
- package/dist/worker/context.d.ts +36 -0
- package/dist/worker/context.js +1 -0
- package/dist/worker/deliver.d.ts +13 -0
- package/dist/worker/deliver.js +258 -0
- package/dist/worker/index.d.ts +4 -0
- package/dist/worker/index.js +2 -0
- package/dist/worker/registry.d.ts +28 -0
- package/dist/worker/registry.js +22 -0
- package/dist/worker/semaphore.d.ts +7 -0
- package/dist/worker/semaphore.js +36 -0
- package/dist/worker/worker.d.ts +21 -0
- package/dist/worker/worker.js +32 -0
- package/package.json +158 -57
- package/bin/run.js +0 -39
- package/dist/blob/bucket.d.ts +0 -55
- package/dist/blob/bucket.js +0 -65
- package/dist/blob/index.d.ts +0 -8
- package/dist/blob/index.js +0 -13
- package/dist/blob/integrations/client.d.ts +0 -25
- package/dist/blob/integrations/client.js +0 -71
- package/dist/blob/integrations/hono.d.ts +0 -31
- package/dist/blob/integrations/hono.js +0 -11
- package/dist/blob/mime-types.d.ts +0 -1
- package/dist/blob/parse.d.ts +0 -16
- package/dist/blob/parse.js +0 -69
- package/dist/blob/s3-listener.d.ts +0 -7
- package/dist/blob/s3-listener.js +0 -35
- package/dist/blob/types.d.ts +0 -128
- package/dist/blob/types.js +0 -1
- package/dist/blob/upload-error.d.ts +0 -35
- package/dist/blob/upload-error.js +0 -54
- package/dist/blob/uploader.d.ts +0 -67
- package/dist/blob/uploader.js +0 -323
- package/dist/internal/db/index.d.ts +0 -68
- package/dist/internal/db/index.js +0 -73
- package/dist/lambda/index.d.ts +0 -2
- package/dist/lambda/index.js +0 -4
- package/dist/lambda/integrations/express.d.ts +0 -3
- package/dist/lambda/integrations/express.js +0 -13
- package/dist/lambda/integrations/hono.d.ts +0 -3
- package/dist/lambda/integrations/hono.js +0 -13
- package/dist/lambda/lambda.d.ts +0 -43
- package/dist/lambda/lambda.js +0 -30
- package/dist/utils/component.d.ts +0 -3
- package/dist/utils/constants.d.ts +0 -2
- package/dist/utils/constants.js +0 -2
- package/dist/utils/nanoid.d.ts +0 -1
- package/dist/utils/nanoid.js +0 -6
- package/dist/utils/rpc.d.ts +0 -11
- package/dist/utils/rpc.js +0 -17
- package/dist/utils/stack.d.ts +0 -4
- package/dist/utils/stack.js +0 -23
- package/dist/workflow/index.d.ts +0 -3
- package/dist/workflow/index.js +0 -4
- package/dist/workflow/workflow.d.ts +0 -34
- package/dist/workflow/workflow.js +0 -146
- package/src/blob/bucket.ts +0 -92
- package/src/blob/index.ts +0 -34
- package/src/blob/integrations/client.ts +0 -127
- package/src/blob/integrations/hono.ts +0 -16
- package/src/blob/mime-types.ts +0 -68
- package/src/blob/parse.ts +0 -93
- package/src/blob/s3-listener.ts +0 -48
- package/src/blob/types.ts +0 -142
- package/src/blob/upload-error.ts +0 -67
- package/src/blob/uploader.ts +0 -426
- package/src/internal/db/index.ts +0 -78
- package/src/lambda/index.ts +0 -8
- package/src/lambda/integrations/express.ts +0 -25
- package/src/lambda/integrations/hono.ts +0 -25
- package/src/lambda/lambda.ts +0 -94
- package/src/postgres/index.ts +0 -5
- package/src/postgres/pg.ts +0 -33
- package/src/utils/component.ts +0 -3
- package/src/utils/constants.ts +0 -2
- package/src/utils/nanoid.ts +0 -9
- package/src/utils/rpc.ts +0 -28
- package/src/utils/stack.ts +0 -26
- package/src/workflow/index.ts +0 -9
- package/src/workflow/workflow.ts +0 -235
- /package/dist/{blob/mime-types.js → bucket/server-only.d.ts} +0 -0
- /package/dist/{utils/component.js → bucket/types.js} +0 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { createClient } from "@connectrpc/connect";
|
|
3
|
+
import { getConfig, refuseUnbound } from "../binding/binding.js";
|
|
4
|
+
import { unprovisioned, unprovisionedPhase } from "../binding/unprovisioned.js";
|
|
5
|
+
import { declarationSite } from "../declaration/callsite.js";
|
|
6
|
+
import { defer } from "../declaration/defer.js";
|
|
7
|
+
import { encodeDuration, parseDurationMilliseconds } from "../delivery/duration.js";
|
|
8
|
+
import { encodeJsonSchema, validatePayload } from "../delivery/schema.js";
|
|
9
|
+
import { RealtimeService } from "../gen/proto/app/realtime/v1/realtime_pb.js";
|
|
10
|
+
import { RealtimePublish, RealtimeSubscribe, ResourceType, } from "../gen/proto/app/resources/v1/resources_pb.js";
|
|
11
|
+
import { rpc } from "../runtime/rpc.js";
|
|
12
|
+
import { createRuntimeTransport } from "../runtime/transport.js";
|
|
13
|
+
import { encodeWireChannel, isChannelNamespace, parseChannelPattern, } from "./wire.js";
|
|
14
|
+
/** Thrown by `publish` when the pattern, params or body cannot be published, naming why. */
|
|
15
|
+
export class RealtimePublishError extends Error {
|
|
16
|
+
name = "RealtimePublishError";
|
|
17
|
+
/**
|
|
18
|
+
* The reason: `unknown-pattern`, `invalid-params`, `missing-param`, `unknown-param`,
|
|
19
|
+
* `empty-value`, `value-too-long`, `invalid-body` or `body-too-large`.
|
|
20
|
+
*/
|
|
21
|
+
code;
|
|
22
|
+
constructor(code, message) {
|
|
23
|
+
super(message);
|
|
24
|
+
this.code = code;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const runtimes = new WeakMap();
|
|
28
|
+
/** Reads what the realtime handler serves of `rt`, throwing for one `realtime()` did not declare. */
|
|
29
|
+
export function readRuntime(rt) {
|
|
30
|
+
const runtime = runtimes.get(rt);
|
|
31
|
+
if (!runtime)
|
|
32
|
+
throw new Error("the realtime handler takes a resource declared by realtime()");
|
|
33
|
+
return runtime;
|
|
34
|
+
}
|
|
35
|
+
const minTtlSeconds = 10;
|
|
36
|
+
const maxTtlSeconds = 300;
|
|
37
|
+
const defaultTtl = "60s";
|
|
38
|
+
/** A declared realtime resource, and the handle its channels are published through. */
|
|
39
|
+
export class Realtime {
|
|
40
|
+
/** The name this resource was declared under, which begins every channel. */
|
|
41
|
+
name;
|
|
42
|
+
constructor(name, options) {
|
|
43
|
+
this.name = name;
|
|
44
|
+
const what = `realtime("${name}")`;
|
|
45
|
+
if (!isChannelNamespace(name)) {
|
|
46
|
+
throw new Error(`${what}: the name begins every channel, so it is letters, digits and -, at most 50 characters, starting and ending with a letter or digit`);
|
|
47
|
+
}
|
|
48
|
+
const ttl = options.tokenTtl ?? defaultTtl;
|
|
49
|
+
const ttlSeconds = parseDurationMilliseconds(ttl) / 1_000;
|
|
50
|
+
if (!Number.isInteger(ttlSeconds) || ttlSeconds < minTtlSeconds || ttlSeconds > maxTtlSeconds) {
|
|
51
|
+
throw new Error(`${what}: token ttl ${ttlSeconds}s is outside ${minTtlSeconds}s to ${maxTtlSeconds}s in whole seconds: a token lives long enough to open a socket and no longer`);
|
|
52
|
+
}
|
|
53
|
+
const channels = new Map();
|
|
54
|
+
for (const [written, declared] of Object.entries(options.channels)) {
|
|
55
|
+
let pattern;
|
|
56
|
+
try {
|
|
57
|
+
pattern = parseChannelPattern(written);
|
|
58
|
+
}
|
|
59
|
+
catch (cause) {
|
|
60
|
+
throw new Error(`${what}: ${cause.message}`);
|
|
61
|
+
}
|
|
62
|
+
if (!declared.schema?.["~standard"]) {
|
|
63
|
+
throw new Error(`${what}: channel "${written}" takes a Standard Schema as its schema, and was given none`);
|
|
64
|
+
}
|
|
65
|
+
if (!options.authorize && (declared.subscribe !== "public" || declared.publish)) {
|
|
66
|
+
throw new Error(`${what}: channel "${written}" declares a rule, which decides from the caller's auth, and the resource declares no authorize to say who calls`);
|
|
67
|
+
}
|
|
68
|
+
channels.set(written, {
|
|
69
|
+
pattern,
|
|
70
|
+
schema: declared.schema,
|
|
71
|
+
wildcard: declared.wildcard ?? false,
|
|
72
|
+
subscribe: declared.subscribe,
|
|
73
|
+
publish: declared.publish,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
if (process.env.OCEL_PHASE === "discovery") {
|
|
77
|
+
const source = declarationSite();
|
|
78
|
+
defer(rpc.resource.declare({
|
|
79
|
+
resource: { name, type: ResourceType.REALTIME },
|
|
80
|
+
config: {
|
|
81
|
+
case: "realtime",
|
|
82
|
+
value: {
|
|
83
|
+
tokenTtl: encodeDuration(ttl),
|
|
84
|
+
channels: [...channels].map(([written, channel]) => ({
|
|
85
|
+
pattern: written,
|
|
86
|
+
wildcard: channel.wildcard,
|
|
87
|
+
schema: encodeJsonSchema(channel.schema),
|
|
88
|
+
subscribe: channel.subscribe === "public"
|
|
89
|
+
? RealtimeSubscribe.PUBLIC
|
|
90
|
+
: RealtimeSubscribe.RULE,
|
|
91
|
+
publish: channel.publish ? RealtimePublish.RULE : RealtimePublish.SERVER,
|
|
92
|
+
source,
|
|
93
|
+
})),
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
source,
|
|
97
|
+
}));
|
|
98
|
+
}
|
|
99
|
+
let client;
|
|
100
|
+
runtimes.set(this, {
|
|
101
|
+
name,
|
|
102
|
+
ttlSeconds,
|
|
103
|
+
channels,
|
|
104
|
+
authorize: options.authorize,
|
|
105
|
+
properties: (access) => {
|
|
106
|
+
if (unprovisionedPhase())
|
|
107
|
+
throw unprovisioned(what, access);
|
|
108
|
+
return getConfig(name, "realtime");
|
|
109
|
+
},
|
|
110
|
+
publish: async (access, channel, envelope) => {
|
|
111
|
+
if (unprovisionedPhase())
|
|
112
|
+
throw unprovisioned(what, access);
|
|
113
|
+
const refusal = refuseUnbound(name, "realtime");
|
|
114
|
+
if (refusal)
|
|
115
|
+
throw refusal;
|
|
116
|
+
client ??= createClient(RealtimeService, createRuntimeTransport());
|
|
117
|
+
try {
|
|
118
|
+
await client.publish({ realtime: name, channel, event: envelope });
|
|
119
|
+
}
|
|
120
|
+
catch (cause) {
|
|
121
|
+
throw new Error(`${what}: publish on ${channel}: ${cause.message}`, { cause });
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Publishes `body` on the channel `params` fill in `pattern`, to every subscriber, after
|
|
128
|
+
* validating it against the pattern's schema. Every param is required. Throws a
|
|
129
|
+
* {@link RealtimePublishError} for a pattern, params or body that cannot be published.
|
|
130
|
+
*/
|
|
131
|
+
async publish(pattern, message) {
|
|
132
|
+
const runtime = readRuntime(this);
|
|
133
|
+
const prepared = await prepareEvent(runtime, pattern, message.params ?? {}, message.body);
|
|
134
|
+
if ("refused" in prepared) {
|
|
135
|
+
throw new RealtimePublishError(prepared.refused, `realtime("${this.name}") cannot publish on "${pattern}": ${prepared.reason}`);
|
|
136
|
+
}
|
|
137
|
+
await runtime.publish("publish", prepared.channel, prepared.envelope);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
const maxEventBytes = 240 * 1024;
|
|
141
|
+
/** Whether `params` is an object mapping each name to a string. */
|
|
142
|
+
export function isParams(params) {
|
|
143
|
+
return (params !== null &&
|
|
144
|
+
typeof params === "object" &&
|
|
145
|
+
!Array.isArray(params) &&
|
|
146
|
+
Object.values(params).every((value) => typeof value === "string"));
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Validates `body` against `channel`'s schema and encodes the envelope it is published in
|
|
150
|
+
* on `wire`, refusing a body the schema rejects or an event over 240 KiB.
|
|
151
|
+
*/
|
|
152
|
+
export async function encodeEvent(channel, wire, body) {
|
|
153
|
+
const validation = await validatePayload(channel.schema, body);
|
|
154
|
+
if (!validation.ok)
|
|
155
|
+
return { refused: "invalid-body", reason: `the body fails its schema: ${validation.message}` };
|
|
156
|
+
const envelope = JSON.stringify({
|
|
157
|
+
v: 1,
|
|
158
|
+
id: randomBytes(16).toString("hex"),
|
|
159
|
+
ch: wire,
|
|
160
|
+
ts: Date.now(),
|
|
161
|
+
kind: "live",
|
|
162
|
+
data: validation.value,
|
|
163
|
+
});
|
|
164
|
+
if (new TextEncoder().encode(envelope).byteLength > maxEventBytes) {
|
|
165
|
+
return { refused: "body-too-large", reason: `an event is at most ${maxEventBytes} bytes` };
|
|
166
|
+
}
|
|
167
|
+
return { channel: wire, body: validation.value, envelope };
|
|
168
|
+
}
|
|
169
|
+
/** Prepares the event the server publishes on `pattern`, or says why it cannot. */
|
|
170
|
+
export async function prepareEvent(runtime, pattern, params, body) {
|
|
171
|
+
const channel = runtime.channels.get(pattern);
|
|
172
|
+
if (!channel)
|
|
173
|
+
return { refused: "unknown-pattern", reason: "no channel of that pattern is declared" };
|
|
174
|
+
if (!isParams(params))
|
|
175
|
+
return { refused: "invalid-params", reason: "params map each name to a string" };
|
|
176
|
+
const wire = encodeWireChannel(runtime.name, channel.pattern, params, false);
|
|
177
|
+
if ("refused" in wire)
|
|
178
|
+
return { refused: wire.refused, reason: `the params are refused: ${wire.refused}` };
|
|
179
|
+
return encodeEvent(channel, wire.channel, body);
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Declares a realtime resource named `name`: typed channels that browsers subscribe to
|
|
183
|
+
* and the server publishes on, each pattern with the rules that decide who may.
|
|
184
|
+
*
|
|
185
|
+
* ```ts
|
|
186
|
+
* export const rt = realtime("app", {
|
|
187
|
+
* authorize: async (req) => (await getSession(req)) ?? null,
|
|
188
|
+
* channels: {
|
|
189
|
+
* "orders/:orderId": {
|
|
190
|
+
* schema: OrderEvent,
|
|
191
|
+
* subscribe: async ({ auth, params }) => ownsOrder(auth, params.orderId),
|
|
192
|
+
* },
|
|
193
|
+
* status: { schema: Status, subscribe: "public" },
|
|
194
|
+
* },
|
|
195
|
+
* });
|
|
196
|
+
*
|
|
197
|
+
* await rt.publish("orders/:orderId", { params: { orderId }, body: { status: "shipped" } });
|
|
198
|
+
* ```
|
|
199
|
+
*
|
|
200
|
+
* Serve it to browsers with `createRealtimeHandler` from `ocel/realtime/next`,
|
|
201
|
+
* `ocel/realtime/express` or `ocel/realtime/hono`.
|
|
202
|
+
*/
|
|
203
|
+
export function realtime(name, options) {
|
|
204
|
+
return new Realtime(name, options);
|
|
205
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { RealtimeProperties } from "../gen/proto/common/bindings/v1/bindings_pb.js";
|
|
2
|
+
/** The operation a token admits its holder to on its channel. */
|
|
3
|
+
export type TokenOperation = "connect" | "subscribe" | "publish";
|
|
4
|
+
/** The realtime resource a token is minted for: its name and how long its tokens live. */
|
|
5
|
+
export interface TokenIssuer {
|
|
6
|
+
name: string;
|
|
7
|
+
ttlSeconds: number;
|
|
8
|
+
}
|
|
9
|
+
/** A signed token and the Unix second it expires at. */
|
|
10
|
+
export interface Token {
|
|
11
|
+
token: string;
|
|
12
|
+
expiresAt: number;
|
|
13
|
+
}
|
|
14
|
+
/** Signs `header` and `claims` as an EdDSA JWT with an Ed25519 signing key. */
|
|
15
|
+
export declare function signToken(signingKey: Uint8Array, header: Record<string, unknown>, claims: Record<string, unknown>): string;
|
|
16
|
+
/**
|
|
17
|
+
* Mints the token admitting `subject` to `operation` on `channel` of `issuer`, signed with
|
|
18
|
+
* the binding's key for the binding's host. Throws when the binding's key cannot sign.
|
|
19
|
+
*/
|
|
20
|
+
export declare function mintToken(properties: RealtimeProperties, issuer: TokenIssuer, subject: string, operation: TokenOperation, channel: string): Token;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { createPrivateKey, randomBytes, sign } from "node:crypto";
|
|
2
|
+
const tokenHeader = { alg: "EdDSA", typ: "JWT" };
|
|
3
|
+
const pkcs8Ed25519Prefix = Buffer.from("302e020100300506032b657004220420", "hex");
|
|
4
|
+
function encodeCanonicalJson(value) {
|
|
5
|
+
if (Array.isArray(value))
|
|
6
|
+
return `[${value.map(encodeCanonicalJson).join(",")}]`;
|
|
7
|
+
if (value !== null && typeof value === "object") {
|
|
8
|
+
const members = Object.keys(value)
|
|
9
|
+
.sort()
|
|
10
|
+
.map((key) => `${JSON.stringify(key)}:${encodeCanonicalJson(value[key])}`);
|
|
11
|
+
return `{${members.join(",")}}`;
|
|
12
|
+
}
|
|
13
|
+
return JSON.stringify(value);
|
|
14
|
+
}
|
|
15
|
+
function encodeBase64url(text) {
|
|
16
|
+
return Buffer.from(text).toString("base64url");
|
|
17
|
+
}
|
|
18
|
+
/** Signs `header` and `claims` as an EdDSA JWT with an Ed25519 signing key. */
|
|
19
|
+
export function signToken(signingKey, header, claims) {
|
|
20
|
+
const key = createPrivateKey({
|
|
21
|
+
key: Buffer.concat([pkcs8Ed25519Prefix, Buffer.from(signingKey)]),
|
|
22
|
+
format: "der",
|
|
23
|
+
type: "pkcs8",
|
|
24
|
+
});
|
|
25
|
+
const input = `${encodeBase64url(encodeCanonicalJson(header))}.${encodeBase64url(encodeCanonicalJson(claims))}`;
|
|
26
|
+
return `${input}.${encodeBase64url(sign(null, Buffer.from(input), key))}`;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Mints the token admitting `subject` to `operation` on `channel` of `issuer`, signed with
|
|
30
|
+
* the binding's key for the binding's host. Throws when the binding's key cannot sign.
|
|
31
|
+
*/
|
|
32
|
+
export function mintToken(properties, issuer, subject, operation, channel) {
|
|
33
|
+
const issuedAt = Math.floor(Date.now() / 1_000);
|
|
34
|
+
const expiresAt = issuedAt + issuer.ttlSeconds;
|
|
35
|
+
const token = signToken(properties.signingKey, tokenHeader, {
|
|
36
|
+
iss: `ocel:rt:${issuer.name}`,
|
|
37
|
+
aud: properties.host,
|
|
38
|
+
sub: subject,
|
|
39
|
+
iat: issuedAt,
|
|
40
|
+
exp: expiresAt,
|
|
41
|
+
jti: randomBytes(16).toString("base64url"),
|
|
42
|
+
ocel: { op: operation, ch: channel, ns: issuer.name },
|
|
43
|
+
});
|
|
44
|
+
return { token, expiresAt };
|
|
45
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type RealtimeProperties } from "../gen/proto/common/bindings/v1/bindings_pb.js";
|
|
2
|
+
/** The transport a realtime binding names, as the handler names it to a browser. */
|
|
3
|
+
export interface Transport {
|
|
4
|
+
name: "appsync-events" | "ocel-gateway";
|
|
5
|
+
host?: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Resolves the transport `properties` names for the resource `name`. Throws for a
|
|
9
|
+
* transport this SDK does not speak.
|
|
10
|
+
*/
|
|
11
|
+
export declare function resolveTransport(properties: RealtimeProperties, name: string): Transport;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { RealtimeTransport, } from "../gen/proto/common/bindings/v1/bindings_pb.js";
|
|
2
|
+
/**
|
|
3
|
+
* Resolves the transport `properties` names for the resource `name`. Throws for a
|
|
4
|
+
* transport this SDK does not speak.
|
|
5
|
+
*/
|
|
6
|
+
export function resolveTransport(properties, name) {
|
|
7
|
+
switch (properties.transport) {
|
|
8
|
+
case RealtimeTransport.OCEL_GATEWAY:
|
|
9
|
+
return { name: "ocel-gateway" };
|
|
10
|
+
case RealtimeTransport.APPSYNC_EVENTS:
|
|
11
|
+
return { name: "appsync-events", host: properties.host };
|
|
12
|
+
default:
|
|
13
|
+
throw new Error(`realtime("${name}"): the binding names transport ${properties.transport}, which this SDK does not speak`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
type Segment = {
|
|
2
|
+
literal: string;
|
|
3
|
+
} | {
|
|
4
|
+
parameter: string;
|
|
5
|
+
};
|
|
6
|
+
/** A channel pattern as written, split into its literal and `:parameter` segments. */
|
|
7
|
+
export interface ChannelPattern {
|
|
8
|
+
readonly written: string;
|
|
9
|
+
readonly segments: readonly Segment[];
|
|
10
|
+
}
|
|
11
|
+
/** Why params cannot fill a pattern into a wire channel. */
|
|
12
|
+
export type WireRefusal = "missing-param" | "unknown-param" | "empty-value" | "value-too-long";
|
|
13
|
+
/** The wire channel params fill a pattern into, or why they cannot. */
|
|
14
|
+
export type WireChannel = {
|
|
15
|
+
channel: string;
|
|
16
|
+
} | {
|
|
17
|
+
refused: WireRefusal;
|
|
18
|
+
};
|
|
19
|
+
/** Parses a written channel pattern, throwing for one its grammar does not allow. */
|
|
20
|
+
export declare function parseChannelPattern(written: string): ChannelPattern;
|
|
21
|
+
/** Whether `name` can begin every channel of a realtime resource. */
|
|
22
|
+
export declare function isChannelNamespace(name: string): boolean;
|
|
23
|
+
/** The names of the `:parameter` segments of `pattern`, in order. */
|
|
24
|
+
export declare function listPatternParameters(pattern: ChannelPattern): string[];
|
|
25
|
+
/**
|
|
26
|
+
* Encodes the wire channel `params` fill `pattern` into under `namespace`; a `wildcard`
|
|
27
|
+
* pattern may leave off trailing params and ends in `*`.
|
|
28
|
+
*/
|
|
29
|
+
export declare function encodeWireChannel(namespace: string, pattern: ChannelPattern, params: Record<string, string>, wildcard: boolean): WireChannel;
|
|
30
|
+
export {};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
const maxSegments = 4;
|
|
2
|
+
const maxValueBytes = 30;
|
|
3
|
+
const channelSegment = /^[A-Za-z0-9](?:[A-Za-z0-9-]{0,48}[A-Za-z0-9])?$/;
|
|
4
|
+
const parameterSegment = /^:[A-Za-z_][A-Za-z0-9_]*$/;
|
|
5
|
+
const segmentRule = "letters, digits and -, at most 50 characters, starting and ending with a letter or digit";
|
|
6
|
+
/** Parses a written channel pattern, throwing for one its grammar does not allow. */
|
|
7
|
+
export function parseChannelPattern(written) {
|
|
8
|
+
if (written === "") {
|
|
9
|
+
throw new Error(`the pattern is empty: write one to ${maxSegments} segments joined by /, each a literal or a :parameter`);
|
|
10
|
+
}
|
|
11
|
+
const parts = written.split("/");
|
|
12
|
+
if (parts.length > maxSegments) {
|
|
13
|
+
throw new Error(`pattern "${written}" has ${parts.length} segments, and a channel pattern has at most ${maxSegments}`);
|
|
14
|
+
}
|
|
15
|
+
const segments = [];
|
|
16
|
+
const named = new Set();
|
|
17
|
+
for (const part of parts) {
|
|
18
|
+
if (part === "") {
|
|
19
|
+
throw new Error(`pattern "${written}" has an empty segment: it neither starts nor ends with /, and no two / are adjacent`);
|
|
20
|
+
}
|
|
21
|
+
if (part.startsWith(":")) {
|
|
22
|
+
if (!parameterSegment.test(part)) {
|
|
23
|
+
throw new Error(`pattern "${written}" has segment "${part}", which is no parameter: a parameter is : and a name of letters, digits and _ that starts with a letter or _`);
|
|
24
|
+
}
|
|
25
|
+
const name = part.slice(1);
|
|
26
|
+
if (named.has(name)) {
|
|
27
|
+
throw new Error(`pattern "${written}" names parameter "${name}" twice, so a channel could not say which value is which`);
|
|
28
|
+
}
|
|
29
|
+
named.add(name);
|
|
30
|
+
segments.push({ parameter: name });
|
|
31
|
+
}
|
|
32
|
+
else if (!channelSegment.test(part)) {
|
|
33
|
+
throw new Error(`pattern "${written}" has segment "${part}", which is no literal: a literal is ${segmentRule}`);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
segments.push({ literal: part });
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return { written, segments };
|
|
40
|
+
}
|
|
41
|
+
/** Whether `name` can begin every channel of a realtime resource. */
|
|
42
|
+
export function isChannelNamespace(name) {
|
|
43
|
+
return channelSegment.test(name);
|
|
44
|
+
}
|
|
45
|
+
/** The names of the `:parameter` segments of `pattern`, in order. */
|
|
46
|
+
export function listPatternParameters(pattern) {
|
|
47
|
+
return pattern.segments.flatMap((s) => ("parameter" in s ? [s.parameter] : []));
|
|
48
|
+
}
|
|
49
|
+
const base32Alphabet = "abcdefghijklmnopqrstuvwxyz234567";
|
|
50
|
+
function encodeLowerBase32(bytes) {
|
|
51
|
+
let out = "";
|
|
52
|
+
let buffer = 0;
|
|
53
|
+
let bits = 0;
|
|
54
|
+
for (const byte of bytes) {
|
|
55
|
+
buffer = (buffer << 8) | byte;
|
|
56
|
+
bits += 8;
|
|
57
|
+
while (bits >= 5) {
|
|
58
|
+
bits -= 5;
|
|
59
|
+
out += base32Alphabet[(buffer >> bits) & 31];
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (bits > 0)
|
|
63
|
+
out += base32Alphabet[(buffer << (5 - bits)) & 31];
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
66
|
+
function encodeValue(value, bytes) {
|
|
67
|
+
if (channelSegment.test(value) && !value.startsWith("0z"))
|
|
68
|
+
return value;
|
|
69
|
+
return `0z${encodeLowerBase32(bytes)}`;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Encodes the wire channel `params` fill `pattern` into under `namespace`; a `wildcard`
|
|
73
|
+
* pattern may leave off trailing params and ends in `*`.
|
|
74
|
+
*/
|
|
75
|
+
export function encodeWireChannel(namespace, pattern, params, wildcard) {
|
|
76
|
+
const names = listPatternParameters(pattern);
|
|
77
|
+
for (const name of Object.keys(params)) {
|
|
78
|
+
if (!names.includes(name))
|
|
79
|
+
return { refused: "unknown-param" };
|
|
80
|
+
}
|
|
81
|
+
const channel = ["", namespace];
|
|
82
|
+
for (const [i, segment] of pattern.segments.entries()) {
|
|
83
|
+
if ("literal" in segment) {
|
|
84
|
+
channel.push(segment.literal);
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
const value = Object.hasOwn(params, segment.parameter) ? params[segment.parameter] : undefined;
|
|
88
|
+
if (value === undefined) {
|
|
89
|
+
const later = pattern.segments
|
|
90
|
+
.slice(i)
|
|
91
|
+
.some((s) => "parameter" in s && Object.hasOwn(params, s.parameter));
|
|
92
|
+
if (!wildcard || later)
|
|
93
|
+
return { refused: "missing-param" };
|
|
94
|
+
return { channel: [...channel, "*"].join("/") };
|
|
95
|
+
}
|
|
96
|
+
if (value === "")
|
|
97
|
+
return { refused: "empty-value" };
|
|
98
|
+
const bytes = new TextEncoder().encode(value);
|
|
99
|
+
if (bytes.byteLength > maxValueBytes)
|
|
100
|
+
return { refused: "value-too-long" };
|
|
101
|
+
channel.push(encodeValue(value, bytes));
|
|
102
|
+
}
|
|
103
|
+
return { channel: channel.join("/") };
|
|
104
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** The address of the running `ocel dev` server; undefined outside it. */
|
|
2
|
+
export declare const OCEL_DEV_SERVER: string | undefined;
|
|
3
|
+
/** The env key the token every call to the dev server presents is delivered under. */
|
|
4
|
+
export declare const DEV_SERVER_TOKEN = "OCEL_DEV_SERVER_TOKEN";
|
|
5
|
+
/** The delivered dev-server token; empty when none was delivered. */
|
|
6
|
+
export declare const getDevServerToken: () => string;
|
|
7
|
+
/** The header every call to the dev server names this SDK's version in. */
|
|
8
|
+
export declare const SDK_VERSION_HEADER = "Ocel-Sdk-Version";
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** The address of the running `ocel dev` server; undefined outside it. */
|
|
2
|
+
export const OCEL_DEV_SERVER = process.env.OCEL_DEV_SERVER;
|
|
3
|
+
/** The env key the token every call to the dev server presents is delivered under. */
|
|
4
|
+
export const DEV_SERVER_TOKEN = "OCEL_DEV_SERVER_TOKEN";
|
|
5
|
+
/** The delivered dev-server token; empty when none was delivered. */
|
|
6
|
+
export const getDevServerToken = () => process.env[DEV_SERVER_TOKEN] ?? "";
|
|
7
|
+
/** The header every call to the dev server names this SDK's version in. */
|
|
8
|
+
export const SDK_VERSION_HEADER = "Ocel-Sdk-Version";
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type Interceptor } from "@connectrpc/connect";
|
|
2
|
+
/** Presents the dev-server token and this SDK's version on every call. */
|
|
3
|
+
export declare const authorized: Interceptor;
|
|
4
|
+
/** Clients for the services the dev server serves. */
|
|
5
|
+
export declare const rpc: {
|
|
6
|
+
resource: import("@connectrpc/connect").Client<import("@bufbuild/protobuf/codegenv2").GenService<{
|
|
7
|
+
declare: {
|
|
8
|
+
methodKind: "unary";
|
|
9
|
+
input: typeof import("../gen/proto/app/resources/v1/resources_pb.js").DeclareRequestSchema;
|
|
10
|
+
output: typeof import("../gen/proto/app/resources/v1/resources_pb.js").DeclareResponseSchema;
|
|
11
|
+
};
|
|
12
|
+
declareEnv: {
|
|
13
|
+
methodKind: "unary";
|
|
14
|
+
input: typeof import("../gen/proto/app/resources/v1/variables_pb.js").DeclareEnvRequestSchema;
|
|
15
|
+
output: typeof import("../gen/proto/app/resources/v1/variables_pb.js").DeclareEnvResponseSchema;
|
|
16
|
+
};
|
|
17
|
+
reportEnvProblems: {
|
|
18
|
+
methodKind: "unary";
|
|
19
|
+
input: typeof import("../gen/proto/app/resources/v1/variables_pb.js").ReportEnvProblemsRequestSchema;
|
|
20
|
+
output: typeof import("../gen/proto/app/resources/v1/variables_pb.js").ReportEnvProblemsResponseSchema;
|
|
21
|
+
};
|
|
22
|
+
}>>;
|
|
23
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { createClient } from "@connectrpc/connect";
|
|
2
|
+
import { createConnectTransport } from "@connectrpc/connect-node";
|
|
3
|
+
import { ResourceService } from "../gen/proto/app/resources/v1/resources_pb.js";
|
|
4
|
+
import { SDK_VERSION } from "../version.js";
|
|
5
|
+
import { getDevServerToken, OCEL_DEV_SERVER, SDK_VERSION_HEADER } from "./dev-server.js";
|
|
6
|
+
/** Presents the dev-server token and this SDK's version on every call. */
|
|
7
|
+
export const authorized = (next) => (req) => {
|
|
8
|
+
req.header.set("Authorization", `Bearer ${getDevServerToken()}`);
|
|
9
|
+
req.header.set(SDK_VERSION_HEADER, `js/${SDK_VERSION}`);
|
|
10
|
+
return next(req);
|
|
11
|
+
};
|
|
12
|
+
const _rpc = () => {
|
|
13
|
+
const transport = createConnectTransport({
|
|
14
|
+
httpVersion: "1.1",
|
|
15
|
+
baseUrl: OCEL_DEV_SERVER,
|
|
16
|
+
interceptors: [authorized],
|
|
17
|
+
});
|
|
18
|
+
const resourceClient = createClient(ResourceService, transport);
|
|
19
|
+
return {
|
|
20
|
+
resource: resourceClient,
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
/** Clients for the services the dev server serves. */
|
|
24
|
+
export const rpc = _rpc();
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Transport } from "@connectrpc/connect";
|
|
2
|
+
/** The env key the ocel runtime's address is delivered under. */
|
|
3
|
+
export declare const RUNTIME_ADDRESS = "OCEL_RUNTIME_ADDRESS";
|
|
4
|
+
/** The env key the token every call to the ocel runtime presents is delivered under. */
|
|
5
|
+
export declare const SESSION_TOKEN = "OCEL_SESSION_TOKEN";
|
|
6
|
+
/** The delivered address of the ocel runtime. Throws when none was delivered. */
|
|
7
|
+
export declare const getRuntimeAddress: () => string;
|
|
8
|
+
/** The delivered session token for the ocel runtime. Throws when none was delivered. */
|
|
9
|
+
export declare const getSessionToken: () => string;
|
|
10
|
+
/** A transport to the ocel runtime that presents the session token on every call. Throws when the address or token was not delivered. */
|
|
11
|
+
export declare function createRuntimeTransport(): Transport;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { createConnectTransport } from "@connectrpc/connect-node";
|
|
2
|
+
/** The env key the ocel runtime's address is delivered under. */
|
|
3
|
+
export const RUNTIME_ADDRESS = "OCEL_RUNTIME_ADDRESS";
|
|
4
|
+
/** The env key the token every call to the ocel runtime presents is delivered under. */
|
|
5
|
+
export const SESSION_TOKEN = "OCEL_SESSION_TOKEN";
|
|
6
|
+
/** The delivered address of the ocel runtime. Throws when none was delivered. */
|
|
7
|
+
export const getRuntimeAddress = () => {
|
|
8
|
+
const address = process.env[RUNTIME_ADDRESS];
|
|
9
|
+
if (!address) {
|
|
10
|
+
throw new Error(`${RUNTIME_ADDRESS} is not defined, so no resource the ocel runtime serves can be reached. Run \`ocel dev\` to serve it locally, or \`ocel deploy\` to have the deployed runtime's address delivered.`);
|
|
11
|
+
}
|
|
12
|
+
return address;
|
|
13
|
+
};
|
|
14
|
+
/** The delivered session token for the ocel runtime. Throws when none was delivered. */
|
|
15
|
+
export const getSessionToken = () => {
|
|
16
|
+
const token = process.env[SESSION_TOKEN];
|
|
17
|
+
if (!token) {
|
|
18
|
+
throw new Error(`${SESSION_TOKEN} is not defined, so the ocel runtime at ${RUNTIME_ADDRESS} would refuse every call. It is delivered beside ${RUNTIME_ADDRESS} by \`ocel dev\` and by the deployed runtime, never set by hand.`);
|
|
19
|
+
}
|
|
20
|
+
return token;
|
|
21
|
+
};
|
|
22
|
+
function createAuthorizationInterceptor(token) {
|
|
23
|
+
return (next) => (req) => {
|
|
24
|
+
req.header.set("Authorization", `Bearer ${token}`);
|
|
25
|
+
return next(req);
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/** A transport to the ocel runtime that presents the session token on every call. Throws when the address or token was not delivered. */
|
|
29
|
+
export function createRuntimeTransport() {
|
|
30
|
+
return createConnectTransport({
|
|
31
|
+
httpVersion: "1.1",
|
|
32
|
+
baseUrl: getRuntimeAddress(),
|
|
33
|
+
interceptors: [createAuthorizationInterceptor(getSessionToken())],
|
|
34
|
+
});
|
|
35
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { RunContext } from "../worker/context.js";
|
|
2
|
+
/** How a run ended: with the output its `run` returned, or with the error that failed it. */
|
|
3
|
+
export type TaskResult<TOutput> = {
|
|
4
|
+
ok: true;
|
|
5
|
+
output: TOutput;
|
|
6
|
+
} | {
|
|
7
|
+
ok: false;
|
|
8
|
+
error: unknown;
|
|
9
|
+
};
|
|
10
|
+
/** What `catchError` answers to fail the run now instead of retrying it. */
|
|
11
|
+
export interface CatchErrorResult {
|
|
12
|
+
/** Fails the run without another attempt. */
|
|
13
|
+
skipRetrying?: boolean;
|
|
14
|
+
}
|
|
15
|
+
/** The hooks a task runs around its attempts. */
|
|
16
|
+
export interface TaskHooks<TPayload, TOutput> {
|
|
17
|
+
/** Runs once the run succeeds. An error it throws is reported, and changes nothing. */
|
|
18
|
+
onSuccess?: (args: {
|
|
19
|
+
payload: TPayload;
|
|
20
|
+
output: TOutput;
|
|
21
|
+
ctx: RunContext;
|
|
22
|
+
}) => unknown;
|
|
23
|
+
/** Runs once the run fails for good. An error it throws is reported, and changes nothing. */
|
|
24
|
+
onFailure?: (args: {
|
|
25
|
+
payload: TPayload;
|
|
26
|
+
error: unknown;
|
|
27
|
+
ctx: RunContext;
|
|
28
|
+
}) => unknown;
|
|
29
|
+
/** Runs after `onSuccess` or `onFailure`. An error it throws is reported, and changes nothing. */
|
|
30
|
+
onComplete?: (args: {
|
|
31
|
+
payload: TPayload;
|
|
32
|
+
ctx: RunContext;
|
|
33
|
+
result: TaskResult<TOutput>;
|
|
34
|
+
}) => unknown;
|
|
35
|
+
/** Runs when the run is canceled during an attempt, best-effort. */
|
|
36
|
+
onCancel?: (args: {
|
|
37
|
+
payload: TPayload;
|
|
38
|
+
ctx: RunContext;
|
|
39
|
+
}) => unknown;
|
|
40
|
+
/** Sees every error an attempt throws; answering a {@link CatchErrorResult} of `{ skipRetrying: true }` fails the run now. */
|
|
41
|
+
catchError?: (args: {
|
|
42
|
+
payload: TPayload;
|
|
43
|
+
error: unknown;
|
|
44
|
+
ctx: RunContext;
|
|
45
|
+
}) => unknown;
|
|
46
|
+
/** Wraps every attempt; `next` runs the attempt, and an error it throws fails the attempt. */
|
|
47
|
+
middleware?: (args: {
|
|
48
|
+
payload: TPayload;
|
|
49
|
+
ctx: RunContext;
|
|
50
|
+
next: () => Promise<void>;
|
|
51
|
+
}) => unknown;
|
|
52
|
+
/** Runs before every attempt; an error it throws fails the attempt. */
|
|
53
|
+
onStartAttempt?: (args: {
|
|
54
|
+
payload: TPayload;
|
|
55
|
+
ctx: RunContext;
|
|
56
|
+
}) => unknown;
|
|
57
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { UnprovisionedResourceError } from "../binding/unprovisioned.js";
|
|
2
|
+
export type { Duration } from "../delivery/duration.js";
|
|
3
|
+
export type { Lane } from "../delivery/lane.js";
|
|
4
|
+
export type { RetryOptions } from "../delivery/retry.js";
|
|
5
|
+
export type { RunContext, RunOptions } from "../worker/context.js";
|
|
6
|
+
export { AbortTaskRunError } from "./errors.js";
|
|
7
|
+
export type { CatchErrorResult, TaskHooks, TaskResult } from "./hooks.js";
|
|
8
|
+
export { type ListRunsOptions, type RescheduleOptions, type Run, type RunPage, type RunStatus, runs, } from "./runs.js";
|
|
9
|
+
export { type BatchOptions, type BatchTriggerItem, type RunFunction, type RunHandle, Task, type TaskOptions, type TriggerOptions, task, } from "./task.js";
|