@fireweaveai/web-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 (59) hide show
  1. package/README.md +2 -51
  2. package/package.json +4 -66
  3. package/LICENSE +0 -21
  4. package/dist/application/client.d.ts +0 -104
  5. package/dist/application/client.js +0 -147
  6. package/dist/application/mode.d.ts +0 -99
  7. package/dist/application/mode.js +0 -101
  8. package/dist/application/ports.d.ts +0 -101
  9. package/dist/application/ports.js +0 -2
  10. package/dist/application/runtime.d.ts +0 -70
  11. package/dist/application/runtime.js +0 -348
  12. package/dist/build/node/define.d.ts +0 -14
  13. package/dist/build/node/define.js +0 -38
  14. package/dist/build/node/shared.d.ts +0 -19
  15. package/dist/build/node/shared.js +0 -39
  16. package/dist/build/node/vite.d.ts +0 -53
  17. package/dist/build/node/vite.js +0 -106
  18. package/dist/build/src/start/build-info.d.ts +0 -2
  19. package/dist/build/src/start/build-info.js +0 -9
  20. package/dist/build/src/start/names.d.ts +0 -38
  21. package/dist/build/src/start/names.js +0 -41
  22. package/dist/build/src/start/policy.d.ts +0 -70
  23. package/dist/build/src/start/policy.js +0 -128
  24. package/dist/domain/context.d.ts +0 -51
  25. package/dist/domain/context.js +0 -72
  26. package/dist/domain/errors.d.ts +0 -36
  27. package/dist/domain/errors.js +0 -97
  28. package/dist/domain/target.d.ts +0 -2
  29. package/dist/domain/target.js +0 -2
  30. package/dist/domain/types.d.ts +0 -89
  31. package/dist/domain/types.js +0 -10
  32. package/dist/domain/validation.d.ts +0 -128
  33. package/dist/domain/validation.js +0 -358
  34. package/dist/index.d.ts +0 -50
  35. package/dist/index.js +0 -66
  36. package/dist/infrastructure/adapters/inmemory.d.ts +0 -50
  37. package/dist/infrastructure/adapters/inmemory.js +0 -94
  38. package/dist/infrastructure/adapters/local.d.ts +0 -79
  39. package/dist/infrastructure/adapters/local.js +0 -98
  40. package/dist/infrastructure/adapters/remote.d.ts +0 -44
  41. package/dist/infrastructure/adapters/remote.js +0 -329
  42. package/dist/infrastructure/hosts.d.ts +0 -10
  43. package/dist/infrastructure/hosts.js +0 -84
  44. package/dist/start/build-info.d.ts +0 -2
  45. package/dist/start/build-info.js +0 -9
  46. package/dist/start/control-points.d.ts +0 -24
  47. package/dist/start/control-points.js +0 -61
  48. package/dist/start/fw.d.ts +0 -42
  49. package/dist/start/fw.js +0 -137
  50. package/dist/start/identity.d.ts +0 -19
  51. package/dist/start/identity.js +0 -93
  52. package/dist/start/index.d.ts +0 -34
  53. package/dist/start/index.js +0 -38
  54. package/dist/start/names.d.ts +0 -38
  55. package/dist/start/names.js +0 -41
  56. package/dist/start/policy.d.ts +0 -70
  57. package/dist/start/policy.js +0 -128
  58. package/dist/start/state.d.ts +0 -103
  59. package/dist/start/state.js +0 -375
