@stackables/bridge 1.0.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.
@@ -0,0 +1,15 @@
1
+ import { type GraphQLSchema } from "graphql";
2
+ import type { Instruction, ToolMap } from "./types.js";
3
+ export type BridgeOptions = {
4
+ /** Tool functions available to the engine.
5
+ * Supports namespaced nesting: `{ myNamespace: { myTool } }`.
6
+ * The built-in `std` namespace and `httpCall` are always included;
7
+ * user tools are merged on top (shallow). */
8
+ tools?: ToolMap;
9
+ /** Optional function to reshape/restrict the GQL context before it reaches bridge files.
10
+ * By default the full context is exposed via `with context`. */
11
+ contextMapper?: (context: any) => Record<string, any>;
12
+ };
13
+ /** Instructions can be a static array or a function that selects per-request */
14
+ export type InstructionSource = Instruction[] | ((context: any) => Instruction[]);
15
+ export declare function bridgeTransform(schema: GraphQLSchema, instructions: InstructionSource, options?: BridgeOptions): GraphQLSchema;
@@ -0,0 +1,57 @@
1
+ import { MapperKind, mapSchema } from "@graphql-tools/utils";
2
+ import { GraphQLList, GraphQLNonNull, defaultFieldResolver, } from "graphql";
3
+ import { ExecutionTree } from "./ExecutionTree.js";
4
+ import { builtinTools } from "./tools/index.js";
5
+ import { SELF_MODULE } from "./types.js";
6
+ export function bridgeTransform(schema, instructions, options) {
7
+ const userTools = options?.tools;
8
+ const contextMapper = options?.contextMapper;
9
+ return mapSchema(schema, {
10
+ [MapperKind.OBJECT_FIELD]: (fieldConfig, fieldName, typeName) => {
11
+ let array = false;
12
+ if (fieldConfig.type instanceof GraphQLNonNull) {
13
+ if (fieldConfig.type.ofType instanceof GraphQLList) {
14
+ array = true;
15
+ }
16
+ }
17
+ if (fieldConfig.type instanceof GraphQLList) {
18
+ array = true;
19
+ }
20
+ const trunk = { module: SELF_MODULE, type: typeName, field: fieldName };
21
+ const { resolve = defaultFieldResolver } = fieldConfig;
22
+ return {
23
+ ...fieldConfig,
24
+ resolve: async function (source, args, context, info) {
25
+ // Start execution tree at query/mutation root
26
+ if (!source && !info.path.prev) {
27
+ const bridgeContext = contextMapper
28
+ ? contextMapper(context)
29
+ : (context ?? {});
30
+ const activeInstructions = typeof instructions === "function"
31
+ ? instructions(context)
32
+ : instructions;
33
+ // Always include builtinTools; user tools merge on top (shallow)
34
+ const allTools = {
35
+ ...builtinTools,
36
+ ...(userTools ?? {}),
37
+ };
38
+ source = new ExecutionTree(trunk, activeInstructions, allTools, bridgeContext);
39
+ }
40
+ if (source instanceof ExecutionTree &&
41
+ args &&
42
+ Object.keys(args).length > 0) {
43
+ source.push(args);
44
+ }
45
+ // Kick off forced wires (<-!) at the root entry point
46
+ if (source instanceof ExecutionTree && !info.path.prev) {
47
+ source.executeForced();
48
+ }
49
+ if (source instanceof ExecutionTree) {
50
+ return source.response(info.path, array);
51
+ }
52
+ return resolve(source, args, context, info);
53
+ },
54
+ };
55
+ },
56
+ });
57
+ }
@@ -0,0 +1,5 @@
1
+ export { parseBridge } from "./bridge-format.js";
2
+ export { bridgeTransform } from "./bridge-transform.js";
3
+ export type { BridgeOptions, InstructionSource } from "./bridge-transform.js";
4
+ export { builtinTools, std, createHttpCall } from "./tools/index.js";
5
+ export type { CacheStore, ConstDef, Instruction, ToolCallFn, ToolDef, ToolMap } from "./types.js";
package/build/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { parseBridge } from "./bridge-format.js";
2
+ export { bridgeTransform } from "./bridge-transform.js";
3
+ export { builtinTools, std, createHttpCall } from "./tools/index.js";
@@ -0,0 +1,4 @@
1
+ export declare function findObject(opts: {
2
+ in: any[];
3
+ [key: string]: any;
4
+ }): any;
@@ -0,0 +1,11 @@
1
+ export function findObject(opts) {
2
+ const { in: arr, ...criteria } = opts;
3
+ return arr.find((obj) => {
4
+ for (const [key, value] of Object.entries(criteria)) {
5
+ if (obj[key] !== value) {
6
+ return false;
7
+ }
8
+ }
9
+ return true;
10
+ });
11
+ }
@@ -0,0 +1,34 @@
1
+ import type { CacheStore, ToolCallFn } from "../types.js";
2
+ /**
3
+ * Parse TTL (in seconds) from HTTP response headers.
4
+ *
5
+ * Priority: `Cache-Control: s-maxage` > `Cache-Control: max-age` > `Expires`.
6
+ * Returns 0 if the response is uncacheable (no-store, no-cache, or no headers).
7
+ */
8
+ declare function parseCacheTTL(response: Response): number;
9
+ /**
10
+ * Create an httpCall tool function — the built-in REST API tool.
11
+ *
12
+ * Receives a fully-built input object from the engine and makes an HTTP call.
13
+ * The engine resolves all wires (from tool definition + bridge wires) before calling.
14
+ *
15
+ * Expected input shape:
16
+ * { baseUrl, method?, path?, headers?, cache?, ...shorthandFields }
17
+ *
18
+ * Routing rules:
19
+ * - GET: shorthand fields → query string parameters
20
+ * - POST/PUT/PATCH/DELETE: shorthand fields → JSON body
21
+ * - `headers` object passed as HTTP headers
22
+ * - `baseUrl` + `path` concatenated for the URL
23
+ *
24
+ * Cache modes:
25
+ * - `cache = "auto"` (default) — respect HTTP Cache-Control / Expires headers
26
+ * - `cache = 0` — disable caching entirely
27
+ * - `cache = <seconds>` — explicit TTL override, ignores response headers
28
+ *
29
+ * @param fetchFn - Fetch implementation (override for testing)
30
+ * @param cacheStore - Pluggable cache store (default: in-memory LRU, 1024 entries)
31
+ */
32
+ export declare function createHttpCall(fetchFn?: typeof fetch, cacheStore?: CacheStore): ToolCallFn;
33
+ /** Exported for testing. */
34
+ export { parseCacheTTL };
@@ -0,0 +1,116 @@
1
+ import { LRUCache } from "lru-cache";
2
+ /** Default in-memory LRU cache with per-entry TTL. */
3
+ function createMemoryCache(maxEntries = 1024) {
4
+ const lru = new LRUCache({ max: maxEntries });
5
+ return {
6
+ get(key) { return lru.get(key); },
7
+ set(key, value, ttlSeconds) {
8
+ if (ttlSeconds <= 0)
9
+ return;
10
+ lru.set(key, value, { ttl: ttlSeconds * 1000 });
11
+ },
12
+ };
13
+ }
14
+ /**
15
+ * Parse TTL (in seconds) from HTTP response headers.
16
+ *
17
+ * Priority: `Cache-Control: s-maxage` > `Cache-Control: max-age` > `Expires`.
18
+ * Returns 0 if the response is uncacheable (no-store, no-cache, or no headers).
19
+ */
20
+ function parseCacheTTL(response) {
21
+ const cc = response.headers.get("cache-control");
22
+ if (cc) {
23
+ if (/\bno-store\b/i.test(cc) || /\bno-cache\b/i.test(cc))
24
+ return 0;
25
+ const sMax = cc.match(/\bs-maxage\s*=\s*(\d+)\b/i);
26
+ if (sMax)
27
+ return Number(sMax[1]);
28
+ const max = cc.match(/\bmax-age\s*=\s*(\d+)\b/i);
29
+ if (max)
30
+ return Number(max[1]);
31
+ }
32
+ const expires = response.headers.get("expires");
33
+ if (expires) {
34
+ const delta = Math.floor((new Date(expires).getTime() - Date.now()) / 1000);
35
+ return delta > 0 ? delta : 0;
36
+ }
37
+ return 0;
38
+ }
39
+ /**
40
+ * Create an httpCall tool function — the built-in REST API tool.
41
+ *
42
+ * Receives a fully-built input object from the engine and makes an HTTP call.
43
+ * The engine resolves all wires (from tool definition + bridge wires) before calling.
44
+ *
45
+ * Expected input shape:
46
+ * { baseUrl, method?, path?, headers?, cache?, ...shorthandFields }
47
+ *
48
+ * Routing rules:
49
+ * - GET: shorthand fields → query string parameters
50
+ * - POST/PUT/PATCH/DELETE: shorthand fields → JSON body
51
+ * - `headers` object passed as HTTP headers
52
+ * - `baseUrl` + `path` concatenated for the URL
53
+ *
54
+ * Cache modes:
55
+ * - `cache = "auto"` (default) — respect HTTP Cache-Control / Expires headers
56
+ * - `cache = 0` — disable caching entirely
57
+ * - `cache = <seconds>` — explicit TTL override, ignores response headers
58
+ *
59
+ * @param fetchFn - Fetch implementation (override for testing)
60
+ * @param cacheStore - Pluggable cache store (default: in-memory LRU, 1024 entries)
61
+ */
62
+ export function createHttpCall(fetchFn = globalThis.fetch, cacheStore = createMemoryCache()) {
63
+ return async (input) => {
64
+ const { baseUrl = "", method = "GET", path = "", headers: inputHeaders = {}, cache: cacheMode = "auto", ...rest } = input;
65
+ // Build URL
66
+ const url = new URL(baseUrl + path);
67
+ // Collect headers
68
+ const headers = {};
69
+ for (const [key, value] of Object.entries(inputHeaders)) {
70
+ if (value != null)
71
+ headers[key] = String(value);
72
+ }
73
+ // GET: shorthand fields → query string
74
+ if (method === "GET") {
75
+ for (const [key, value] of Object.entries(rest)) {
76
+ if (value != null) {
77
+ url.searchParams.set(key, String(value));
78
+ }
79
+ }
80
+ }
81
+ // Non-GET: shorthand fields → JSON body
82
+ let body;
83
+ if (method !== "GET") {
84
+ const bodyObj = {};
85
+ for (const [key, value] of Object.entries(rest)) {
86
+ if (value != null)
87
+ bodyObj[key] = value;
88
+ }
89
+ if (Object.keys(bodyObj).length > 0) {
90
+ body = JSON.stringify(bodyObj);
91
+ headers["Content-Type"] ??= "application/json";
92
+ }
93
+ }
94
+ // cache = 0 → no caching at all
95
+ const mode = String(cacheMode);
96
+ if (mode === "0") {
97
+ const response = await fetchFn(url.toString(), { method, headers, body });
98
+ return response.json();
99
+ }
100
+ const cacheKey = method + " " + url.toString() + (body ?? "");
101
+ // Check cache before fetching
102
+ const cached = await cacheStore.get(cacheKey);
103
+ if (cached !== undefined)
104
+ return cached;
105
+ const response = await fetchFn(url.toString(), { method, headers, body });
106
+ const data = await response.json();
107
+ // Determine TTL
108
+ const ttl = mode === "auto" ? parseCacheTTL(response) : Number(mode);
109
+ if (ttl > 0) {
110
+ await cacheStore.set(cacheKey, data, ttl);
111
+ }
112
+ return data;
113
+ };
114
+ }
115
+ /** Exported for testing. */
116
+ export { parseCacheTTL };
@@ -0,0 +1,43 @@
1
+ import { upperCase } from "./upper-case.js";
2
+ import { lowerCase } from "./lower-case.js";
3
+ import { findObject } from "./find-object.js";
4
+ import { pickFirst } from "./pick-first.js";
5
+ import { toArray } from "./to-array.js";
6
+ export declare const std: {
7
+ readonly httpCall: import("../types.js").ToolCallFn;
8
+ readonly upperCase: typeof upperCase;
9
+ readonly lowerCase: typeof lowerCase;
10
+ readonly findObject: typeof findObject;
11
+ readonly pickFirst: typeof pickFirst;
12
+ readonly toArray: typeof toArray;
13
+ };
14
+ /**
15
+ * Built-in tools bundle.
16
+ *
17
+ * Used as the base for `BridgeOptions.tools`. The `std` namespace is always
18
+ * included; user-provided tools are merged on top.
19
+ *
20
+ * ```ts
21
+ * import { builtinTools } from "@stackables/bridge";
22
+ *
23
+ * bridgeTransform(schema, instructions, {
24
+ * tools: { myCustomTool } // std + httpCall are still available
25
+ * });
26
+ * ```
27
+ */
28
+ export declare const builtinTools: {
29
+ readonly std: {
30
+ readonly httpCall: import("../types.js").ToolCallFn;
31
+ readonly upperCase: typeof upperCase;
32
+ readonly lowerCase: typeof lowerCase;
33
+ readonly findObject: typeof findObject;
34
+ readonly pickFirst: typeof pickFirst;
35
+ readonly toArray: typeof toArray;
36
+ };
37
+ };
38
+ export { createHttpCall } from "./http-call.js";
39
+ export { upperCase } from "./upper-case.js";
40
+ export { lowerCase } from "./lower-case.js";
41
+ export { findObject } from "./find-object.js";
42
+ export { pickFirst } from "./pick-first.js";
43
+ export { toArray } from "./to-array.js";
@@ -0,0 +1,43 @@
1
+ import { createHttpCall } from "./http-call.js";
2
+ import { upperCase } from "./upper-case.js";
3
+ import { lowerCase } from "./lower-case.js";
4
+ import { findObject } from "./find-object.js";
5
+ import { pickFirst } from "./pick-first.js";
6
+ import { toArray } from "./to-array.js";
7
+ /**
8
+ * Standard built-in tools — available under the `std` namespace.
9
+ *
10
+ * Referenced in `.bridge` files as `std.upperCase`, `std.pickFirst`, etc.
11
+ */
12
+ const httpCallFn = createHttpCall();
13
+ export const std = {
14
+ httpCall: httpCallFn,
15
+ upperCase,
16
+ lowerCase,
17
+ findObject,
18
+ pickFirst,
19
+ toArray,
20
+ };
21
+ /**
22
+ * Built-in tools bundle.
23
+ *
24
+ * Used as the base for `BridgeOptions.tools`. The `std` namespace is always
25
+ * included; user-provided tools are merged on top.
26
+ *
27
+ * ```ts
28
+ * import { builtinTools } from "@stackables/bridge";
29
+ *
30
+ * bridgeTransform(schema, instructions, {
31
+ * tools: { myCustomTool } // std + httpCall are still available
32
+ * });
33
+ * ```
34
+ */
35
+ export const builtinTools = {
36
+ std,
37
+ };
38
+ export { createHttpCall } from "./http-call.js";
39
+ export { upperCase } from "./upper-case.js";
40
+ export { lowerCase } from "./lower-case.js";
41
+ export { findObject } from "./find-object.js";
42
+ export { pickFirst } from "./pick-first.js";
43
+ export { toArray } from "./to-array.js";
@@ -0,0 +1,3 @@
1
+ export declare function lowerCase(opts: {
2
+ in: string;
3
+ }): string;
@@ -0,0 +1,3 @@
1
+ export function lowerCase(opts) {
2
+ return opts.in.toLowerCase();
3
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Returns the first element of the array in `opts.in`.
3
+ *
4
+ * By default silently returns `undefined` for empty arrays.
5
+ * Set `opts.strict` to `true` (or the string "true") to throw when
6
+ * the array is empty or contains more than one element.
7
+ */
8
+ export declare function pickFirst(opts: {
9
+ in: any[];
10
+ strict?: boolean | string;
11
+ }): any;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Returns the first element of the array in `opts.in`.
3
+ *
4
+ * By default silently returns `undefined` for empty arrays.
5
+ * Set `opts.strict` to `true` (or the string "true") to throw when
6
+ * the array is empty or contains more than one element.
7
+ */
8
+ export function pickFirst(opts) {
9
+ const arr = opts.in;
10
+ const strict = opts.strict === true || opts.strict === "true";
11
+ if (strict) {
12
+ if (!Array.isArray(arr) || arr.length === 0) {
13
+ throw new Error("pickFirst: expected a non-empty array");
14
+ }
15
+ if (arr.length > 1) {
16
+ throw new Error(`pickFirst: expected exactly one element but got ${arr.length}`);
17
+ }
18
+ }
19
+ return Array.isArray(arr) ? arr[0] : undefined;
20
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Wraps a single value in an array.
3
+ *
4
+ * If `opts.in` is already an array it is returned as-is.
5
+ */
6
+ export declare function toArray(opts: {
7
+ in: any;
8
+ }): any[];
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Wraps a single value in an array.
3
+ *
4
+ * If `opts.in` is already an array it is returned as-is.
5
+ */
6
+ export function toArray(opts) {
7
+ return Array.isArray(opts.in) ? opts.in : [opts.in];
8
+ }
@@ -0,0 +1,3 @@
1
+ export declare function upperCase(opts: {
2
+ in: string;
3
+ }): string;
@@ -0,0 +1,3 @@
1
+ export function upperCase(opts) {
2
+ return opts.in.toUpperCase();
3
+ }
@@ -0,0 +1,218 @@
1
+ /**
2
+ * Structured node reference — identifies a specific data point in the execution graph.
3
+ *
4
+ * Every wire has a "from" and "to", each described by a NodeRef.
5
+ * The trunk (module + type + field + instance) identifies the node,
6
+ * while path drills into its data.
7
+ */
8
+ export type NodeRef = {
9
+ /** Module identifier: "hereapi", "sendgrid", "zillow", or SELF_MODULE */
10
+ module: string;
11
+ /** GraphQL type ("Query" | "Mutation") or "Tools" for tool functions */
12
+ type: string;
13
+ /** Field or function name: "geocode", "search", "centsToUsd" */
14
+ field: string;
15
+ /** Instance number for tool calls (1, 2, ...) */
16
+ instance?: number;
17
+ /** References the current array element in a shadow tree (for per-element mapping) */
18
+ element?: boolean;
19
+ /** Path into the data: ["items", "0", "position", "lat"] */
20
+ path: string[];
21
+ };
22
+ /**
23
+ * A wire connects a data source (from) to a data sink (to).
24
+ * Execution is pull-based: when "to" is demanded, "from" is resolved.
25
+ *
26
+ * Constant wires (`=`) set a fixed value on the target.
27
+ * Pull wires (`<-`) resolve the source at runtime.
28
+ * Pipe wires (`pipe: true`) are generated by the `<- h1|h2|source` shorthand
29
+ * and route data through declared tool handles; the serializer collapses them
30
+ * back to pipe notation.
31
+ */
32
+ export type Wire = {
33
+ from: NodeRef;
34
+ to: NodeRef;
35
+ pipe?: true;
36
+ force?: true;
37
+ fallback?: string;
38
+ } | {
39
+ value: string;
40
+ to: NodeRef;
41
+ };
42
+ /**
43
+ * Bridge definition — wires one GraphQL field to its data sources.
44
+ */
45
+ export type Bridge = {
46
+ kind: "bridge";
47
+ /** GraphQL type: "Query" | "Mutation" */
48
+ type: string;
49
+ /** GraphQL field name */
50
+ field: string;
51
+ /** Declared data sources and their wire handles */
52
+ handles: HandleBinding[];
53
+ /** Connection wires */
54
+ wires: Wire[];
55
+ /**
56
+ * Pipe fork registry — one entry per pipe use.
57
+ * Maps the fork's trunk key to the originating handle name and the base
58
+ * trunk (used by the executor to inherit non-pipe bridge wires).
59
+ */
60
+ pipeHandles?: Array<{
61
+ /** Unique trunk key for this fork: "module:type:field:instance" */
62
+ key: string;
63
+ /** The bridge handle name that was piped, e.g. "pt" or "convertToEur" */
64
+ handle: string;
65
+ /** Base trunk — regular (non-pipe) bridge wires targeting this trunk are
66
+ * applied to every fork before the fork-specific pipe wires. */
67
+ baseTrunk: {
68
+ module: string;
69
+ type: string;
70
+ field: string;
71
+ instance?: number;
72
+ };
73
+ }>;
74
+ };
75
+ /**
76
+ * A handle binding — declares a named data source available in a bridge.
77
+ *
78
+ * Every wire reference in the bridge body must trace back to one of these.
79
+ */
80
+ export type HandleBinding = {
81
+ handle: string;
82
+ kind: "tool";
83
+ name: string;
84
+ } | {
85
+ handle: string;
86
+ kind: "input";
87
+ } | {
88
+ handle: string;
89
+ kind: "context";
90
+ } | {
91
+ handle: string;
92
+ kind: "const";
93
+ };
94
+ /** Internal module identifier for the bridge's own trunk (input args + output fields) */
95
+ export declare const SELF_MODULE = "_";
96
+ /**
97
+ * Tool definition — a declared tool with wires, dependencies, and optional inheritance.
98
+ *
99
+ * Tool blocks define reusable, composable API call configurations:
100
+ * tool hereapi httpCall — root tool with function name
101
+ * tool hereapi.geocode extends hereapi — child inherits parent wires
102
+ *
103
+ * The engine resolves extends chains, merges wires, and calls the
104
+ * registered tool function with the fully-built input object.
105
+ */
106
+ export type ToolDef = {
107
+ kind: "tool";
108
+ /** Tool name: "hereapi", "sendgrid.send", "authService" */
109
+ name: string;
110
+ /** Function name — looked up in the tools map. Omitted when extends is used. */
111
+ fn?: string;
112
+ /** Parent tool name — inherits fn, deps, and wires */
113
+ extends?: string;
114
+ /** Dependencies declared via `with` inside the tool block */
115
+ deps: ToolDep[];
116
+ /** Wires: constants (`=`) and pulls (`<-`) defining the tool's input */
117
+ wires: ToolWire[];
118
+ };
119
+ /**
120
+ * A dependency declared inside a tool block.
121
+ *
122
+ * with context — brings the full GraphQL context into scope
123
+ * with authService as auth — brings another tool's output into scope
124
+ */
125
+ export type ToolDep = {
126
+ kind: "context";
127
+ handle: string;
128
+ } | {
129
+ kind: "tool";
130
+ handle: string;
131
+ tool: string;
132
+ } | {
133
+ kind: "const";
134
+ handle: string;
135
+ };
136
+ /**
137
+ * A wire in a tool block — either a constant value, a pull from a dependency,
138
+ * or an error fallback.
139
+ *
140
+ * Examples:
141
+ * baseUrl = "https://api.sendgrid.com/v3" → constant
142
+ * method = POST → constant (unquoted)
143
+ * headers.Authorization <- ctx.sendgrid.token → pull from context
144
+ * headers.Authorization <- auth.access_token → pull from tool dep
145
+ * on error = { "lat": 0, "lon": 0 } → constant fallback
146
+ * on error <- ctx.fallbacks.geo → pull fallback from context
147
+ */
148
+ export type ToolWire = {
149
+ target: string;
150
+ kind: "constant";
151
+ value: string;
152
+ } | {
153
+ target: string;
154
+ kind: "pull";
155
+ source: string;
156
+ } | {
157
+ kind: "onError";
158
+ value: string;
159
+ } | {
160
+ kind: "onError";
161
+ source: string;
162
+ };
163
+ /**
164
+ * Tool call function — the signature for registered tool functions.
165
+ *
166
+ * Receives a fully-built nested input object and returns the response.
167
+ * The engine builds the input from tool wires + bridge wires.
168
+ *
169
+ * Example (httpCall):
170
+ * input = { baseUrl: "https://...", method: "GET", path: "/geocode",
171
+ * headers: { apiKey: "..." }, q: "Berlin" }
172
+ */
173
+ export type ToolCallFn = (input: Record<string, any>) => Promise<Record<string, any>>;
174
+ /**
175
+ * Recursive tool map — supports namespaced tools via nesting.
176
+ *
177
+ * Example:
178
+ * { std: { upperCase, lowerCase }, httpCall: createHttpCall(), myCompany: { myTool } }
179
+ *
180
+ * Lookup is dot-separated: "std.upperCase" → tools.std.upperCase
181
+ */
182
+ export type ToolMap = {
183
+ [key: string]: ToolCallFn | ((...args: any[]) => any) | ToolMap;
184
+ };
185
+ /**
186
+ * Named constant definition — a reusable value defined in the bridge file.
187
+ *
188
+ * Constants are available in bridge blocks via `with const as c` and in tool
189
+ * blocks via `with const`. The engine collects all ConstDef instructions into
190
+ * a single namespace object keyed by name.
191
+ *
192
+ * Examples:
193
+ * const fallbackGeo = { "lat": 0, "lon": 0 }
194
+ * const defaultCurrency = "EUR"
195
+ */
196
+ export type ConstDef = {
197
+ kind: "const";
198
+ /** Constant name — used as the key in the const namespace */
199
+ name: string;
200
+ /** Raw JSON string — parsed at runtime when accessed */
201
+ value: string;
202
+ };
203
+ /** Union of all instruction types */
204
+ export type Instruction = Bridge | ToolDef | ConstDef;
205
+ /**
206
+ * Pluggable cache store for httpCall.
207
+ *
208
+ * Default: in-memory Map with TTL eviction.
209
+ * Override: pass any key-value store (Redis, Memcached, etc.) to `createHttpCall`.
210
+ *
211
+ * ```ts
212
+ * const httpCall = createHttpCall(fetch, myRedisStore);
213
+ * ```
214
+ */
215
+ export type CacheStore = {
216
+ get(key: string): Promise<any | undefined> | any | undefined;
217
+ set(key: string, value: any, ttlSeconds: number): Promise<void> | void;
218
+ };
package/build/types.js ADDED
@@ -0,0 +1,2 @@
1
+ /** Internal module identifier for the bridge's own trunk (input args + output fields) */
2
+ export const SELF_MODULE = "_";