@fireweaveai/server-sdk-cjs 0.0.0-reserve.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 +21 -0
  2. package/README.md +55 -0
  3. package/dist/application/client.d.ts +77 -0
  4. package/dist/application/client.js +144 -0
  5. package/dist/application/mode.d.ts +70 -0
  6. package/dist/application/mode.js +74 -0
  7. package/dist/application/ports.d.ts +94 -0
  8. package/dist/application/ports.js +2 -0
  9. package/dist/application/runtime.d.ts +101 -0
  10. package/dist/application/runtime.js +346 -0
  11. package/dist/domain/context.d.ts +67 -0
  12. package/dist/domain/context.js +90 -0
  13. package/dist/domain/errors.d.ts +45 -0
  14. package/dist/domain/errors.js +99 -0
  15. package/dist/domain/target.d.ts +2 -0
  16. package/dist/domain/target.js +2 -0
  17. package/dist/domain/types.d.ts +107 -0
  18. package/dist/domain/types.js +2 -0
  19. package/dist/domain/validation.d.ts +124 -0
  20. package/dist/domain/validation.js +355 -0
  21. package/dist/index.d.ts +34 -0
  22. package/dist/index.js +52 -0
  23. package/dist/infrastructure/adapters/inmemory.d.ts +77 -0
  24. package/dist/infrastructure/adapters/inmemory.js +151 -0
  25. package/dist/infrastructure/adapters/local.d.ts +108 -0
  26. package/dist/infrastructure/adapters/local.js +105 -0
  27. package/dist/infrastructure/adapters/remote.d.ts +74 -0
  28. package/dist/infrastructure/adapters/remote.js +373 -0
  29. package/dist/infrastructure/hosts.d.ts +18 -0
  30. package/dist/infrastructure/hosts.js +72 -0
  31. package/dist/register-browser.d.ts +1 -0
  32. package/dist/register-browser.js +7 -0
  33. package/dist/register.d.ts +1 -0
  34. package/dist/register.js +13 -0
  35. package/dist/start/browser.d.ts +12 -0
  36. package/dist/start/browser.js +36 -0
  37. package/dist/start/build-info.d.ts +2 -0
  38. package/dist/start/build-info.js +9 -0
  39. package/dist/start/control-points.d.ts +20 -0
  40. package/dist/start/control-points.js +59 -0
  41. package/dist/start/env.d.ts +11 -0
  42. package/dist/start/env.js +64 -0
  43. package/dist/start/fw.d.ts +29 -0
  44. package/dist/start/fw.js +76 -0
  45. package/dist/start/index.d.ts +23 -0
  46. package/dist/start/index.js +28 -0
  47. package/dist/start/instance.d.ts +17 -0
  48. package/dist/start/instance.js +56 -0
  49. package/dist/start/names.d.ts +22 -0
  50. package/dist/start/names.js +25 -0
  51. package/dist/start/resolve.d.ts +40 -0
  52. package/dist/start/resolve.js +139 -0
  53. package/dist/start/state.d.ts +96 -0
  54. package/dist/start/state.js +303 -0
  55. package/package.json +58 -0
