@fireweaveai/server-sdk-cjs 0.0.0-reserve.0 → 0.0.0-stage

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/README.md +2 -54
  2. package/package.json +4 -56
  3. package/LICENSE +0 -21
  4. package/dist/application/client.d.ts +0 -77
  5. package/dist/application/client.js +0 -144
  6. package/dist/application/mode.d.ts +0 -70
  7. package/dist/application/mode.js +0 -74
  8. package/dist/application/ports.d.ts +0 -94
  9. package/dist/application/ports.js +0 -2
  10. package/dist/application/runtime.d.ts +0 -101
  11. package/dist/application/runtime.js +0 -346
  12. package/dist/domain/context.d.ts +0 -67
  13. package/dist/domain/context.js +0 -90
  14. package/dist/domain/errors.d.ts +0 -45
  15. package/dist/domain/errors.js +0 -99
  16. package/dist/domain/target.d.ts +0 -2
  17. package/dist/domain/target.js +0 -2
  18. package/dist/domain/types.d.ts +0 -107
  19. package/dist/domain/types.js +0 -2
  20. package/dist/domain/validation.d.ts +0 -124
  21. package/dist/domain/validation.js +0 -355
  22. package/dist/index.d.ts +0 -34
  23. package/dist/index.js +0 -52
  24. package/dist/infrastructure/adapters/inmemory.d.ts +0 -77
  25. package/dist/infrastructure/adapters/inmemory.js +0 -151
  26. package/dist/infrastructure/adapters/local.d.ts +0 -108
  27. package/dist/infrastructure/adapters/local.js +0 -105
  28. package/dist/infrastructure/adapters/remote.d.ts +0 -74
  29. package/dist/infrastructure/adapters/remote.js +0 -373
  30. package/dist/infrastructure/hosts.d.ts +0 -18
  31. package/dist/infrastructure/hosts.js +0 -72
  32. package/dist/register-browser.d.ts +0 -1
  33. package/dist/register-browser.js +0 -7
  34. package/dist/register.d.ts +0 -1
  35. package/dist/register.js +0 -13
  36. package/dist/start/browser.d.ts +0 -12
  37. package/dist/start/browser.js +0 -36
  38. package/dist/start/build-info.d.ts +0 -2
  39. package/dist/start/build-info.js +0 -9
  40. package/dist/start/control-points.d.ts +0 -20
  41. package/dist/start/control-points.js +0 -59
  42. package/dist/start/env.d.ts +0 -11
  43. package/dist/start/env.js +0 -64
  44. package/dist/start/fw.d.ts +0 -29
  45. package/dist/start/fw.js +0 -76
  46. package/dist/start/index.d.ts +0 -23
  47. package/dist/start/index.js +0 -28
  48. package/dist/start/instance.d.ts +0 -17
  49. package/dist/start/instance.js +0 -56
  50. package/dist/start/names.d.ts +0 -22
  51. package/dist/start/names.js +0 -25
  52. package/dist/start/resolve.d.ts +0 -40
  53. package/dist/start/resolve.js +0 -139
  54. package/dist/start/state.d.ts +0 -96
  55. package/dist/start/state.js +0 -303