@@ -1,358 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.validateControlPointKey = validateControlPointKey;
4
- exports.matchesExpectedType = matchesExpectedType;
5
- exports.validateDefaultValue = validateDefaultValue;
6
- exports.validateTargetingKey = validateTargetingKey;
7
- exports.validateContext = validateContext;
8
- exports.canonicalizeContext = canonicalizeContext;
9
- exports.validateInitOptions = validateInitOptions;
10
- /**
11
- * Fireweave web SDK validation — pure, total functions per
12
- * spec/control-points.md "Validation, before any I/O" and spec/modes.md
13
- * "Initialisation validation".
14
- *
15
- * Ported from sdks/node/src/domain/validation.ts (the reference
16
- * implementation) — same five validators, same `Validated<T>` discipline,
17
- * same malformed-key → ControlPointNotFound mapping. Reimplemented here rather than
18
- * shared so the browser package keeps zero cross-package dependency to
19
- * audit — the same reasoning ADR-0009 gives for duplicating types.ts/errors.ts
20
- * extends to this module.
21
- *
22
- * Every read-path validator here (`validateControlPointKey`,
23
- * `validateDefaultValue`, `validateContext`) returns `Validated<T>` instead
24
- * of throwing. `FireweaveWebRuntime.evaluateSync` runs them, in the fixed
25
- * order the spec names — key, default-vs-type, context, lifecycle — and
26
- * degrades to the caller's default on the first failure; it NEVER throws
27
- * (spec/control-points.md "Return discipline — never throw into a read
28
- * path"). `validateInitOptions` is the one exception (spec/modes.md
29
- * "Initialisation validation"): it still returns `Validated<T>` — mode.ts is
30
- * what converts a failed result into the throw spec/modes.md requires.
31
- *
32
- * Note: unlike node, web's `FireweaveError` has no custom-message support —
33
- * messages are FIXED per kind by design, because a browser error is visible
34
- * in a devtools console on a machine this package does not control
35
- * (errors.ts's own doc comment). So every branch below that would carry a
36
- * distinct node message instead just uses the taxonomy's fixed message for
37
- * its kind.
38
- */
39
- const errors_js_1 = require("./errors.js");
40
- const ok = (value) => ({ ok: true, value });
41
- const fail = (error) => ({ ok: false, error });
42
- // ---------------------------------------------------------------------------
43
- // Rule 1 — validateControlPointKey (spec/control-points.md "Validation,
44
- // before any I/O": "key — non-empty, ≤256 characters, no control characters")
45
- // ---------------------------------------------------------------------------
46
- /** C0 + C1 control characters (U+0000-U+001F, U+007F-U+009F). */
47
- const CONTROL_CHARACTERS = new RegExp('[' + String.fromCharCode(0x00) + '-' + String.fromCharCode(0x1f) +
48
- String.fromCharCode(0x7f) + '-' + String.fromCharCode(0x9f) + ']');
49
- const MAX_CONTROL_POINT_KEY_LENGTH = 256;
50
- /**
51
- * key — non-empty, ≤256 characters, no control characters
52
- * (spec/control-points.md rule 1, the first check in the fixed order).
53
- *
54
- * No taxonomy kind names "malformed key" explicitly (the return-discipline
55
- * table's closest row is "key unknown to the backend" → ControlPointNotFound):
56
- * controller-ruled interim mapping (mirrors node's domain/validation.ts
57
- * verbatim) — the 15-kind taxonomy in errors.schema.json is frozen at
58
- * exactly 15 entries, `InvalidContext` is textually scoped to the evaluation
59
- * *context* (not the key), and the schema already maps another non-literal
60
- * case — quota-limited responses — onto `ControlPointNotFound` rather than adding a
61
- * kind for it. `ControlPointNotFound` is therefore the least-wrong existing kind,
62
- * not a literal fit. Other-language implementations copying node as the
63
- * reference SHOULD use the same mapping rather than each independently
64
- * guessing a different one.
65
- */
66
- function validateControlPointKey(key) {
67
- if (typeof key !== 'string' || key.length === 0) {
68
- return fail(new errors_js_1.FireweaveError('ControlPointNotFound'));
69
- }
70
- if (key.length > MAX_CONTROL_POINT_KEY_LENGTH) {
71
- return fail(new errors_js_1.FireweaveError('ControlPointNotFound'));
72
- }
73
- if (CONTROL_CHARACTERS.test(key)) {
74
- return fail(new errors_js_1.FireweaveError('ControlPointNotFound'));
75
- }
76
- return ok(key);
77
- }
78
- /**
79
- * Whether `value` matches the shape `expected` names. Shared by
80
- * {@link validateDefaultValue} (the caller's default, before any read) and
81
- * `FireweaveWebRuntime.evaluateSync`'s post-cache-lookup check (the backend's
82
- * resolved value, from the prefetched cache) — same predicate, two different
83
- * inputs.
84
- */
85
- function matchesExpectedType(value, expected) {
86
- switch (expected) {
87
- case 'boolean':
88
- return typeof value === 'boolean';
89
- case 'string':
90
- return typeof value === 'string';
91
- case 'number':
92
- return typeof value === 'number';
93
- case 'object':
94
- return typeof value === 'object' && value !== null;
95
- }
96
- }
97
- /**
98
- * default vs type — e.g. `getBooleanValue` with a non-boolean default is
99
- * TypeMismatch (spec/control-points.md rule 2, checked before any read).
100
- */
101
- function validateDefaultValue(expectedType, defaultValue) {
102
- if (!matchesExpectedType(defaultValue, expectedType)) {
103
- return fail(new errors_js_1.FireweaveError('TypeMismatch'));
104
- }
105
- return ok(defaultValue);
106
- }
107
- // ---------------------------------------------------------------------------
108
- // validateTargetingKey (spec/control-points.md "Context": targetingKey)
109
- // ---------------------------------------------------------------------------
110
- /**
111
- * targetingKey: "An SDK MUST NOT invent one: a missing targeting key is
112
- * InvalidContext where the evaluation needs it, never a generated anonymous
113
- * id" (spec/control-points.md "Context"). `required` is call-site policy —
114
- * the remote adapter always requires one for `prefetch`/`registerTarget`; the
115
- * generic context pipeline ({@link validateContext}) only does when
116
- * `ContextPolicy.requireTargetingKey` opts in (unset by default here — web's
117
- * targetingKey requirement is enforced at the remote adapter, not the
118
- * general-purpose context validator every read runs).
119
- */
120
- function validateTargetingKey(targetingKey, required) {
121
- if (required && (targetingKey === undefined || targetingKey === '')) {
122
- return fail(new errors_js_1.FireweaveError('InvalidContext', { openFeatureErrorCode: 'TARGETING_KEY_MISSING' }));
123
- }
124
- return ok(targetingKey);
125
- }
126
- // ---------------------------------------------------------------------------
127
- // Rule 3 — validateContext (spec/control-points.md rule 3: "context — depth,
128
- // key count, value size, reserved keys (evaluation-context.schema.json)")
129
- // ---------------------------------------------------------------------------
130
- /**
131
- * UTF-8 byte length. `TextEncoder` rather than `Buffer.byteLength` — `Buffer`
132
- * does not exist in a browser at all (browser-portability.test.ts pins this).
133
- */
134
- const utf8Encoder = new TextEncoder();
135
- const byteLength = (s) => utf8Encoder.encode(s).length;
136
- /**
137
- * Sentinel returned (never thrown) when `deepCopyJson` walks back into an
138
- * object already on the current recursion path — an actual reference cycle,
139
- * not merely the same object shared by two sibling branches (which is legal
140
- * and handled by backtracking `seen.delete(...)` below). Untyped caller
141
- * input is `unknown`, so a circular attribute value is reachable from a
142
- * caller today; without this, recursion never terminates and crashes with
143
- * `RangeError: Maximum call stack size exceeded` — exactly the kind of throw
144
- * a "before any I/O" validator must not allow (spec/control-points.md
145
- * "Return discipline — never throw into a read path"). Module-local: never
146
- * returned across this file's public surface, only used to fail
147
- * `validateContext` closed as `InvalidContext`.
148
- */
149
- const CYCLIC = Symbol('fireweave.cyclicJson');
150
- function deepCopyJson(value, seen = new WeakSet()) {
151
- if (value === null || typeof value === 'string' || typeof value === 'boolean' || typeof value === 'number') {
152
- return value;
153
- }
154
- if (Array.isArray(value)) {
155
- if (seen.has(value))
156
- return CYCLIC;
157
- seen.add(value);
158
- const out = [];
159
- for (const v of value) {
160
- const copied = deepCopyJson(v, seen);
161
- if (copied === CYCLIC)
162
- return CYCLIC;
163
- out.push(copied);
164
- }
165
- seen.delete(value);
166
- return out;
167
- }
168
- if (typeof value === 'object') {
169
- if (seen.has(value))
170
- return CYCLIC;
171
- seen.add(value);
172
- const out = {};
173
- for (const [k, v] of Object.entries(value)) {
174
- if (v !== undefined) {
175
- const copied = deepCopyJson(v, seen);
176
- if (copied === CYCLIC)
177
- return CYCLIC;
178
- out[k] = copied;
179
- }
180
- }
181
- seen.delete(value);
182
- return out;
183
- }
184
- return null;
185
- }
186
- /**
187
- * Cycle-safe defensively: `nestingDepth` only ever runs on `deepCopyJson`'s
188
- * own output today, which is guaranteed acyclic by construction (it builds
189
- * fresh containers, never back-references). Guarded anyway per the same
190
- * "must be genuinely total" reasoning as `deepCopyJson`. A detected cycle
191
- * returns `Infinity`, which always exceeds `limits.maxNestingDepth` and fails
192
- * closed through the existing bound check below — no new failure path.
193
- */
194
- function nestingDepth(value, seen = new WeakSet()) {
195
- if (value === null || typeof value !== 'object')
196
- return 0;
197
- if (seen.has(value))
198
- return Infinity;
199
- seen.add(value);
200
- let max = 0;
201
- const children = Array.isArray(value) ? value : Object.values(value);
202
- for (const child of children) {
203
- const d = nestingDepth(child, seen);
204
- if (d > max)
205
- max = d;
206
- }
207
- seen.delete(value);
208
- return max + 1;
209
- }
210
- const invalidContext = () => new errors_js_1.FireweaveError('InvalidContext');
211
- /** groups/groupProperties carriers: canonical fireweave.* spelling first, then the plain alias. */
212
- function extractSpecialKey(attributes, canonicalKey, aliasKey) {
213
- if (canonicalKey in attributes)
214
- return attributes[canonicalKey];
215
- if (aliasKey in attributes)
216
- return attributes[aliasKey];
217
- return undefined;
218
- }
219
- /**
220
- * context — depth, key count, value size, reserved keys
221
- * (evaluation-context.schema.json) (spec/control-points.md rule 3). Also
222
- * enforces `policy.requireTargetingKey` via {@link validateTargetingKey}, and
223
- * — on success — extracts `groups`/`groupProperties` the same way the former
224
- * `canonicalizeContext` did.
225
- *
226
- * `groups`/`groupProperties` (and their `fireweave.*` aliases) are pulled out
227
- * BEFORE the attribute-count/size limit checks run, matching the pre-existing
228
- * behaviour this module replaces: they are not ordinary attributes and were
229
- * never counted against those limits.
230
- *
231
- * `merged` is the already-merged (global → invocation) raw input; merging
232
- * itself (`mergeContexts`) lives in context.ts and carries no validation
233
- * concerns of its own.
234
- */
235
- function validateContext(merged, policy) {
236
- const { limits } = policy;
237
- const rawAttributes = { ...merged.attributes };
238
- const rawGroups = extractSpecialKey(rawAttributes, 'fireweave.groups', 'groups');
239
- const rawGroupProperties = extractSpecialKey(rawAttributes, 'fireweave.groupProperties', 'groupProperties');
240
- delete rawAttributes['fireweave.groups'];
241
- delete rawAttributes['groups'];
242
- delete rawAttributes['fireweave.groupProperties'];
243
- delete rawAttributes['groupProperties'];
244
- const copied = deepCopyJson(rawAttributes);
245
- if (copied === CYCLIC) {
246
- return fail(invalidContext());
247
- }
248
- const attributes = copied;
249
- const keys = Object.keys(attributes);
250
- if (keys.length > limits.maxAttributeCount) {
251
- return fail(invalidContext());
252
- }
253
- for (const key of keys) {
254
- if (byteLength(key) > limits.maxKeyBytes) {
255
- return fail(invalidContext());
256
- }
257
- }
258
- for (const key of keys) {
259
- const value = attributes[key];
260
- if (byteLength(JSON.stringify(value ?? null)) > limits.maxValueBytes) {
261
- return fail(invalidContext());
262
- }
263
- }
264
- for (const key of keys) {
265
- const value = attributes[key];
266
- if (value !== undefined && nestingDepth(value) > limits.maxNestingDepth) {
267
- return fail(invalidContext());
268
- }
269
- }
270
- const serialized = JSON.stringify({ targetingKey: merged.targetingKey, attributes });
271
- if (byteLength(serialized) > limits.maxSerializedBytes) {
272
- return fail(invalidContext());
273
- }
274
- for (const key of keys) {
275
- if (policy.reservedAttributeKeys.includes(key)) {
276
- return fail(invalidContext());
277
- }
278
- }
279
- const targetingKeyResult = validateTargetingKey(merged.targetingKey, policy.requireTargetingKey);
280
- if (!targetingKeyResult.ok)
281
- return targetingKeyResult;
282
- let groups;
283
- if (rawGroups !== undefined) {
284
- const g = deepCopyJson(rawGroups);
285
- if (g !== CYCLIC && g !== null && typeof g === 'object' && !Array.isArray(g)) {
286
- const entries = Object.entries(g);
287
- if (entries.every(([, v]) => typeof v === 'string')) {
288
- groups = Object.fromEntries(entries);
289
- }
290
- }
291
- }
292
- let groupProperties;
293
- if (rawGroupProperties !== undefined) {
294
- const gp = deepCopyJson(rawGroupProperties);
295
- if (gp !== CYCLIC && gp !== null && typeof gp === 'object' && !Array.isArray(gp)) {
296
- groupProperties = gp;
297
- }
298
- }
299
- const canonical = {
300
- attributes,
301
- ...(merged.targetingKey !== undefined ? { targetingKey: merged.targetingKey } : {}),
302
- ...(groups !== undefined ? { groups } : {}),
303
- ...(groupProperties !== undefined ? { groupProperties } : {}),
304
- };
305
- return ok(canonical);
306
- }
307
- /**
308
- * Throwing wrapper around {@link validateContext}, kept for callers that
309
- * genuinely want a throw (`FireweaveWebRuntime.refresh`, which is already
310
- * inside its own try/catch feeding lifecycle state — see runtime.ts). Read
311
- * paths do not use this — they call {@link validateContext} directly so a
312
- * failure degrades instead of throwing.
313
- */
314
- function canonicalizeContext(merged, policy) {
315
- const result = validateContext(merged, policy);
316
- if (!result.ok)
317
- throw result.error;
318
- return result.value;
319
- }
320
- // ---------------------------------------------------------------------------
321
- // validateInitOptions (spec/modes.md "Initialisation validation")
322
- // ---------------------------------------------------------------------------
323
- /** "missing" and "blank" collapse to one check: not a non-empty string. */
324
- const isBlank = (value) => typeof value !== 'string' || value.trim().length === 0;
325
- const configError = () => new errors_js_1.FireweaveError('Configuration');
326
- /**
327
- * Initialisation-validation table (spec/modes.md), rows 1, 2 and 4:
328
- * - `mode` absent or unrecognised
329
- * - `mode: 'remote'` with `apiKey` or `apiUrl` missing/blank
330
- * - `mode: 'local'` with credentials supplied (a config half-migrated from
331
- * remote to local reads as neither, silently — reject it instead)
332
- *
333
- * Row 3 ("apiUrl fails the host allowlist") is intentionally NOT checked
334
- * here — mode.ts validates it directly via `assertHostAllowed` before
335
- * calling into the (deliberately non-throwing) runtime; see mode.ts's module
336
- * doc comment for why web cannot rely on the runtime the way node does.
337
- */
338
- function validateInitOptions(options) {
339
- const mode = options?.mode;
340
- if (mode !== 'local' && mode !== 'remote') {
341
- return fail(configError());
342
- }
343
- // Runtime-only guard: a config object half-migrated from remote to local
344
- // can carry apiKey/apiUrl even though the local-mode type declares
345
- // neither — TypeScript's excess-property check only fires on a fresh
346
- // object literal, not on a variable assembled elsewhere and passed in.
347
- const credentials = options;
348
- if (mode === 'remote') {
349
- if (isBlank(credentials.apiKey) || isBlank(credentials.apiUrl)) {
350
- return fail(configError());
351
- }
352
- return ok(options);
353
- }
354
- if (!isBlank(credentials.apiKey) || !isBlank(credentials.apiUrl)) {
355
- return fail(configError());
356
- }
357
- return ok(options);
358
- }
package/dist/index.d.ts DELETED
@@ -1,50 +0,0 @@
1
- /**
2
- * @fireweaveai/web-sdk — Fireweave control points for the browser (ADR-0009).
3
- *
4
- * Remote-only and secret-free by construction: it never evaluates locally, has
5
- * no vendor SDK dependency, reads no environment, and rejects vendor/secret key
6
- * shapes at the door. Credentials are passed in explicitly by the embedding app.
7
- *
8
- * ```ts
9
- * import { initFireweave } from '@fireweaveai/web-sdk';
10
- *
11
- * const fw = await initFireweave({
12
- * mode: 'remote',
13
- * apiKey,
14
- * apiUrl,
15
- * context: { targetingKey: 'anonymous' },
16
- * });
17
- *
18
- * // Reads are SYNCHRONOUS — no await, safe inside render.
19
- * const on = fw.controlPoints.getBooleanValue('new-checkout', false);
20
- * ```
21
- *
22
- * `src/` is layered domain/ · application/ · infrastructure/
23
- * (test/unit/architecture-layers.test.ts guards the boundary): `domain/` is
24
- * pure data + validation with no outward imports; `application/` wires
25
- * domain types into the runtime/client surface, with `mode.ts` as the sole
26
- * composition root allowed to reach into `infrastructure/`; `infrastructure/`
27
- * holds the concrete adapters and the host allowlist.
28
- */
29
- export { FireweaveError, ERROR_TAXONOMY, isFireweaveError } from './domain/errors.js';
30
- export type { FireweaveErrorKind, FireweaveErrorOptions, OpenFeatureErrorCode, ErrorKindSpec } from './domain/errors.js';
31
- export type { JsonValue, FlagValueType, Decision, DecisionReason, CanonicalContext, LifecycleState, Signal, SignalKind, Exposure, ReleaseContext, ReleaseState, Capabilities, } from './domain/types.js';
32
- export { DEFAULT_CONTEXT_LIMITS, DEFAULT_RESERVED_ATTRIBUTE_KEYS, mergeContexts } from './domain/context.js';
33
- export type { ContextInput, ContextLimits, ContextPolicy } from './domain/context.js';
34
- export { canonicalizeContext, matchesExpectedType, validateContext, validateControlPointKey, validateDefaultValue, validateTargetingKey, validateInitOptions, } from './domain/validation.js';
35
- export type { Validated } from './domain/validation.js';
36
- export type { TargetKind } from './domain/target.js';
37
- export { DEFAULT_ALLOWED_HOSTS, assertHostAllowed, assertNotSecretKey, isLoopbackHostname } from './infrastructure/hosts.js';
38
- export type { WebBackendAdapter, AdapterResolution, AdapterRuntimeFeatures, PrefetchOptions, PrefetchResult, RegisterTargetOptions, RegisterTargetResult, } from './application/ports.js';
39
- export { FireweaveRemoteWebAdapter } from './infrastructure/adapters/remote.js';
40
- export type { FireweaveRemoteWebAdapterOptions, FireweaveFetchLike } from './infrastructure/adapters/remote.js';
41
- export { InMemoryWebAdapter } from './infrastructure/adapters/inmemory.js';
42
- export type { InMemoryWebAdapterOptions, InMemoryFlagDefinition, InMemoryFault } from './infrastructure/adapters/inmemory.js';
43
- export { FireweaveLocalWebAdapter } from './infrastructure/adapters/local.js';
44
- export type { FireweaveLocalWebAdapterOptions, LocalRegisteredTarget } from './infrastructure/adapters/local.js';
45
- export { FireweaveWebRuntime, DEFAULT_FLAGS_READY_TIMEOUT_MS } from './application/runtime.js';
46
- export type { FireweaveWebRuntimeConfig, ExpectedFlagType } from './application/runtime.js';
47
- export { FireweaveWebClient, WebControlPointsApi } from './application/client.js';
48
- export type { FireweaveWebClientOptions, ExtensionResult, EvaluateOptions } from './application/client.js';
49
- export { initFireweave } from './application/mode.js';
50
- export type { InitFireweaveOptions, InitFireweaveLocalOptions, InitFireweaveRemoteOptions } from './application/mode.js';
package/dist/index.js DELETED
@@ -1,66 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.initFireweave = exports.WebControlPointsApi = exports.FireweaveWebClient = exports.DEFAULT_FLAGS_READY_TIMEOUT_MS = exports.FireweaveWebRuntime = exports.FireweaveLocalWebAdapter = exports.InMemoryWebAdapter = exports.FireweaveRemoteWebAdapter = exports.isLoopbackHostname = exports.assertNotSecretKey = exports.assertHostAllowed = exports.DEFAULT_ALLOWED_HOSTS = exports.validateInitOptions = exports.validateTargetingKey = exports.validateDefaultValue = exports.validateControlPointKey = exports.validateContext = exports.matchesExpectedType = exports.canonicalizeContext = exports.mergeContexts = exports.DEFAULT_RESERVED_ATTRIBUTE_KEYS = exports.DEFAULT_CONTEXT_LIMITS = exports.isFireweaveError = exports.ERROR_TAXONOMY = exports.FireweaveError = void 0;
4
- /**
5
- * @fireweaveai/web-sdk — Fireweave control points for the browser (ADR-0009).
6
- *
7
- * Remote-only and secret-free by construction: it never evaluates locally, has
8
- * no vendor SDK dependency, reads no environment, and rejects vendor/secret key
9
- * shapes at the door. Credentials are passed in explicitly by the embedding app.
10
- *
11
- * ```ts
12
- * import { initFireweave } from '@fireweaveai/web-sdk';
13
- *
14
- * const fw = await initFireweave({
15
- * mode: 'remote',
16
- * apiKey,
17
- * apiUrl,
18
- * context: { targetingKey: 'anonymous' },
19
- * });
20
- *
21
- * // Reads are SYNCHRONOUS — no await, safe inside render.
22
- * const on = fw.controlPoints.getBooleanValue('new-checkout', false);
23
- * ```
24
- *
25
- * `src/` is layered domain/ · application/ · infrastructure/
26
- * (test/unit/architecture-layers.test.ts guards the boundary): `domain/` is
27
- * pure data + validation with no outward imports; `application/` wires
28
- * domain types into the runtime/client surface, with `mode.ts` as the sole
29
- * composition root allowed to reach into `infrastructure/`; `infrastructure/`
30
- * holds the concrete adapters and the host allowlist.
31
- */
32
- var errors_js_1 = require("./domain/errors.js");
33
- Object.defineProperty(exports, "FireweaveError", { enumerable: true, get: function () { return errors_js_1.FireweaveError; } });
34
- Object.defineProperty(exports, "ERROR_TAXONOMY", { enumerable: true, get: function () { return errors_js_1.ERROR_TAXONOMY; } });
35
- Object.defineProperty(exports, "isFireweaveError", { enumerable: true, get: function () { return errors_js_1.isFireweaveError; } });
36
- var context_js_1 = require("./domain/context.js");
37
- Object.defineProperty(exports, "DEFAULT_CONTEXT_LIMITS", { enumerable: true, get: function () { return context_js_1.DEFAULT_CONTEXT_LIMITS; } });
38
- Object.defineProperty(exports, "DEFAULT_RESERVED_ATTRIBUTE_KEYS", { enumerable: true, get: function () { return context_js_1.DEFAULT_RESERVED_ATTRIBUTE_KEYS; } });
39
- Object.defineProperty(exports, "mergeContexts", { enumerable: true, get: function () { return context_js_1.mergeContexts; } });
40
- var validation_js_1 = require("./domain/validation.js");
41
- Object.defineProperty(exports, "canonicalizeContext", { enumerable: true, get: function () { return validation_js_1.canonicalizeContext; } });
42
- Object.defineProperty(exports, "matchesExpectedType", { enumerable: true, get: function () { return validation_js_1.matchesExpectedType; } });
43
- Object.defineProperty(exports, "validateContext", { enumerable: true, get: function () { return validation_js_1.validateContext; } });
44
- Object.defineProperty(exports, "validateControlPointKey", { enumerable: true, get: function () { return validation_js_1.validateControlPointKey; } });
45
- Object.defineProperty(exports, "validateDefaultValue", { enumerable: true, get: function () { return validation_js_1.validateDefaultValue; } });
46
- Object.defineProperty(exports, "validateTargetingKey", { enumerable: true, get: function () { return validation_js_1.validateTargetingKey; } });
47
- Object.defineProperty(exports, "validateInitOptions", { enumerable: true, get: function () { return validation_js_1.validateInitOptions; } });
48
- var hosts_js_1 = require("./infrastructure/hosts.js");
49
- Object.defineProperty(exports, "DEFAULT_ALLOWED_HOSTS", { enumerable: true, get: function () { return hosts_js_1.DEFAULT_ALLOWED_HOSTS; } });
50
- Object.defineProperty(exports, "assertHostAllowed", { enumerable: true, get: function () { return hosts_js_1.assertHostAllowed; } });
51
- Object.defineProperty(exports, "assertNotSecretKey", { enumerable: true, get: function () { return hosts_js_1.assertNotSecretKey; } });
52
- Object.defineProperty(exports, "isLoopbackHostname", { enumerable: true, get: function () { return hosts_js_1.isLoopbackHostname; } });
53
- var remote_js_1 = require("./infrastructure/adapters/remote.js");
54
- Object.defineProperty(exports, "FireweaveRemoteWebAdapter", { enumerable: true, get: function () { return remote_js_1.FireweaveRemoteWebAdapter; } });
55
- var inmemory_js_1 = require("./infrastructure/adapters/inmemory.js");
56
- Object.defineProperty(exports, "InMemoryWebAdapter", { enumerable: true, get: function () { return inmemory_js_1.InMemoryWebAdapter; } });
57
- var local_js_1 = require("./infrastructure/adapters/local.js");
58
- Object.defineProperty(exports, "FireweaveLocalWebAdapter", { enumerable: true, get: function () { return local_js_1.FireweaveLocalWebAdapter; } });
59
- var runtime_js_1 = require("./application/runtime.js");
60
- Object.defineProperty(exports, "FireweaveWebRuntime", { enumerable: true, get: function () { return runtime_js_1.FireweaveWebRuntime; } });
61
- Object.defineProperty(exports, "DEFAULT_FLAGS_READY_TIMEOUT_MS", { enumerable: true, get: function () { return runtime_js_1.DEFAULT_FLAGS_READY_TIMEOUT_MS; } });
62
- var client_js_1 = require("./application/client.js");
63
- Object.defineProperty(exports, "FireweaveWebClient", { enumerable: true, get: function () { return client_js_1.FireweaveWebClient; } });
64
- Object.defineProperty(exports, "WebControlPointsApi", { enumerable: true, get: function () { return client_js_1.WebControlPointsApi; } });
65
- var mode_js_1 = require("./application/mode.js");
66
- Object.defineProperty(exports, "initFireweave", { enumerable: true, get: function () { return mode_js_1.initFireweave; } });
@@ -1,50 +0,0 @@
1
- /**
2
- * In-memory adapter — offline evaluation for tests and conformance fixtures.
3
- *
4
- * Declares control points up front and serves them from memory. Fault
5
- * injection exists so the conformance suite can drive the error taxonomy
6
- * without a network or a stub server.
7
- */
8
- import { type FireweaveErrorKind } from '../../domain/errors.js';
9
- import type { AdapterResolution, AdapterRuntimeFeatures, PrefetchOptions, PrefetchResult, WebBackendAdapter } from '../../application/ports.js';
10
- import type { CanonicalContext, Exposure, FlagValueType, JsonValue, Signal } from '../../domain/types.js';
11
- export interface InMemoryFlagDefinition {
12
- readonly type: FlagValueType;
13
- readonly enabled: boolean;
14
- readonly value: JsonValue;
15
- readonly variant?: string;
16
- readonly payload?: JsonValue;
17
- /** Only resolve for this targeting key; otherwise the control point misses. */
18
- readonly matchTargetingKey?: string;
19
- readonly fireweaveReason?: AdapterResolution['reason'];
20
- }
21
- export interface InMemoryFault {
22
- readonly kind: FireweaveErrorKind;
23
- /** Throw on initialize rather than on prefetch. */
24
- readonly onInitialize?: boolean;
25
- }
26
- export interface InMemoryWebAdapterOptions {
27
- readonly flags?: Record<string, InMemoryFlagDefinition>;
28
- readonly fault?: InMemoryFault;
29
- }
30
- export declare class InMemoryWebAdapter implements WebBackendAdapter {
31
- readonly name: "inmemory";
32
- private flags;
33
- private fault;
34
- private readonly exposures;
35
- private readonly signals;
36
- private closed;
37
- constructor(options?: InMemoryWebAdapterOptions);
38
- setFlags(flags: Record<string, InMemoryFlagDefinition>): void;
39
- setFault(fault: InMemoryFault | undefined): void;
40
- initialize(_signal?: AbortSignal): Promise<void>;
41
- prefetch(context: CanonicalContext, options?: PrefetchOptions): Promise<PrefetchResult>;
42
- recordExposure(exposure: Exposure): void;
43
- getExposures(): readonly Exposure[];
44
- recordSignal(signal: Signal): void;
45
- getSignals(): readonly Signal[];
46
- flush(): Promise<void>;
47
- shutdown(): Promise<void>;
48
- isClosed(): boolean;
49
- features(): AdapterRuntimeFeatures;
50
- }
@@ -1,94 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.InMemoryWebAdapter = void 0;
4
- /**
5
- * In-memory adapter — offline evaluation for tests and conformance fixtures.
6
- *
7
- * Declares control points up front and serves them from memory. Fault
8
- * injection exists so the conformance suite can drive the error taxonomy
9
- * without a network or a stub server.
10
- */
11
- const errors_js_1 = require("../../domain/errors.js");
12
- class InMemoryWebAdapter {
13
- name = 'inmemory';
14
- flags;
15
- fault;
16
- exposures = [];
17
- signals = [];
18
- closed = false;
19
- constructor(options = {}) {
20
- this.flags = new Map(Object.entries(options.flags ?? {}));
21
- this.fault = options.fault;
22
- }
23
- setFlags(flags) {
24
- this.flags = new Map(Object.entries(flags));
25
- }
26
- setFault(fault) {
27
- this.fault = fault;
28
- }
29
- async initialize(_signal) {
30
- if (this.fault?.onInitialize === true)
31
- throw new errors_js_1.FireweaveError(this.fault.kind);
32
- }
33
- async prefetch(context, options) {
34
- if (this.fault !== undefined && this.fault.onInitialize !== true) {
35
- throw new errors_js_1.FireweaveError(this.fault.kind);
36
- }
37
- const wanted = options?.controlPointKeys;
38
- const out = new Map();
39
- for (const [key, def] of this.flags) {
40
- if (wanted !== undefined && wanted.length > 0 && !wanted.includes(key))
41
- continue;
42
- if (def.matchTargetingKey !== undefined && context.targetingKey !== def.matchTargetingKey) {
43
- out.set(key, { found: false });
44
- continue;
45
- }
46
- const resolution = {
47
- found: true,
48
- enabled: def.enabled,
49
- value: def.value,
50
- flagType: def.type,
51
- };
52
- if (def.variant !== undefined)
53
- resolution.variant = def.variant;
54
- if (def.payload !== undefined)
55
- resolution.payload = def.payload;
56
- if (def.fireweaveReason !== undefined)
57
- resolution.reason = def.fireweaveReason;
58
- out.set(key, resolution);
59
- }
60
- return out;
61
- }
62
- recordExposure(exposure) {
63
- this.exposures.push(exposure);
64
- }
65
- getExposures() {
66
- return this.exposures;
67
- }
68
- recordSignal(signal) {
69
- this.signals.push(signal);
70
- }
71
- getSignals() {
72
- return this.signals;
73
- }
74
- async flush() {
75
- // no-op: telemetry stays queryable for tests
76
- }
77
- async shutdown() {
78
- this.closed = true;
79
- }
80
- isClosed() {
81
- return this.closed;
82
- }
83
- features() {
84
- return {
85
- remoteEvaluation: false,
86
- localEvaluation: false,
87
- localOnly: true,
88
- exposureEmission: true,
89
- sideEffectFreeReads: true,
90
- groupAnalytics: false,
91
- };
92
- }
93
- }
94
- exports.InMemoryWebAdapter = InMemoryWebAdapter;