@volter/twin-temporal 0.1.0

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.
Files changed (55) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +104 -0
  3. package/dist/src/cli.d.ts +2 -0
  4. package/dist/src/cli.js +36 -0
  5. package/dist/src/generated/hpack.json +1 -0
  6. package/dist/src/generated/temporal-api.json +1 -0
  7. package/dist/src/index.d.ts +18 -0
  8. package/dist/src/index.js +70 -0
  9. package/dist/src/temporal-budget.d.ts +31 -0
  10. package/dist/src/temporal-budget.js +50 -0
  11. package/dist/src/temporal-capabilities.d.ts +7 -0
  12. package/dist/src/temporal-capabilities.js +650 -0
  13. package/dist/src/temporal-conformance.d.ts +8 -0
  14. package/dist/src/temporal-conformance.js +54 -0
  15. package/dist/src/temporal-connector.d.ts +32 -0
  16. package/dist/src/temporal-connector.js +121 -0
  17. package/dist/src/temporal-engine.d.ts +274 -0
  18. package/dist/src/temporal-engine.js +2132 -0
  19. package/dist/src/temporal-grpc.d.ts +51 -0
  20. package/dist/src/temporal-grpc.js +124 -0
  21. package/dist/src/temporal-h2.d.ts +40 -0
  22. package/dist/src/temporal-h2.js +644 -0
  23. package/dist/src/temporal-history.d.ts +27 -0
  24. package/dist/src/temporal-history.js +85 -0
  25. package/dist/src/temporal-http.d.ts +17 -0
  26. package/dist/src/temporal-http.js +160 -0
  27. package/dist/src/temporal-json.d.ts +7 -0
  28. package/dist/src/temporal-json.js +68 -0
  29. package/dist/src/temporal-proto.d.ts +17 -0
  30. package/dist/src/temporal-proto.js +46 -0
  31. package/dist/src/temporal-server.d.ts +25 -0
  32. package/dist/src/temporal-server.js +56 -0
  33. package/dist/src/temporal-twin.d.ts +25 -0
  34. package/dist/src/temporal-twin.js +100 -0
  35. package/dist/src/temporal-visibility.d.ts +49 -0
  36. package/dist/src/temporal-visibility.js +293 -0
  37. package/package.json +59 -0
  38. package/src/cli.ts +36 -0
  39. package/src/generated/hpack.json +1 -0
  40. package/src/generated/temporal-api.json +1 -0
  41. package/src/index.ts +81 -0
  42. package/src/temporal-budget.ts +75 -0
  43. package/src/temporal-capabilities.ts +679 -0
  44. package/src/temporal-conformance.ts +52 -0
  45. package/src/temporal-connector.ts +118 -0
  46. package/src/temporal-engine.ts +2176 -0
  47. package/src/temporal-grpc.ts +139 -0
  48. package/src/temporal-h2.ts +614 -0
  49. package/src/temporal-history.ts +99 -0
  50. package/src/temporal-http.ts +146 -0
  51. package/src/temporal-json.ts +77 -0
  52. package/src/temporal-proto.ts +53 -0
  53. package/src/temporal-server.ts +67 -0
  54. package/src/temporal-twin.ts +102 -0
  55. package/src/temporal-visibility.ts +245 -0