@@ -0,0 +1,373 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FireweaveRemoteAdapter = void 0;
4
+ /**
5
+ * Fireweave remote backend adapter (ADR-0005) — **default production path**.
6
+ *
7
+ * Speaks only the vendor-neutral Fireweave remote protocol to fw-server:
8
+ * POST /v1/control-points/evaluate
9
+ * POST /v1/capture
10
+ * POST /v1/targets/register
11
+ *
12
+ * Auth: Authorization: Bearer <project key> (project-api-key_…).
13
+ * Which backend fw-server forwards to is fw-server's concern: no vendor SDK,
14
+ * key, or host ever enters the application process.
15
+ *
16
+ * See spec/remote-protocol.md.
17
+ */
18
+ const errors_js_1 = require("../../domain/errors.js");
19
+ const hosts_js_1 = require("../hosts.js");
20
+ const validation_js_1 = require("../../domain/validation.js");
21
+ const DEFAULT_ADAPTER_SHUTDOWN_TIMEOUT_MS = 10000;
22
+ const DEFAULT_REQUEST_TIMEOUT_MS = 3000;
23
+ const EVALUATE_PATH = '/v1/control-points/evaluate';
24
+ const CAPTURE_PATH = '/v1/capture';
25
+ const REGISTER_TARGET_PATH = '/v1/targets/register';
26
+ function mapHttpStatus(status) {
27
+ if (status === 401)
28
+ return new errors_js_1.FireweaveError('Authentication');
29
+ if (status === 403)
30
+ return new errors_js_1.FireweaveError('Authorization');
31
+ if (status === 429)
32
+ return new errors_js_1.FireweaveError('RateLimited');
33
+ if (status >= 500)
34
+ return new errors_js_1.FireweaveError('BackendUnavailable');
35
+ return new errors_js_1.FireweaveError('BackendUnavailable');
36
+ }
37
+ /**
38
+ * `apiUrl`/`apiKey` come from `options` only — never the environment
39
+ * (spec/modes.md "The SDK reads no environment variables. Credentials
40
+ * arrive as explicit options.", unscoped).
41
+ */
42
+ function resolveCredentials(options) {
43
+ const apiUrl = (options.apiUrl ?? '').replace(/\/+$/, '');
44
+ const apiKey = options.apiKey ?? '';
45
+ return { apiUrl, apiKey };
46
+ }
47
+ function defaultAllowedHostsFor(apiUrl) {
48
+ try {
49
+ const hostname = new URL(apiUrl).hostname;
50
+ const hosts = [hostname, 'localhost', '127.0.0.1', '::1'];
51
+ if (hostname === '[::1]')
52
+ hosts.push('::1');
53
+ return hosts;
54
+ }
55
+ catch {
56
+ return ['localhost', '127.0.0.1', '::1'];
57
+ }
58
+ }
59
+ function reasonToAdapter(reason) {
60
+ const allowed = new Set([
61
+ 'STATIC',
62
+ 'DEFAULT',
63
+ 'TARGETING_MATCH',
64
+ 'SPLIT',
65
+ 'CACHED',
66
+ 'STALE',
67
+ 'DISABLED',
68
+ 'UNKNOWN',
69
+ 'ERROR',
70
+ ]);
71
+ return allowed.has(reason) ? reason : undefined;
72
+ }
73
+ class FireweaveRemoteAdapter {
74
+ name = 'fireweave';
75
+ options;
76
+ apiUrl = '';
77
+ apiKey = '';
78
+ closed = false;
79
+ ready = false;
80
+ pending = [];
81
+ fetchImpl;
82
+ constructor(options = {}) {
83
+ this.options = { ...options };
84
+ this.fetchImpl = options.fetch ?? globalThis.fetch;
85
+ }
86
+ async initialize(_signal) {
87
+ if (this.closed)
88
+ throw new errors_js_1.FireweaveError('AlreadyClosed');
89
+ const { apiUrl, apiKey } = resolveCredentials(this.options);
90
+ if (!apiUrl || !apiKey) {
91
+ throw new errors_js_1.FireweaveError('Configuration');
92
+ }
93
+ let url;
94
+ try {
95
+ url = new URL(apiUrl);
96
+ }
97
+ catch {
98
+ throw new errors_js_1.FireweaveError('Configuration');
99
+ }
100
+ if (url.protocol !== 'http:' && url.protocol !== 'https:') {
101
+ throw new errors_js_1.FireweaveError('Configuration');
102
+ }
103
+ if (url.protocol === 'http:' && !(0, hosts_js_1.isLoopbackHostname)(url.hostname)) {
104
+ throw new errors_js_1.FireweaveError('Configuration');
105
+ }
106
+ const allow = this.options.allowedHosts !== undefined && this.options.allowedHosts.length > 0
107
+ ? this.options.allowedHosts
108
+ : defaultAllowedHostsFor(apiUrl);
109
+ (0, hosts_js_1.assertHostAllowed)(apiUrl, allow);
110
+ this.apiUrl = apiUrl.replace(/\/+$/, '');
111
+ this.apiKey = apiKey;
112
+ this.ready = true;
113
+ }
114
+ async resolve(controlPointKey, context, options) {
115
+ if (this.closed)
116
+ throw new errors_js_1.FireweaveError('AlreadyClosed');
117
+ if (!this.ready)
118
+ throw new errors_js_1.FireweaveError('NotReady');
119
+ const targetingKeyResult = (0, validation_js_1.validateTargetingKey)(context.targetingKey, true);
120
+ if (!targetingKeyResult.ok)
121
+ throw targetingKeyResult.error;
122
+ const targetingKey = targetingKeyResult.value ?? '';
123
+ const attributes = {};
124
+ for (const [k, v] of Object.entries(context.attributes)) {
125
+ if (k === 'groups' || k === 'groupProperties' || k.startsWith('$') || k.startsWith('fireweave.')) {
126
+ continue;
127
+ }
128
+ attributes[k] = v;
129
+ }
130
+ const body = {
131
+ targetingKey,
132
+ controlPointKeys: [controlPointKey],
133
+ };
134
+ if (Object.keys(attributes).length > 0)
135
+ body['attributes'] = attributes;
136
+ if (context.groups !== undefined)
137
+ body['groups'] = context.groups;
138
+ if (context.groupProperties !== undefined)
139
+ body['groupProperties'] = context.groupProperties;
140
+ const response = await this.requestJson(EVALUATE_PATH, body, options?.signal);
141
+ const item = response.decisions?.find((d) => d.controlPointKey === controlPointKey);
142
+ if (item === undefined) {
143
+ const missing = { found: false };
144
+ if (response.quotaLimited === true)
145
+ missing.quotaLimited = true;
146
+ return missing;
147
+ }
148
+ return this.toResolution(item, response.quotaLimited === true);
149
+ }
150
+ /**
151
+ * Register a user or device so flag rules can target its DURABLE properties.
152
+ *
153
+ * Call once per login / device provisioning, then send the same
154
+ * `targetingKey` on evaluate. Per-request `attributes` still override the
155
+ * stored properties for a single evaluation — the two identity paths compose
156
+ * (see spec/remote-protocol.md § Two identity paths).
157
+ *
158
+ * Never throws for transport failures: registration sits in login paths, and
159
+ * an analytics call must not break sign-in. Retried ONCE when the error
160
+ * taxonomy marks the failure retryable (network / timeout / backend); a
161
+ * rejected payload or bad key is not retried, since it would be rejected
162
+ * identically. The result object reports what happened for callers that
163
+ * want to log it.
164
+ */
165
+ async registerTarget(targetingKey, options = {}) {
166
+ if (this.closed)
167
+ return { ok: false, error: new errors_js_1.FireweaveError('AlreadyClosed') };
168
+ if (!this.ready)
169
+ return { ok: false, error: new errors_js_1.FireweaveError('NotReady') };
170
+ const targetingKeyResult = (0, validation_js_1.validateTargetingKey)(targetingKey, true);
171
+ if (!targetingKeyResult.ok)
172
+ return { ok: false, error: targetingKeyResult.error };
173
+ const body = { targetingKey };
174
+ if (options.kind !== undefined)
175
+ body['kind'] = options.kind;
176
+ if (options.environment !== undefined)
177
+ body['environment'] = options.environment;
178
+ if (options.properties !== undefined && Object.keys(options.properties).length > 0) {
179
+ body['properties'] = options.properties;
180
+ }
181
+ let lastError;
182
+ for (let attempt = 0; attempt < 2; attempt += 1) {
183
+ try {
184
+ await this.requestJson(REGISTER_TARGET_PATH, body, options.signal);
185
+ return { ok: true };
186
+ }
187
+ catch (err) {
188
+ lastError =
189
+ err instanceof errors_js_1.FireweaveError ? err : new errors_js_1.FireweaveError('BackendUnavailable');
190
+ if (!lastError.retryable)
191
+ break;
192
+ }
193
+ }
194
+ return { ok: false, ...(lastError !== undefined ? { error: lastError } : {}) };
195
+ }
196
+ recordExposure(exposure) {
197
+ if (this.closed || !this.ready)
198
+ return;
199
+ const event = {
200
+ type: 'exposure',
201
+ targetingKey: exposure.targetingKey,
202
+ controlPointKey: exposure.controlPointKey,
203
+ value: exposure.value,
204
+ };
205
+ if (exposure.variant !== undefined)
206
+ event.variant = exposure.variant;
207
+ if (exposure.rolloutId !== undefined)
208
+ event.rolloutId = exposure.rolloutId;
209
+ if (exposure.changeId !== undefined)
210
+ event.changeId = exposure.changeId;
211
+ if (exposure.stampId !== undefined)
212
+ event.stampId = exposure.stampId;
213
+ this.pending.push(event);
214
+ }
215
+ recordSignal(signal) {
216
+ if (this.closed || !this.ready)
217
+ return;
218
+ const event = {
219
+ type: 'signal',
220
+ targetingKey: signal.targetingKey ?? signal.rolloutId ?? 'fireweave-sdk',
221
+ name: signal.name,
222
+ properties: {
223
+ kind: signal.kind,
224
+ ...(signal.status !== undefined ? { status: signal.status } : {}),
225
+ ...(signal.errorKind !== undefined ? { errorKind: signal.errorKind } : {}),
226
+ ...(signal.message !== undefined ? { message: signal.message } : {}),
227
+ ...(signal.value !== undefined ? { value: signal.value } : {}),
228
+ },
229
+ };
230
+ if (signal.controlPointKey !== undefined)
231
+ event.controlPointKey = signal.controlPointKey;
232
+ if (signal.variant !== undefined)
233
+ event.variant = signal.variant;
234
+ if (signal.rolloutId !== undefined)
235
+ event.rolloutId = signal.rolloutId;
236
+ if (signal.changeId !== undefined)
237
+ event.changeId = signal.changeId;
238
+ if (signal.stampId !== undefined)
239
+ event.stampId = signal.stampId;
240
+ if (signal.timestamp !== undefined)
241
+ event.timestamp = signal.timestamp;
242
+ this.pending.push(event);
243
+ }
244
+ async flush() {
245
+ if (this.closed || !this.ready || this.pending.length === 0)
246
+ return;
247
+ const batch = this.pending.splice(0, this.pending.length);
248
+ try {
249
+ await this.requestJson(CAPTURE_PATH, { events: batch });
250
+ }
251
+ catch {
252
+ // flush failures must not throw into shutdown paths; re-queue best-effort
253
+ this.pending.unshift(...batch);
254
+ }
255
+ }
256
+ async shutdown() {
257
+ if (this.closed)
258
+ return;
259
+ this.closed = true;
260
+ // The race timer is cleared once the flush wins; left armed, it held the
261
+ // process open for up to shutdownTimeoutMs after a clean shutdown.
262
+ let timer;
263
+ try {
264
+ const timeout = this.options.shutdownTimeoutMs ?? DEFAULT_ADAPTER_SHUTDOWN_TIMEOUT_MS;
265
+ await Promise.race([
266
+ this.flush(),
267
+ new Promise((resolve) => {
268
+ timer = setTimeout(resolve, timeout);
269
+ }),
270
+ ]);
271
+ }
272
+ catch {
273
+ // never throw from shutdown
274
+ }
275
+ finally {
276
+ if (timer !== undefined)
277
+ clearTimeout(timer);
278
+ }
279
+ this.ready = false;
280
+ }
281
+ features() {
282
+ return {
283
+ remoteEvaluation: true,
284
+ localEvaluation: false,
285
+ localOnly: false,
286
+ exposureEmission: true,
287
+ sideEffectFreeReads: true,
288
+ groupAnalytics: true,
289
+ };
290
+ }
291
+ toResolution(item, quotaLimited) {
292
+ if (item.found === false) {
293
+ const missing = { found: false };
294
+ if (quotaLimited)
295
+ missing.quotaLimited = true;
296
+ return missing;
297
+ }
298
+ const resolution = {
299
+ found: true,
300
+ value: item.value,
301
+ enabled: item.enabled ?? true,
302
+ };
303
+ if (item.variant != null)
304
+ resolution.variant = item.variant;
305
+ const reason = reasonToAdapter(item.reason);
306
+ if (reason !== undefined)
307
+ resolution.reason = reason;
308
+ if (item.payload !== undefined)
309
+ resolution.payload = item.payload;
310
+ const meta = item.controlPointMetadata ?? {};
311
+ if (typeof meta['fireweave.controlPointVersion'] === 'number') {
312
+ resolution.version = meta['fireweave.controlPointVersion'];
313
+ }
314
+ if (typeof meta['fireweave.vendorControlPointId'] === 'number') {
315
+ resolution.vendorControlPointId = meta['fireweave.vendorControlPointId'];
316
+ }
317
+ if (typeof meta['fireweave.reasonCode'] === 'string') {
318
+ resolution.reasonCode = meta['fireweave.reasonCode'];
319
+ }
320
+ if (quotaLimited || meta['fireweave.quotaLimited'] === true) {
321
+ resolution.quotaLimited = true;
322
+ }
323
+ return resolution;
324
+ }
325
+ async requestJson(path, body, outerSignal) {
326
+ const timeoutMs = this.options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
327
+ const controller = new AbortController();
328
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
329
+ const onOuterAbort = () => controller.abort();
330
+ if (outerSignal !== undefined) {
331
+ if (outerSignal.aborted)
332
+ controller.abort();
333
+ else
334
+ outerSignal.addEventListener('abort', onOuterAbort, { once: true });
335
+ }
336
+ try {
337
+ let response;
338
+ try {
339
+ response = await this.fetchImpl(`${this.apiUrl}${path}`, {
340
+ method: 'POST',
341
+ headers: {
342
+ 'content-type': 'application/json',
343
+ authorization: `Bearer ${this.apiKey}`,
344
+ },
345
+ body: JSON.stringify(body),
346
+ signal: controller.signal,
347
+ });
348
+ }
349
+ catch (err) {
350
+ const aborted = err instanceof Error && err.name === 'AbortError';
351
+ throw aborted ? new errors_js_1.FireweaveError('Timeout') : new errors_js_1.FireweaveError('Network');
352
+ }
353
+ if (response.status >= 400) {
354
+ throw mapHttpStatus(response.status);
355
+ }
356
+ let parsed;
357
+ try {
358
+ parsed = await response.json();
359
+ }
360
+ catch {
361
+ throw new errors_js_1.FireweaveError('MalformedResponse');
362
+ }
363
+ return parsed;
364
+ }
365
+ finally {
366
+ clearTimeout(timer);
367
+ if (outerSignal !== undefined) {
368
+ outerSignal.removeEventListener('abort', onOuterAbort);
369
+ }
370
+ }
371
+ }
372
+ }
373
+ exports.FireweaveRemoteAdapter = FireweaveRemoteAdapter;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Canonical default host allowlist.
3
+ *
4
+ * v3 replaced the third-party analytics hostnames this list used to carry
5
+ * (ADR-0006) with Fireweave's own fw-server hostnames. The security property is
6
+ * unchanged — an unconfigured allowlist still denies everything it does not name
7
+ * — but the *contents* changed: code doing
8
+ * `allowedHosts: [...DEFAULT_ALLOWED_HOSTS, …]` no longer reaches the former
9
+ * vendor endpoints, which is the intent.
10
+ */
11
+ export declare const DEFAULT_ALLOWED_HOSTS: readonly string[];
12
+ /** True for loopback hostnames (http and test stubs are permitted here). */
13
+ export declare function isLoopbackHostname(hostname: string): boolean;
14
+ /**
15
+ * Validate a backend host URL against the allowlist.
16
+ * Throws FireweaveError('Configuration') — fixed message, no host echo.
17
+ */
18
+ export declare function assertHostAllowed(host: string, allowedHosts?: readonly string[]): void;
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_ALLOWED_HOSTS = void 0;
4
+ exports.isLoopbackHostname = isLoopbackHostname;
5
+ exports.assertHostAllowed = assertHostAllowed;
6
+ /**
7
+ * Backend host allowlist (SSRF guard).
8
+ *
9
+ * The allowlist is ON by default: when no explicit `allowedHosts` is
10
+ * configured, only the canonical Fireweave hosts plus loopback are permitted,
11
+ * so a typo'd or tampered `host` cannot be aimed at cloud metadata endpoints.
12
+ * Custom / self-hosted deployments require an explicit `allowedHosts` entry;
13
+ * `['*']` opts out entirely.
14
+ *
15
+ * Scheme policy: https is required for non-loopback hosts; plain http is
16
+ * permitted on loopback only (the repo's test-server stub).
17
+ */
18
+ const errors_js_1 = require("../domain/errors.js");
19
+ /**
20
+ * Canonical default host allowlist.
21
+ *
22
+ * v3 replaced the third-party analytics hostnames this list used to carry
23
+ * (ADR-0006) with Fireweave's own fw-server hostnames. The security property is
24
+ * unchanged — an unconfigured allowlist still denies everything it does not name
25
+ * — but the *contents* changed: code doing
26
+ * `allowedHosts: [...DEFAULT_ALLOWED_HOSTS, …]` no longer reaches the former
27
+ * vendor endpoints, which is the intent.
28
+ */
29
+ exports.DEFAULT_ALLOWED_HOSTS = Object.freeze([
30
+ 'app-server.fireweave.ai',
31
+ 'staging-app-server.fireweave.ai',
32
+ 'localhost',
33
+ '127.0.0.1',
34
+ '::1',
35
+ ]);
36
+ /** True for loopback hostnames (http and test stubs are permitted here). */
37
+ function isLoopbackHostname(hostname) {
38
+ return (hostname === 'localhost' ||
39
+ hostname === '127.0.0.1' ||
40
+ hostname === '::1' ||
41
+ hostname === '[::1]');
42
+ }
43
+ /**
44
+ * Validate a backend host URL against the allowlist.
45
+ * Throws FireweaveError('Configuration') — fixed message, no host echo.
46
+ */
47
+ function assertHostAllowed(host, allowedHosts) {
48
+ let url;
49
+ try {
50
+ url = new URL(host);
51
+ }
52
+ catch {
53
+ throw new errors_js_1.FireweaveError('Configuration');
54
+ }
55
+ if (url.protocol !== 'http:' && url.protocol !== 'https:') {
56
+ throw new errors_js_1.FireweaveError('Configuration');
57
+ }
58
+ const loopback = isLoopbackHostname(url.hostname);
59
+ if (url.protocol === 'http:' && !loopback) {
60
+ // https required for anything that leaves the machine.
61
+ throw new errors_js_1.FireweaveError('Configuration');
62
+ }
63
+ const list = allowedHosts !== undefined && allowedHosts.length > 0 ? allowedHosts : exports.DEFAULT_ALLOWED_HOSTS;
64
+ if (list.includes('*'))
65
+ return; // explicit opt-out
66
+ const allowed = list.some((h) => url.hostname === h ||
67
+ (h === 'localhost' && loopback) ||
68
+ (h === '::1' && url.hostname === '[::1]'));
69
+ if (!allowed) {
70
+ throw new errors_js_1.FireweaveError('Configuration');
71
+ }
72
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /** Browser build of @fireweaveai/server-sdk/register: fails loudly, never starts. */
4
+ const index_js_1 = require("./index.js");
5
+ throw new index_js_1.FireweaveError('Configuration', {
6
+ message: '[fireweave] @fireweaveai/server-sdk/register is for server runtimes. Browser apps use @fireweaveai/web-sdk.',
7
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /**
4
+ * @fireweaveai/server-sdk/register: start FireWeave from the environment alone.
5
+ *
6
+ * import '@fireweaveai/server-sdk/register'; // first import of the entrypoint
7
+ * node --import @fireweaveai/server-sdk/register app.js
8
+ * bun --preload @fireweaveai/server-sdk/register app.ts
9
+ *
10
+ * Use src/fireweave/start.ts instead when you pass control points or other options.
11
+ */
12
+ const index_js_1 = require("./start/index.js");
13
+ (0, index_js_1.start)();
@@ -0,0 +1,12 @@
1
+ export declare function start(): void;
2
+ export declare function defineControlPoints<T>(controlPoints: T): T;
3
+ export declare function resetForTests(): Promise<void>;
4
+ export declare const fw: Readonly<{
5
+ controlPoints: {};
6
+ identify: () => never;
7
+ instanceKey: () => never;
8
+ status: () => never;
9
+ client: () => never;
10
+ shutdown: () => never;
11
+ }>;
12
+ export { SDK_VERSION, SDK_CHANNEL } from './build-info.js';
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SDK_CHANNEL = exports.SDK_VERSION = exports.fw = void 0;
4
+ exports.start = start;
5
+ exports.defineControlPoints = defineControlPoints;
6
+ exports.resetForTests = resetForTests;
7
+ /**
8
+ * Browser build of @fireweaveai/server-sdk/start (the "browser" export
9
+ * condition). Server keys must never reach a bundle, so every entry point
10
+ * fails loudly and names the right package.
11
+ */
12
+ const index_js_1 = require("../index.js");
13
+ const serverOnly = () => new index_js_1.FireweaveError('Configuration', {
14
+ message: '[fireweave] @fireweaveai/server-sdk/start is for server runtimes. Browser apps use @fireweaveai/web-sdk.',
15
+ });
16
+ function start() {
17
+ throw serverOnly();
18
+ }
19
+ function defineControlPoints(controlPoints) {
20
+ return controlPoints;
21
+ }
22
+ async function resetForTests() { }
23
+ const fail = () => {
24
+ throw serverOnly();
25
+ };
26
+ exports.fw = Object.freeze({
27
+ controlPoints: new Proxy({}, { get: () => fail }),
28
+ identify: fail,
29
+ instanceKey: fail,
30
+ status: fail,
31
+ client: fail,
32
+ shutdown: fail,
33
+ });
34
+ var build_info_js_1 = require("./build-info.js");
35
+ Object.defineProperty(exports, "SDK_VERSION", { enumerable: true, get: function () { return build_info_js_1.SDK_VERSION; } });
36
+ Object.defineProperty(exports, "SDK_CHANNEL", { enumerable: true, get: function () { return build_info_js_1.SDK_CHANNEL; } });
@@ -0,0 +1,2 @@
1
+ export declare const SDK_VERSION = "3.0.0";
2
+ export declare const SDK_CHANNEL: 'staging' | 'production';
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SDK_CHANNEL = exports.SDK_VERSION = void 0;
4
+ // GENERATED by tools/release/version.sh apply server — do not edit by hand.
5
+ // The start profile reads SDK_CHANNEL to choose its default fw-server host
6
+ // (docs/adr/0012-start-profile.md). test/unit/start-build-info.test.ts pins
7
+ // SDK_VERSION to package.json, so a release that forgets to stamp fails CI.
8
+ exports.SDK_VERSION = '3.0.0';
9
+ exports.SDK_CHANNEL = 'production';
@@ -0,0 +1,20 @@
1
+ export interface ControlPointDefinition {
2
+ /** Value served in local mode. Ignored in remote mode. */
3
+ readonly local: boolean;
4
+ /** Optional note for humans and agents. Never sent anywhere. */
5
+ readonly description?: string;
6
+ }
7
+ export type ControlPointMap = Readonly<Record<string, ControlPointDefinition>>;
8
+ /** Validate a control-points object and return a frozen copy. Throws Configuration on a bad entry. */
9
+ export declare function normalizeControlPoints(controlPoints: unknown): ControlPointMap;
10
+ /**
11
+ * Declare the app's control points. Returns its argument unchanged, typed, and
12
+ * checks it at import time so a typo fails where it was made.
13
+ *
14
+ * export const controlPoints = defineControlPoints({
15
+ * 'new-checkout': { local: true },
16
+ * });
17
+ */
18
+ export declare function defineControlPoints<const T extends Record<string, ControlPointDefinition>>(controlPoints: T): T;
19
+ /** The core local adapter's seed map. */
20
+ export declare function toLocalControlPoints(controlPoints: ControlPointMap): Record<string, boolean>;
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizeControlPoints = normalizeControlPoints;
4
+ exports.defineControlPoints = defineControlPoints;
5
+ exports.toLocalControlPoints = toLocalControlPoints;
6
+ /**
7
+ * The control-points object: every control point the app reads, with the value
8
+ * served in local mode. It lives in its own file
9
+ * (src/fireweave/control-points.ts by default) and is passed as
10
+ * start({ controlPoints }).
11
+ *
12
+ * It holds local values only. In remote mode fw-server and the rollout decide,
13
+ * and call sites keep `false` as their default (RAMP-1), so this file can
14
+ * never switch a feature on in production.
15
+ */
16
+ const index_js_1 = require("../index.js");
17
+ const configError = (message) => new index_js_1.FireweaveError('Configuration', { message });
18
+ /** Validate a control-points object and return a frozen copy. Throws Configuration on a bad entry. */
19
+ function normalizeControlPoints(controlPoints) {
20
+ if (controlPoints === undefined || controlPoints === null)
21
+ return Object.freeze({});
22
+ if (typeof controlPoints !== 'object' || Array.isArray(controlPoints)) {
23
+ throw configError('[fireweave] start({ controlPoints }) must be an object of { key: { local: boolean } }.');
24
+ }
25
+ const out = {};
26
+ for (const [key, entry] of Object.entries(controlPoints)) {
27
+ const keyResult = (0, index_js_1.validateControlPointKey)(key);
28
+ if (!keyResult.ok)
29
+ throw configError(`[fireweave] controlPoints: '${key}' is not a valid control point key.`);
30
+ if (typeof entry !== 'object' || entry === null || typeof entry.local !== 'boolean') {
31
+ throw configError(`[fireweave] controlPoints['${key}'] must be { local: true } or { local: false }.`);
32
+ }
33
+ const { local, description } = entry;
34
+ if (description !== undefined && typeof description !== 'string') {
35
+ throw configError(`[fireweave] controlPoints['${key}'].description must be a string.`);
36
+ }
37
+ out[key] = Object.freeze(description === undefined ? { local } : { local, description });
38
+ }
39
+ return Object.freeze(out);
40
+ }
41
+ /**
42
+ * Declare the app's control points. Returns its argument unchanged, typed, and
43
+ * checks it at import time so a typo fails where it was made.
44
+ *
45
+ * export const controlPoints = defineControlPoints({
46
+ * 'new-checkout': { local: true },
47
+ * });
48
+ */
49
+ function defineControlPoints(controlPoints) {
50
+ normalizeControlPoints(controlPoints);
51
+ return controlPoints;
52
+ }
53
+ /** The core local adapter's seed map. */
54
+ function toLocalControlPoints(controlPoints) {
55
+ const out = {};
56
+ for (const [key, entry] of Object.entries(controlPoints))
57
+ out[key] = entry.local;
58
+ return out;
59
+ }
@@ -0,0 +1,11 @@
1
+ /** Reads one variable. Returns undefined when unset, empty, or only whitespace. */
2
+ export type EnvReader = (name: string) => string | undefined;
3
+ /** Reader over an explicit bag (start({ env }) and tests). Only string values count. */
4
+ export declare function envFromBag(bag: Readonly<Record<string, unknown>>): EnvReader;
5
+ /** Reader over the running process: Deno.env first, then process.env. */
6
+ export declare function processEnv(): EnvReader;
7
+ /**
8
+ * A reader that never throws, for values only needed to word a warning or an
9
+ * error (for example the key a local start ignores).
10
+ */
11
+ export declare function quiet(read: EnvReader): EnvReader;