@@ -1,139 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.resolveStart = resolveStart;
4
- /**
5
- * Pure start-profile resolver: options + env + build stamp in, one resolved
6
- * config out. No I/O, no globals, no timers — every rule here is unit-tested
7
- * through `resolveStart` alone.
8
- *
9
- * Precedence for every value: explicit start() option, then FIREWEAVE_* env,
10
- * then the default. Reads are lazy:
11
- * a source is read only if every earlier source was unset, so an explicit
12
- * option never touches the environment (which matters on Deno).
13
- */
14
- const index_js_1 = require("../index.js");
15
- const env_js_1 = require("./env.js");
16
- const control_points_js_1 = require("./control-points.js");
17
- const names_js_1 = require("./names.js");
18
- const configError = (message) => new index_js_1.FireweaveError('Configuration', { message });
19
- const optionString = (value, name) => {
20
- if (value === undefined || value === null)
21
- return undefined;
22
- if (typeof value !== 'string')
23
- throw configError(`[fireweave] start({ ${name} }) must be a string.`);
24
- const trimmed = value.trim();
25
- return trimmed === '' ? undefined : trimmed;
26
- };
27
- /** First non-empty of: the option, then each env name in order. */
28
- function pick(option, optionName, names, read) {
29
- if (option !== undefined)
30
- return { value: option, source: `start({ ${optionName} })` };
31
- for (const name of names) {
32
- const value = read(name);
33
- if (value !== undefined)
34
- return { value, source: name };
35
- }
36
- return undefined;
37
- }
38
- /**
39
- * Key family check, before any request. Messages name the source, never the
40
- * value. The redactor masks analytics-vendor prefixes, so the message says
41
- * "analytics vendor key" instead of quoting one.
42
- */
43
- function checkKeyFamily(key, source) {
44
- if (key.startsWith('fw_public_')) {
45
- throw configError(`[fireweave] The key from ${source} is a browser key (fw_public_…). Server apps need a project key (project-api-key_…) from Project settings, API keys.`);
46
- }
47
- if (/^ph[a-z]_/.test(key)) {
48
- throw configError(`[fireweave] The key from ${source} is an analytics vendor key, not a FireWeave project key. Use the project key (project-api-key_…).`);
49
- }
50
- if (key.startsWith('fw_org_') || key.startsWith('cli_at_')) {
51
- throw configError(`[fireweave] The key from ${source} is an organisation or CLI token, not a project key. Use the project key (project-api-key_…).`);
52
- }
53
- }
54
- function resolveUrl(input, read, build) {
55
- const picked = pick(optionString(input.url, 'url'), 'url', [names_js_1.ENV.url], read);
56
- if (picked === undefined) {
57
- // The channel host is in DEFAULT_ALLOWED_HOSTS, so no custom allowlist.
58
- return { url: names_js_1.CHANNEL_URLS[build.channel], urlSource: `SDK channel (${build.channel})` };
59
- }
60
- const url = picked.value.replace(/\/+$/, '');
61
- let hostname;
62
- try {
63
- hostname = new URL(url).hostname;
64
- }
65
- catch {
66
- throw configError(`[fireweave] The endpoint from ${picked.source} is not a valid URL.`);
67
- }
68
- const allowedHosts = [hostname.replace(/^\[|\]$/g, ''), ...names_js_1.LOOPBACK_HOSTS];
69
- try {
70
- (0, index_js_1.assertHostAllowed)(url, allowedHosts);
71
- }
72
- catch {
73
- throw configError(`[fireweave] The endpoint from ${picked.source} must use https (http is allowed only for localhost).`);
74
- }
75
- return { url, urlSource: picked.source, allowedHosts };
76
- }
77
- function resolveKey(input, read) {
78
- const picked = pick(optionString(input.key, 'key'), 'key', [names_js_1.ENV.key], read);
79
- if (picked !== undefined)
80
- checkKeyFamily(picked.value, picked.source);
81
- return picked;
82
- }
83
- function resolveEnvironment(input, read) {
84
- return pick(optionString(input.environment, 'environment'), 'environment', [names_js_1.ENV.environment, ...names_js_1.ENVIRONMENT_FALLBACKS], read);
85
- }
86
- function noKeyError(env) {
87
- const where = env === undefined
88
- ? 'no environment name is set (checked the environment option, FIREWEAVE_ENV, APP_ENV and NODE_ENV)'
89
- : `the environment is '${env.value}' (from ${env.source}), which is not a development name`;
90
- return configError(`[fireweave] ${names_js_1.ENV.key} is not set and ${where}. Set ${names_js_1.ENV.key} to the project's server key, or for local development set FIREWEAVE_ENV=development or call start({ mode: 'local' }).`);
91
- }
92
- /**
93
- * Resolve start() options against the environment.
94
- * Throws FireweaveError('Configuration') naming the variable at fault.
95
- */
96
- function resolveStart(input, read, build) {
97
- const warnings = [];
98
- const controlPoints = (0, control_points_js_1.normalizeControlPoints)(input.controlPoints);
99
- const base = { controlPoints, channel: build.channel, sdkVersion: build.version };
100
- if (input.mode !== undefined && input.mode !== 'remote' && input.mode !== 'local') {
101
- throw configError(`[fireweave] start({ mode }) must be 'remote' or 'local'.`);
102
- }
103
- if (input.mode === 'local') {
104
- // The key is ignored. Look only to warn, and never fail on a refused read.
105
- const ignored = resolveKeyQuietly(input, read);
106
- if (ignored !== undefined) {
107
- warnings.push(`[fireweave] start({ mode: 'local' }) ignores the key from ${ignored}; nothing is sent to fw-server.`);
108
- }
109
- return { ...base, mode: 'local', modeSource: 'option', keySource: 'none', warnings };
110
- }
111
- const key = resolveKey(input, read);
112
- if (input.mode === 'remote') {
113
- if (key === undefined) {
114
- throw configError(`[fireweave] start({ mode: 'remote' }) needs a key. Set ${names_js_1.ENV.key} or pass start({ key }).`);
115
- }
116
- return { ...base, mode: 'remote', modeSource: 'option', ...resolveUrl(input, read, build), key: key.value, keySource: key.source, warnings };
117
- }
118
- if (key !== undefined) {
119
- return { ...base, mode: 'remote', modeSource: 'key', ...resolveUrl(input, read, build), key: key.value, keySource: key.source, warnings };
120
- }
121
- const env = resolveEnvironment(input, read);
122
- if (env !== undefined && names_js_1.DEV_ENVIRONMENTS.has(env.value.toLowerCase())) {
123
- return {
124
- ...base,
125
- mode: 'local',
126
- modeSource: 'environment',
127
- keySource: 'none',
128
- environment: env.value,
129
- environmentSource: env.source,
130
- warnings,
131
- };
132
- }
133
- throw noKeyError(env);
134
- }
135
- function resolveKeyQuietly(input, read) {
136
- if (typeof input.key === 'string' && input.key.trim() !== '')
137
- return 'start({ key })';
138
- return (0, env_js_1.quiet)(read)(names_js_1.ENV.key) !== undefined ? names_js_1.ENV.key : undefined;
139
- }
@@ -1,96 +0,0 @@
1
- /**
2
- * start() and the process-wide singleton behind `fw`.
3
- *
4
- * One slot per process (or worker thread) at a Symbol.for key on globalThis,
5
- * so two bundled copies of this package share one client. start() is
6
- * synchronous: it resolves and validates everything up front, throws a
7
- * Configuration error for bad config, then hands off to the unchanged core
8
- * initFireweave(). Reads await readiness and never throw.
9
- *
10
- * A read that happens before any start() schedules an env-only start on the
11
- * next macrotask. An ES module graph without top-level await evaluates in one
12
- * job, so an entrypoint that imports ./fireweave/start first always wins.
13
- */
14
- import { FireweaveError, type FireweaveClient, type FireweaveErrorKind, type FireweaveRemoteAdapterOptions } from '../index.js';
15
- import { type EnvReader } from './env.js';
16
- import { type ControlPointMap } from './control-points.js';
17
- import { type InstanceKey } from './instance.js';
18
- import { type ResolvedStart, type SdkChannel, type StartMode } from './resolve.js';
19
- export interface StartOptions {
20
- /** Control points and their local values; import from src/fireweave/control-points.ts. */
21
- readonly controlPoints?: ControlPointMap;
22
- /** Force a mode. Without it: a key means remote; no key means local only in a dev environment. */
23
- readonly mode?: StartMode;
24
- /** Environment name used to infer the mode, instead of FIREWEAVE_ENV, APP_ENV or NODE_ENV. */
25
- readonly environment?: string;
26
- /** fw-server URL. Default: FIREWEAVE_URL, else this SDK build's channel host. */
27
- readonly url?: string;
28
- /** Project key (project-api-key_…). Default: FIREWEAVE_KEY. */
29
- readonly key?: string;
30
- /** Stable id for this process in server-subject reads. Default: FIREWEAVE_INSTANCE_ID, else the host. */
31
- readonly instanceId?: string;
32
- /** Read these values instead of the process environment (tests, Workers bindings). */
33
- readonly env?: Readonly<Record<string, unknown>>;
34
- /** Where [fireweave] lines go. Default: console. Not part of the idempotency check. */
35
- readonly log?: (line: string) => void;
36
- /** Transport override for tests. Not part of the idempotency check. */
37
- readonly fetch?: FireweaveRemoteAdapterOptions['fetch'];
38
- }
39
- export type StartState = 'unstarted' | 'starting' | 'ready' | 'failed' | 'shutdown';
40
- export interface FireweaveStatus {
41
- readonly state: StartState;
42
- readonly mode?: StartMode;
43
- readonly modeSource?: ResolvedStart['modeSource'];
44
- readonly channel: SdkChannel;
45
- readonly sdkVersion: string;
46
- /** fw-server host name only: never a path or a credential. */
47
- readonly host?: string;
48
- readonly endpointSource?: string;
49
- readonly keySource?: string;
50
- readonly environment?: string;
51
- readonly controlPointCount?: number;
52
- /** Why start failed, when it did. Already redacted. */
53
- readonly error?: string;
54
- /**
55
- * The kind of the latest failed fw-server request (Authentication,
56
- * Authorization, RateLimited, Network, Timeout or BackendUnavailable).
57
- * Sticky: a later success does not clear it; a fresh start() does.
58
- */
59
- readonly lastErrorKind?: FireweaveErrorKind;
60
- }
61
- interface Deferred<T> {
62
- readonly promise: Promise<T>;
63
- readonly resolve: (value: T | PromiseLike<T>) => void;
64
- }
65
- interface Slot {
66
- readonly protocol: 1;
67
- state: StartState;
68
- signature?: string;
69
- implicit: boolean;
70
- resolved?: ResolvedStart;
71
- ready: Deferred<FireweaveClient | undefined>;
72
- client?: FireweaveClient | undefined;
73
- error?: FireweaveError;
74
- implicitScheduled: boolean;
75
- read?: EnvReader;
76
- instanceIdOption?: string;
77
- instance?: InstanceKey;
78
- log: (line: string) => void;
79
- readonly warned: Set<string>;
80
- lastErrorKind?: FireweaveErrorKind;
81
- /** fw-server failure groups already logged; one line each for the life of the process. */
82
- readonly diagnosed: Set<string>;
83
- }
84
- export declare function slot(): Slot;
85
- export declare function warnOnce(s: Slot, line: string): void;
86
- /** Start FireWeave for this process. Synchronous; call once, from src/fireweave/start.ts. */
87
- export declare function start(options?: StartOptions): void;
88
- export declare function startWith(options: StartOptions, implicit: boolean): void;
89
- /** The client once start() settles; undefined when start failed. Schedules an implicit start if none ran. */
90
- export declare function ensureClient(): Promise<FireweaveClient | undefined>;
91
- export declare function currentStatus(): FireweaveStatus;
92
- export declare function instanceKeyFor(): string;
93
- export declare function shutdownSlot(): Promise<void>;
94
- /** Test only: shut down and forget the singleton so the next start() begins fresh. */
95
- export declare function resetForTests(): Promise<void>;
96
- export {};
@@ -1,303 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.slot = slot;
4
- exports.warnOnce = warnOnce;
5
- exports.start = start;
6
- exports.startWith = startWith;
7
- exports.ensureClient = ensureClient;
8
- exports.currentStatus = currentStatus;
9
- exports.instanceKeyFor = instanceKeyFor;
10
- exports.shutdownSlot = shutdownSlot;
11
- exports.resetForTests = resetForTests;
12
- /**
13
- * start() and the process-wide singleton behind `fw`.
14
- *
15
- * One slot per process (or worker thread) at a Symbol.for key on globalThis,
16
- * so two bundled copies of this package share one client. start() is
17
- * synchronous: it resolves and validates everything up front, throws a
18
- * Configuration error for bad config, then hands off to the unchanged core
19
- * initFireweave(). Reads await readiness and never throw.
20
- *
21
- * A read that happens before any start() schedules an env-only start on the
22
- * next macrotask. An ES module graph without top-level await evaluates in one
23
- * job, so an entrypoint that imports ./fireweave/start first always wins.
24
- */
25
- const index_js_1 = require("../index.js");
26
- const build_info_js_1 = require("./build-info.js");
27
- const env_js_1 = require("./env.js");
28
- const control_points_js_1 = require("./control-points.js");
29
- const instance_js_1 = require("./instance.js");
30
- const resolve_js_1 = require("./resolve.js");
31
- const deferred = () => {
32
- let resolve;
33
- const promise = new Promise((r) => {
34
- resolve = r;
35
- });
36
- return { promise, resolve };
37
- };
38
- const SLOT_KEY = Symbol.for('@fireweaveai/server-sdk/start');
39
- const defaultLog = (line) => {
40
- if (line.startsWith('[fireweave:local]'))
41
- console.info(line);
42
- else
43
- console.warn(line);
44
- };
45
- const freshSlot = () => ({
46
- protocol: 1,
47
- state: 'unstarted',
48
- implicit: false,
49
- ready: deferred(),
50
- implicitScheduled: false,
51
- log: defaultLog,
52
- warned: new Set(),
53
- diagnosed: new Set(),
54
- });
55
- function slot() {
56
- const g = globalThis;
57
- const existing = g[SLOT_KEY];
58
- if (existing !== undefined) {
59
- if (existing.protocol !== 1) {
60
- throw new index_js_1.FireweaveError('Configuration', {
61
- message: '[fireweave] Two incompatible copies of @fireweaveai/server-sdk are loaded. Deduplicate the dependency.',
62
- });
63
- }
64
- return existing;
65
- }
66
- const created = freshSlot();
67
- g[SLOT_KEY] = created;
68
- return created;
69
- }
70
- function warnOnce(s, line) {
71
- if (s.warned.has(line))
72
- return;
73
- s.warned.add(line);
74
- s.log(line);
75
- }
76
- const signatureOf = (r, instanceId) => (0, index_js_1.stableStringify)({
77
- mode: r.mode,
78
- url: r.url ?? null,
79
- key: r.key ?? null,
80
- allowedHosts: r.allowedHosts !== undefined ? [...r.allowedHosts] : null,
81
- instanceId: instanceId ?? null,
82
- // Seeds only matter in local mode; remote ignores them, so an env-only
83
- // implicit start followed by start({ controlPoints }) under a key is not a conflict.
84
- ...(r.mode === 'local' ? { controlPoints: (0, control_points_js_1.toLocalControlPoints)(r.controlPoints) } : {}),
85
- });
86
- function localLine(r) {
87
- const why = r.modeSource === 'option'
88
- ? "start({ mode: 'local' })"
89
- : `no FIREWEAVE_KEY; environment '${r.environment ?? ''}' from ${r.environmentSource ?? 'env'}`;
90
- const n = Object.keys(r.controlPoints).length;
91
- return `[fireweave:local] Local mode (${why}). Serving ${n} control point${n === 1 ? '' : 's'} from your control-points object; nothing is sent to fw-server.`;
92
- }
93
- /** The kind the core's remote adapter reports for an HTTP status, or undefined for a success. */
94
- function kindForStatus(status) {
95
- if (status < 400)
96
- return undefined;
97
- if (status === 401)
98
- return 'Authentication';
99
- if (status === 403)
100
- return 'Authorization';
101
- if (status === 429)
102
- return 'RateLimited';
103
- return 'BackendUnavailable';
104
- }
105
- /**
106
- * SP-27: a refused key, a rate limit or an unreachable fw-server is logged
107
- * once per group for the life of the process and kept as lastErrorKind, so a
108
- * revoked key does not look like a rollout at 0%. Lines name the key's source
109
- * and the host, never the key.
110
- */
111
- function observeFailure(s, token, r, kind, detail) {
112
- if (s.ready !== token)
113
- return; // a response for a client that was shut down or replaced
114
- s.lastErrorKind = kind;
115
- const host = r.url !== undefined ? new URL(r.url).host : 'fw-server';
116
- let group;
117
- let line;
118
- switch (kind) {
119
- case 'Authentication':
120
- group = 'key-rejected-401';
121
- line = `[fireweave] fw-server at ${host} rejected the key from ${r.keySource} (HTTP 401): it is wrong, revoked or from another project. Reads serve their defaults; this is not a rollout at 0%.`;
122
- break;
123
- case 'Authorization':
124
- group = 'key-rejected-403';
125
- line = `[fireweave] fw-server at ${host} refused the key from ${r.keySource} for this project or environment (HTTP 403). Reads serve their defaults; this is not a rollout at 0%.`;
126
- break;
127
- case 'RateLimited':
128
- group = 'rate-limited';
129
- line = `[fireweave] fw-server at ${host} rate-limited the key from ${r.keySource} (HTTP 429). Reads serve their defaults until a request succeeds.`;
130
- break;
131
- default:
132
- group = 'unreachable';
133
- line = `[fireweave] Could not reach fw-server at ${host} (endpoint from ${r.urlSource ?? 'the SDK channel'}; ${detail}): offline, a firewall, or the wrong endpoint. Reads serve their defaults until a request succeeds.`;
134
- }
135
- if (s.diagnosed.has(group))
136
- return;
137
- s.diagnosed.add(group);
138
- try {
139
- s.log(line);
140
- }
141
- catch {
142
- // A faulty log sink must not turn into a transport failure.
143
- }
144
- }
145
- /** The transport handed to the core, observed. The core's behaviour is unchanged. */
146
- function observedTransport(s, token, r, base) {
147
- return async (url, init) => {
148
- const call = base ?? globalThis.fetch;
149
- let response;
150
- try {
151
- response = await call(url, init);
152
- }
153
- catch (err) {
154
- // The same split the core makes: its own deadline aborts the request.
155
- const timedOut = err instanceof Error && err.name === 'AbortError';
156
- observeFailure(s, token, r, timedOut ? 'Timeout' : 'Network', timedOut ? 'timed out' : 'network error');
157
- throw err;
158
- }
159
- const kind = kindForStatus(response.status);
160
- if (kind !== undefined)
161
- observeFailure(s, token, r, kind, `HTTP ${response.status}`);
162
- return response;
163
- };
164
- }
165
- function initOptions(r, options, s, token) {
166
- if (r.mode === 'local') {
167
- return { mode: 'local', local: { controlPoints: (0, control_points_js_1.toLocalControlPoints)(r.controlPoints), log: (line) => s.log(line) } };
168
- }
169
- return {
170
- mode: 'remote',
171
- apiKey: r.key,
172
- apiUrl: r.url,
173
- ...(r.allowedHosts !== undefined ? { allowedHosts: r.allowedHosts } : {}),
174
- fetch: observedTransport(s, token, r, options.fetch),
175
- };
176
- }
177
- /** Start FireWeave for this process. Synchronous; call once, from src/fireweave/start.ts. */
178
- function start(options = {}) {
179
- startWith(options, false);
180
- }
181
- function startWith(options, implicit) {
182
- const s = slot();
183
- if (s.state === 'shutdown' || s.state === 'failed') {
184
- // Warnings and fw-server failure lines are once per process, across restarts.
185
- Object.assign(s, freshSlot(), { warned: s.warned, diagnosed: s.diagnosed });
186
- delete s.lastErrorKind; // freshSlot() has no such key, so assign alone would keep it
187
- }
188
- const read = options.env !== undefined ? (0, env_js_1.envFromBag)(options.env) : (0, env_js_1.processEnv)();
189
- const resolved = (0, resolve_js_1.resolveStart)(options, read, { version: build_info_js_1.SDK_VERSION, channel: build_info_js_1.SDK_CHANNEL });
190
- const signature = signatureOf(resolved, options.instanceId);
191
- if (s.state === 'starting' || s.state === 'ready') {
192
- if (signature === s.signature)
193
- return;
194
- throw new index_js_1.FireweaveError('Configuration', {
195
- message: s.implicit
196
- ? "[fireweave] A control point was read before start() ran, so FireWeave started from the environment alone. Import './fireweave/start' as the first import of your entrypoint."
197
- : '[fireweave] start() was already called with a different configuration. Call start() once, from src/fireweave/start.ts.',
198
- });
199
- }
200
- if (options.instanceId !== undefined && s.instance !== undefined && s.instance.value !== options.instanceId) {
201
- throw new index_js_1.FireweaveError('Configuration', {
202
- message: '[fireweave] start({ instanceId }) differs from the instanceKey() already handed out. Pass instanceId on the first start().',
203
- });
204
- }
205
- // Only a start that actually begins sets the log sink: an identical second
206
- // start() is a no-op and a conflicting one throws, and neither may swap it.
207
- if (!implicit && options.log !== undefined)
208
- s.log = options.log;
209
- s.state = 'starting';
210
- s.signature = signature;
211
- s.implicit = implicit;
212
- s.resolved = resolved;
213
- s.read = read;
214
- if (options.instanceId !== undefined)
215
- s.instanceIdOption = options.instanceId;
216
- for (const w of resolved.warnings)
217
- warnOnce(s, w);
218
- if (resolved.mode === 'local')
219
- s.log(localLine(resolved));
220
- const ready = s.ready;
221
- const pending = (0, index_js_1.initFireweave)(initOptions(resolved, options, s, ready)).then((client) => {
222
- if (s.ready !== ready)
223
- return undefined; // superseded by shutdown/restart
224
- s.state = 'ready';
225
- s.client = client;
226
- return client;
227
- }, (err) => {
228
- if (s.ready !== ready)
229
- return undefined;
230
- s.state = 'failed';
231
- s.error = (0, index_js_1.isFireweaveError)(err) ? err : new index_js_1.FireweaveError('Internal', { cause: err });
232
- warnOnce(s, `[fireweave] start failed: ${s.error.message}. Reads serve their defaults.`);
233
- return undefined;
234
- });
235
- ready.resolve(pending);
236
- }
237
- /** The client once start() settles; undefined when start failed. Schedules an implicit start if none ran. */
238
- function ensureClient() {
239
- const s = slot();
240
- if (s.state === 'unstarted' && !s.implicitScheduled) {
241
- s.implicitScheduled = true;
242
- setTimeout(() => {
243
- if (s.state !== 'unstarted')
244
- return;
245
- try {
246
- startWith({}, true);
247
- }
248
- catch (err) {
249
- s.state = 'failed';
250
- s.error = (0, index_js_1.isFireweaveError)(err) ? err : new index_js_1.FireweaveError('Internal', { cause: err });
251
- warnOnce(s, `${s.error.message} (FireWeave was not started; reads serve their defaults.)`);
252
- s.ready.resolve(undefined);
253
- }
254
- }, 0);
255
- }
256
- return s.ready.promise;
257
- }
258
- function currentStatus() {
259
- const s = slot();
260
- const r = s.resolved;
261
- return {
262
- state: s.state,
263
- channel: r?.channel ?? build_info_js_1.SDK_CHANNEL,
264
- sdkVersion: r?.sdkVersion ?? build_info_js_1.SDK_VERSION,
265
- ...(r !== undefined
266
- ? {
267
- mode: r.mode,
268
- modeSource: r.modeSource,
269
- keySource: r.keySource,
270
- controlPointCount: Object.keys(r.controlPoints).length,
271
- ...(r.url !== undefined ? { host: new URL(r.url).hostname, endpointSource: r.urlSource } : {}),
272
- ...(r.environment !== undefined ? { environment: r.environment } : {}),
273
- }
274
- : {}),
275
- ...(s.error !== undefined ? { error: s.error.message } : {}),
276
- ...(s.lastErrorKind !== undefined ? { lastErrorKind: s.lastErrorKind } : {}),
277
- };
278
- }
279
- function instanceKeyFor() {
280
- const s = slot();
281
- if (s.instance === undefined)
282
- s.instance = (0, instance_js_1.deriveInstanceKey)(s.instanceIdOption, s.read ?? (0, env_js_1.processEnv)());
283
- return s.instance.value;
284
- }
285
- async function shutdownSlot() {
286
- const s = slot();
287
- const client = s.state === 'starting' ? await s.ready.promise : s.client;
288
- s.state = 'shutdown';
289
- s.client = undefined;
290
- s.ready = deferred();
291
- s.ready.resolve(undefined);
292
- s.implicitScheduled = true; // no implicit restart after an explicit shutdown
293
- if (client !== undefined)
294
- await client.shutdown();
295
- }
296
- /** Test only: shut down and forget the singleton so the next start() begins fresh. */
297
- async function resetForTests() {
298
- const g = globalThis;
299
- const s = g[SLOT_KEY];
300
- if (s?.client !== undefined)
301
- await s.client.shutdown().catch(() => undefined);
302
- delete g[SLOT_KEY];
303
- }