@@ -0,0 +1,18 @@
1
+ export { createTemporalTwinServer, serveH2, TEMPORAL_LOOPBACK_HOST } from './temporal-server.js';
2
+ export type { TemporalTwinServer } from './temporal-server.js';
3
+ export { handleTemporalCall, SERVED, TEMPORAL_CAPABILITY_FLAGS, TEMPORAL_SERVER_VERSION } from './temporal-twin.js';
4
+ export { TemporalEngine, DEFAULT_NAMESPACE, SYSTEM_SEARCH_ATTRIBUTES, executionInfo, payloadValue, jsonPayload } from './temporal-engine.js';
5
+ export type { Run as TemporalRun, EngineOptions as TemporalEngineOptions } from './temporal-engine.js';
6
+ export { TemporalStore, TEMPORAL_SERVICE } from './temporal-history.js';
7
+ export { grpcStreamHandler, GrpcError, GrpcStatus, grpcFrame, splitGrpcFrames, parseGrpcTimeout } from './temporal-grpc.js';
8
+ export type { GrpcCallContext, GrpcHandler } from './temporal-grpc.js';
9
+ export { acceptH2Connection, HpackDecoder, hpackEncode, huffmanDecode } from './temporal-h2.js';
10
+ export { createTemporalTwinFetch, handleTemporalHttpRequest } from './temporal-http.js';
11
+ export { toProto3Json, fromProto3Json } from './temporal-json.js';
12
+ export { temporalRoot, messageType, serviceMethods, decodeMessage, encodeMessage, WORKFLOW_SERVICE, OPERATOR_SERVICE, HEALTH_SERVICE } from './temporal-proto.js';
13
+ export { parseVisibilityQuery, matchesVisibilityQuery } from './temporal-visibility.js';
14
+ export { liveTemporalExecute, performTemporalAction, syncTemporalFromReal, pushPendingTemporalActions } from './temporal-connector.js';
15
+ export { TemporalBudget, TemporalBudgetError, TEMPORAL_RATE_BUDGET, TEMPORAL_BUDGET_CEILING, TEMPORAL_BUDGET_WINDOW_MS, TEMPORAL_CALL_WEIGHTS, temporalBudgetPath, temporalCallWeight } from './temporal-budget.js';
16
+ export type { TemporalExecute } from './temporal-connector.js';
17
+ import { type TwinPack } from '@volter/world-core';
18
+ export declare const pack: TwinPack;
@@ -0,0 +1,70 @@
1
+ // @volter/twin-temporal — Temporal (the workflow engine) served from the World, containerless: the
2
+ // frontend's gRPC API (WorkflowService, OperatorService, grpc.health) on a loopback port, so an
3
+ // unmodified app on the Temporal SDKs (@temporalio/client + @temporalio/worker) starts, runs and
4
+ // reads workflows against it with nothing installed. It stands where `temporal server start-dev` or
5
+ // Temporal's docker-compose stack stands in local development. Read README.md before extending it.
6
+ export { createTemporalTwinServer, serveH2, TEMPORAL_LOOPBACK_HOST } from "./temporal-server.js";
7
+ export { handleTemporalCall, SERVED, TEMPORAL_CAPABILITY_FLAGS, TEMPORAL_SERVER_VERSION } from "./temporal-twin.js";
8
+ export { TemporalEngine, DEFAULT_NAMESPACE, SYSTEM_SEARCH_ATTRIBUTES, executionInfo, payloadValue, jsonPayload } from "./temporal-engine.js";
9
+ export { TemporalStore, TEMPORAL_SERVICE } from "./temporal-history.js";
10
+ export { grpcStreamHandler, GrpcError, GrpcStatus, grpcFrame, splitGrpcFrames, parseGrpcTimeout } from "./temporal-grpc.js";
11
+ export { acceptH2Connection, HpackDecoder, hpackEncode, huffmanDecode } from "./temporal-h2.js";
12
+ export { createTemporalTwinFetch, handleTemporalHttpRequest } from "./temporal-http.js";
13
+ export { toProto3Json, fromProto3Json } from "./temporal-json.js";
14
+ export { temporalRoot, messageType, serviceMethods, decodeMessage, encodeMessage, WORKFLOW_SERVICE, OPERATOR_SERVICE, HEALTH_SERVICE } from "./temporal-proto.js";
15
+ export { parseVisibilityQuery, matchesVisibilityQuery } from "./temporal-visibility.js";
16
+ export { liveTemporalExecute, performTemporalAction, syncTemporalFromReal, pushPendingTemporalActions } from "./temporal-connector.js";
17
+ export { TemporalBudget, TemporalBudgetError, TEMPORAL_RATE_BUDGET, TEMPORAL_BUDGET_CEILING, TEMPORAL_BUDGET_WINDOW_MS, TEMPORAL_CALL_WEIGHTS, temporalBudgetPath, temporalCallWeight } from "./temporal-budget.js";
18
+ // NB: ./temporal-conformance.ts and ./temporal-capabilities.ts are NOT re-exported — they are dev-only
19
+ // tooling (@volter/world-tooling); cli.ts imports the conformance module lazily.
20
+ import { registerPack } from '@volter/world-core';
21
+ import { TEMPORAL_RATE_BUDGET } from "./temporal-budget.js";
22
+ import { performTemporalAction } from "./temporal-connector.js";
23
+ export const pack = {
24
+ vendor: 'temporal',
25
+ // gRPC is HTTP/2 framing straight onto a TCP socket: the injector patches http/fetch and never sees
26
+ // it (@grpc/grpc-js drives node:http2 itself; the worker's Rust core has its own client), so this is
27
+ // the raw-protocol class — wired by the app-read address, never by a host map.
28
+ transport: 'raw-tcp',
29
+ protocol: '2',
30
+ archetype: 'engine-control',
31
+ // the real-system half: a real head refuses every entry by name (performTemporalAction says why);
32
+ // the pull is syncTemporalFromReal over a gRPC execute, not the kernel's HTTP refresh
33
+ stateSystem: { perform: performTemporalAction },
34
+ // the branch round-trip's writes, on the frontend's HTTP API: start a run, then terminate it. The id
35
+ // is then free (a closed run allows a duplicate), so the same pair on a branch makes a new run there
36
+ // and never touches the run the base already holds.
37
+ roundTrip: [
38
+ { method: 'POST', path: '/api/v1/namespaces/default/workflows/round-trip', body: { workflowType: { name: 'round-trip' }, taskQueue: { name: 'round-trip' } } },
39
+ { method: 'POST', path: '/api/v1/namespaces/default/workflows/round-trip/terminate', body: { reason: 'round-trip' } },
40
+ ],
41
+ rateBudget: TEMPORAL_RATE_BUDGET,
42
+ bin: 'world-temporal',
43
+ resources: ['workflow-execution', 'namespace'],
44
+ // The history beside the tree: content-addressed chunks in the World's blob store, named in order by
45
+ // each run's subject (temporal-history.ts owns every write outside the tree).
46
+ engine: { module: 'temporal-history', note: 'a run\'s history as content-addressed event chunks in the blob store; the workflow-execution subject names them in order' },
47
+ specSource: 'temporalio/api protos as vendored by @temporalio/core-bridge@1.15.0 (sdk-core 12c656f7, crates/common/protos/api_upstream: '
48
+ + 'temporal.api.workflowservice.v1.WorkflowService, temporal.api.operatorservice.v1.OperatorService) + grpc.health.v1; '
49
+ + 'the gRPC wire per grpc/grpc doc/PROTOCOL-HTTP2.md over HTTP/2 (RFC 9113) with HPACK (RFC 7541, vendored)',
50
+ description: 'Temporal frontend twin — WorkflowService over gRPC on loopback: start/signal/signal-with-start/cancel/terminate, '
51
+ + 'workflow and activity task queues with long polls, the history service (activities with retry policies and '
52
+ + 'heartbeats, timers on the World clock, signals, queries, child workflows, continue-as-new, markers, search '
53
+ + 'attributes), history reads, describe and list/count visibility queries; OperatorService search attributes. '
54
+ + 'Every other RPC answers gRPC UNIMPLEMENTED.',
55
+ adoption: {
56
+ sdks: ['@temporalio/client', '@temporalio/worker', '@temporalio/workflow', '@temporalio/activity'],
57
+ scopes: ['@temporalio/'],
58
+ pypi: ['temporalio'],
59
+ envStems: ['TEMPORAL'],
60
+ },
61
+ // INTERCEPTION RULING — hostsNone: there is no host to map. The SDKs take the frontend's address as
62
+ // configuration (`Connection.connect({ address })`, `NativeConnection.connect({ address })`, which
63
+ // apps read from TEMPORAL_ADDRESS — Postiz's temporal.module.ts, the SDK samples, the Temporal CLI's
64
+ // own env), and the traffic is gRPC the http/fetch injector cannot see. The World injects
65
+ // TEMPORAL_ADDRESS=<host>:<port> at this twin's listener (APP_READ_ENDPOINT_ENV in
66
+ // world-runtime/src/init.ts: an address, not a URL, which the descriptor's endpointEnv cannot carry).
67
+ hostsNone: 'RAW-PROTOCOL pack — there is no host to map. The Temporal SDKs dial the frontend address they are configured with (Connection.connect / NativeConnection.connect `address`, conventionally TEMPORAL_ADDRESS) over gRPC, which the http/fetch injector never sees. Interception is the app-read address: the World injects TEMPORAL_ADDRESS=<host>:<port> at this twin (APP_READ_ENDPOINT_ENV in world-runtime/src/init.ts).',
68
+ };
69
+ // a protocol 2 pack registers itself on load: its rate budget arms and its state-system adapters reach the head
70
+ registerPack(pack);
@@ -0,0 +1,31 @@
1
+ import { RateBudget, type RateBudgetDeclaration, type RateBudgetOptions, type RateBudgetReservation, type RateBudgetSnapshot } from '@volter/world-core';
2
+ /** Rolling window, in ms. */
3
+ export declare const TEMPORAL_BUDGET_WINDOW_MS = 60000;
4
+ /** Weighted units per window: 20 calls a minute at weight 1 — under the kernel fallback (60 units at
5
+ * weight 2: 30 calls a minute). */
6
+ export declare const TEMPORAL_BUDGET_CEILING = 20;
7
+ /** Seconds. Above this a frontend is refusing, not throttling — fail loudly. */
8
+ export declare const TEMPORAL_BUDGET_MAX_RETRY_AFTER_S = 300;
9
+ export declare const TEMPORAL_CALL_WEIGHTS: {
10
+ /** a list call reads a page of a cluster's records */
11
+ readonly list: 2;
12
+ /** every other unary call */
13
+ readonly other: 1;
14
+ };
15
+ export declare const TEMPORAL_RATE_BUDGET: RateBudgetDeclaration;
16
+ /** Price one call, keyed `<service>/<method>`; an unclassified call costs the default, never nothing. */
17
+ export declare function temporalCallWeight(service: string, method: string): number;
18
+ /** The ledger: keyed by the frontend address, so one cluster's allowance is shared across checkouts. */
19
+ export declare function temporalBudgetPath(opts?: {
20
+ root?: string;
21
+ token?: string;
22
+ } | string): string;
23
+ export type TemporalBudgetOptions = Omit<RateBudgetOptions, 'vendor'>;
24
+ /** Temporal's budget — the kernel guard bound to this declaration (a subclass, so `instanceof`
25
+ * says which ledger and ceiling it accounts against). */
26
+ export declare class TemporalBudget extends RateBudget {
27
+ constructor(opts?: TemporalBudgetOptions);
28
+ }
29
+ export { RateBudgetError as TemporalBudgetError } from '@volter/world-core';
30
+ export type TemporalBudgetReservation = RateBudgetReservation;
31
+ export type TemporalBudgetSnapshot = RateBudgetSnapshot;
@@ -0,0 +1,50 @@
1
+ // Temporal's CLIENT-SIDE RATE BUDGET — the ceiling the connector's one live call site
2
+ // (liveTemporalExecute) enforces before a call leaves for a real frontend. The mechanism is the
3
+ // kernel's (rateBudget.ts); this file holds only the numbers and why.
4
+ import { declareRateBudget, rateBudgetPath, rateBudgetWeight, RateBudget, } from '@volter/world-core';
5
+ const VENDOR = 'temporal';
6
+ /** Rolling window, in ms. */
7
+ export const TEMPORAL_BUDGET_WINDOW_MS = 60_000;
8
+ /** Weighted units per window: 20 calls a minute at weight 1 — under the kernel fallback (60 units at
9
+ * weight 2: 30 calls a minute). */
10
+ export const TEMPORAL_BUDGET_CEILING = 20;
11
+ /** Seconds. Above this a frontend is refusing, not throttling — fail loudly. */
12
+ export const TEMPORAL_BUDGET_MAX_RETRY_AFTER_S = 300;
13
+ export const TEMPORAL_CALL_WEIGHTS = {
14
+ /** a list call reads a page of a cluster's records */
15
+ list: 2,
16
+ /** every other unary call */
17
+ other: 1,
18
+ };
19
+ export const TEMPORAL_RATE_BUDGET = {
20
+ windowMs: TEMPORAL_BUDGET_WINDOW_MS,
21
+ ceiling: TEMPORAL_BUDGET_CEILING,
22
+ defaultWeight: TEMPORAL_CALL_WEIGHTS.other,
23
+ maxRetryAfterSeconds: TEMPORAL_BUDGET_MAX_RETRY_AFTER_S,
24
+ rules: [
25
+ { match: '/List[A-Za-z]*$', weight: TEMPORAL_CALL_WEIGHTS.list },
26
+ ],
27
+ reason: 'The frontend a pull reads is whatever the operator names — a self-hosted cluster whose limits are its own '
28
+ + 'dynamic config, or a Temporal Cloud namespace metered per namespace — so this declaration cites no vendor '
29
+ + 'figure. A pull is a handful of calls (ListNamespaces, then ListSearchAttributes per namespace); 20 weighted '
30
+ + 'units a minute, list calls at 2, sits strictly under the kernel fallback and far under any frontend\'s '
31
+ + 'request rate (20 plain calls, 10 list calls), so a looping caller is stopped long before it could matter to a cluster.',
32
+ };
33
+ declareRateBudget(VENDOR, TEMPORAL_RATE_BUDGET);
34
+ /** Price one call, keyed `<service>/<method>`; an unclassified call costs the default, never nothing. */
35
+ export function temporalCallWeight(service, method) {
36
+ return rateBudgetWeight(VENDOR, `${service}/${method}`, {});
37
+ }
38
+ /** The ledger: keyed by the frontend address, so one cluster's allowance is shared across checkouts. */
39
+ export function temporalBudgetPath(opts = {}) {
40
+ const o = typeof opts === 'string' ? { root: opts } : opts;
41
+ return rateBudgetPath({ ...o, vendor: VENDOR });
42
+ }
43
+ /** Temporal's budget — the kernel guard bound to this declaration (a subclass, so `instanceof`
44
+ * says which ledger and ceiling it accounts against). */
45
+ export class TemporalBudget extends RateBudget {
46
+ constructor(opts = {}) {
47
+ super({ ...opts, vendor: VENDOR });
48
+ }
49
+ }
50
+ export { RateBudgetError as TemporalBudgetError } from '@volter/world-core';
@@ -0,0 +1,7 @@
1
+ import { type CapabilityReport, type CapabilitySpec } from '@volter/world-tooling';
2
+ export declare const TEMPORAL_CAPABILITIES: CapabilitySpec[];
3
+ /** Committed area census: the three services of the vendored protos, the command table, the history
4
+ * service's own behaviours, the wire, and the connector. A committed literal, so a shrinking
5
+ * regeneration reddens instead of silently dropping an area. */
6
+ export declare const TEMPORAL_AREAS: readonly ["commands", "connector", "health", "history", "operatorservice", "wire", "workflowservice"];
7
+ export declare function temporalCapabilities(): Promise<CapabilityReport>;