@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,53 +0,0 @@
1
- import { type BuildOptions } from './shared.js';
2
- export type FireweavePluginOptions = BuildOptions;
3
- /** The subset of Vite's types the plugin touches, so the package needs no vite types to build. */
4
- interface UserConfigLike {
5
- readonly root?: string;
6
- readonly envDir?: string | false;
7
- readonly build?: {
8
- readonly lib?: unknown;
9
- };
10
- }
11
- interface ConfigEnvLike {
12
- readonly command: 'serve' | 'build';
13
- readonly mode: string;
14
- readonly isPreview?: boolean;
15
- }
16
- interface ResolvedConfigLike {
17
- readonly envPrefix?: string | readonly string[];
18
- readonly build?: {
19
- readonly ssr?: unknown;
20
- };
21
- }
22
- type OutputFileLike = {
23
- readonly type: 'chunk';
24
- readonly code: string;
25
- } | {
26
- readonly type: 'asset';
27
- readonly source: string | Uint8Array;
28
- };
29
- interface PluginContextLike {
30
- readonly environment?: {
31
- readonly config?: {
32
- readonly consumer?: string;
33
- };
34
- };
35
- }
36
- export type LoadEnv = (mode: string, envDir: string, prefixes: string[]) => Record<string, string> | Promise<Record<string, string>>;
37
- export interface FireweaveVitePlugin {
38
- readonly name: 'fireweave';
39
- readonly enforce: 'pre';
40
- config(userConfig: UserConfigLike, env: ConfigEnvLike): Promise<Record<string, unknown> | undefined>;
41
- configResolved(config: ResolvedConfigLike): void;
42
- generateBundle(this: PluginContextLike, options: unknown, bundle: Readonly<Record<string, OutputFileLike>>): void;
43
- }
44
- /** Test seam: the env-file loader and the process environment. */
45
- export interface PluginDeps {
46
- readonly loadEnv?: LoadEnv;
47
- readonly env?: Readonly<Record<string, string | undefined>>;
48
- readonly log?: (line: string) => void;
49
- }
50
- export declare function createFireweavePlugin(options?: FireweavePluginOptions, deps?: PluginDeps): FireweaveVitePlugin;
51
- /** The fireweave() Vite plugin. Add it to `plugins` in vite.config.ts (or a framework's `vite.plugins`). */
52
- export declare function fireweave(options?: FireweavePluginOptions): FireweaveVitePlugin;
53
- export {};
@@ -1,106 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createFireweavePlugin = createFireweavePlugin;
4
- exports.fireweave = fireweave;
5
- /**
6
- * @fireweaveai/web-sdk/vite — the fireweave() Vite plugin.
7
- *
8
- * // vite.config.ts
9
- * import { fireweave } from '@fireweaveai/web-sdk/vite';
10
- * export default defineConfig({ plugins: [fireweave()] });
11
- *
12
- * At config time (Node) it reads FIREWEAVE_BROWSER_KEY, FIREWEAVE_URL and the
13
- * environment name from the process environment and Vite's env files, applies
14
- * the start profile's policy, and either fails the dev server or build with a
15
- * message naming the variable, or injects the result as
16
- * __FIREWEAVE_WEB_CONFIG__ for start() to pick up. After a client build it
17
- * fails if a server key reached any output file.
18
- *
19
- * Environment name: FIREWEAVE_ENV, then APP_ENV; the dev server and Vitest
20
- * also fall back to Vite's mode. A build never infers local mode from
21
- * `--mode development`, so a production build without a key fails.
22
- *
23
- * Node build tooling: nothing here ships to the browser. vite is an optional
24
- * peer and is loaded lazily, so this file has no static import of it.
25
- */
26
- const node_path_1 = require("node:path");
27
- const names_js_1 = require("../src/start/names.js");
28
- const policy_js_1 = require("../src/start/policy.js");
29
- const shared_js_1 = require("./shared.js");
30
- const ENV_PREFIXES = ['FIREWEAVE_', names_js_1.ENVIRONMENT_FALLBACK];
31
- const SERVER_KEY_PATTERN = /project-api-key_[A-Za-z0-9_-]{8,}/;
32
- async function viteLoadEnv() {
33
- const specifier = 'vite';
34
- const vite = (await import(specifier));
35
- return vite.loadEnv;
36
- }
37
- /** Mirror Vite's env-file location: envDir resolved against the resolved root; false disables files. */
38
- function envDirOf(userConfig) {
39
- if (userConfig.envDir === false)
40
- return false;
41
- const root = (0, node_path_1.resolve)(userConfig.root ?? process.cwd());
42
- return userConfig.envDir !== undefined ? (0, node_path_1.resolve)(root, userConfig.envDir) : root;
43
- }
44
- function createFireweavePlugin(options = {}, deps = {}) {
45
- const env = deps.env ?? process.env;
46
- const log = deps.log ?? ((line) => console.info(line));
47
- let skip = false;
48
- let clientSsr = false;
49
- return {
50
- name: 'fireweave',
51
- enforce: 'pre',
52
- async config(userConfig, configEnv) {
53
- if (configEnv.isPreview === true) {
54
- skip = true;
55
- return undefined;
56
- }
57
- if (userConfig.build?.lib !== undefined && userConfig.build.lib !== false) {
58
- skip = true;
59
- log('[fireweave] build.lib: no config injected. A library must not bake in an app key; the app that bundles it runs fireweave().');
60
- return undefined;
61
- }
62
- const envDir = envDirOf(userConfig);
63
- const loadEnv = deps.loadEnv ?? (envDir === false ? undefined : await viteLoadEnv());
64
- const files = envDir === false || loadEnv === undefined ? {} : await loadEnv(configEnv.mode, envDir, ENV_PREFIXES);
65
- const lookup = (name) => (0, policy_js_1.sourced)(env[name], name) ?? (0, policy_js_1.sourced)(files[name], `${name} (.env files)`);
66
- const viteMode = configEnv.command === 'serve' ? (0, policy_js_1.sourced)(configEnv.mode, `Vite mode '${configEnv.mode}'`) : undefined;
67
- const resolved = (0, shared_js_1.resolveBuild)(options, lookup, names_js_1.BUILD_ENV.key, [names_js_1.BUILD_ENV.environment, names_js_1.ENVIRONMENT_FALLBACK], viteMode);
68
- for (const w of resolved.warnings)
69
- log(w);
70
- log((0, shared_js_1.summaryLine)(resolved.config));
71
- return {
72
- define: { [names_js_1.INJECTED_CONFIG_NAME]: JSON.stringify(resolved.injected) },
73
- // Keep the SDK out of the dep pre-bundle so the injected config is never cached stale.
74
- optimizeDeps: { exclude: ['@fireweaveai/web-sdk'] },
75
- };
76
- },
77
- configResolved(config) {
78
- if (skip)
79
- return;
80
- const prefixes = config.envPrefix === undefined ? ['VITE_'] : typeof config.envPrefix === 'string' ? [config.envPrefix] : [...config.envPrefix];
81
- const exposed = names_js_1.SERVER_KEY_NAMES.filter((name) => prefixes.some((p) => p !== '' && name.startsWith(p)));
82
- if (exposed.length > 0) {
83
- throw new Error(`[fireweave] envPrefix would expose ${exposed.join(' and ')} to the browser bundle. Remove that prefix; the browser key is injected by fireweave() as ${names_js_1.BUILD_ENV.key}.`);
84
- }
85
- clientSsr = config.build?.ssr !== undefined && config.build.ssr !== false;
86
- },
87
- generateBundle(_options, bundle) {
88
- if (skip)
89
- return;
90
- const serverEnvironment = this.environment?.config?.consumer === 'server' || (this.environment === undefined && clientSsr);
91
- if (serverEnvironment)
92
- return;
93
- const secrets = names_js_1.SERVER_KEY_NAMES.map((name) => env[name]?.trim()).filter((v) => v !== undefined && v.length >= 8);
94
- for (const [fileName, file] of Object.entries(bundle)) {
95
- const text = file.type === 'chunk' ? file.code : typeof file.source === 'string' ? file.source : '';
96
- if (SERVER_KEY_PATTERN.test(text) || secrets.some((secret) => text.includes(secret))) {
97
- throw new Error(`[fireweave] ${fileName} contains a server key. Server keys must never ship to a browser: remove it from client code, and revoke it if this build was deployed.`);
98
- }
99
- }
100
- },
101
- };
102
- }
103
- /** The fireweave() Vite plugin. Add it to `plugins` in vite.config.ts (or a framework's `vite.plugins`). */
104
- function fireweave(options = {}) {
105
- return createFireweavePlugin(options);
106
- }
@@ -1,2 +0,0 @@
1
- export declare const SDK_VERSION = "3.0.0";
2
- export declare const SDK_CHANNEL: 'staging' | 'production';
@@ -1,9 +0,0 @@
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 web — 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';
@@ -1,38 +0,0 @@
1
- /**
2
- * Every name the web start profile and its build helpers use, in one place, so
3
- * the README, the initialise skill and the error messages cannot drift apart.
4
- *
5
- * Imported by browser code and by the Node build helpers (node/), so it holds
6
- * constants only.
7
- */
8
- /** Build-time variables the Vite plugin and the define helper read. The browser reads none. */
9
- export declare const BUILD_ENV: Readonly<{
10
- key: "FIREWEAVE_BROWSER_KEY";
11
- url: "FIREWEAVE_URL";
12
- environment: "FIREWEAVE_ENV";
13
- }>;
14
- /** Fallback environment name after FIREWEAVE_ENV, at build time. */
15
- export declare const ENVIRONMENT_FALLBACK = "APP_ENV";
16
- /** Server secrets the Vite plugin looks for in output chunks. Never injected, never printed. */
17
- export declare const SERVER_KEY_NAMES: readonly string[];
18
- /** Environment names that mean "local development" when no key is set. Case-insensitive. */
19
- export declare const DEV_ENVIRONMENTS: ReadonlySet<string>;
20
- /** fw-server host for each release channel of this package. */
21
- export declare const CHANNEL_URLS: Readonly<{
22
- production: "https://app-server.fireweave.ai";
23
- staging: "https://staging-app-server.fireweave.ai";
24
- }>;
25
- /** Hosts always allowed beside a custom endpoint, so local stacks keep working. */
26
- export declare const LOOPBACK_HOSTS: readonly string[];
27
- /** The only key family a browser may hold. */
28
- export declare const BROWSER_KEY_PREFIX = "fw_public_";
29
- /** localStorage keys. The device id key and its dev_ prefix match the scaffolded harness, so returning visitors keep their bucket. */
30
- export declare const STORAGE_KEYS: Readonly<{
31
- deviceId: "fireweave.device-id";
32
- identity: "fireweave.identity";
33
- deviceRegistered: "fireweave.device-registered";
34
- }>;
35
- /** Where the control-points object lives by convention; named in warnings. */
36
- export declare const CONTROL_POINTS_FILE = "src/fireweave/control-points.ts";
37
- /** The identifier the build helpers define and the browser reads behind a typeof guard. */
38
- export declare const INJECTED_CONFIG_NAME = "__FIREWEAVE_WEB_CONFIG__";
@@ -1,41 +0,0 @@
1
- "use strict";
2
- /**
3
- * Every name the web start profile and its build helpers use, in one place, so
4
- * the README, the initialise skill and the error messages cannot drift apart.
5
- *
6
- * Imported by browser code and by the Node build helpers (node/), so it holds
7
- * constants only.
8
- */
9
- Object.defineProperty(exports, "__esModule", { value: true });
10
- exports.INJECTED_CONFIG_NAME = exports.CONTROL_POINTS_FILE = exports.STORAGE_KEYS = exports.BROWSER_KEY_PREFIX = exports.LOOPBACK_HOSTS = exports.CHANNEL_URLS = exports.DEV_ENVIRONMENTS = exports.SERVER_KEY_NAMES = exports.ENVIRONMENT_FALLBACK = exports.BUILD_ENV = void 0;
11
- /** Build-time variables the Vite plugin and the define helper read. The browser reads none. */
12
- exports.BUILD_ENV = Object.freeze({
13
- key: 'FIREWEAVE_BROWSER_KEY',
14
- url: 'FIREWEAVE_URL',
15
- environment: 'FIREWEAVE_ENV',
16
- });
17
- /** Fallback environment name after FIREWEAVE_ENV, at build time. */
18
- exports.ENVIRONMENT_FALLBACK = 'APP_ENV';
19
- /** Server secrets the Vite plugin looks for in output chunks. Never injected, never printed. */
20
- exports.SERVER_KEY_NAMES = Object.freeze(['FIREWEAVE_KEY', 'FW_PROJECT_API_KEY']);
21
- /** Environment names that mean "local development" when no key is set. Case-insensitive. */
22
- exports.DEV_ENVIRONMENTS = new Set(['development', 'dev', 'local', 'test']);
23
- /** fw-server host for each release channel of this package. */
24
- exports.CHANNEL_URLS = Object.freeze({
25
- production: 'https://app-server.fireweave.ai',
26
- staging: 'https://staging-app-server.fireweave.ai',
27
- });
28
- /** Hosts always allowed beside a custom endpoint, so local stacks keep working. */
29
- exports.LOOPBACK_HOSTS = Object.freeze(['localhost', '127.0.0.1', '::1']);
30
- /** The only key family a browser may hold. */
31
- exports.BROWSER_KEY_PREFIX = 'fw_public_';
32
- /** localStorage keys. The device id key and its dev_ prefix match the scaffolded harness, so returning visitors keep their bucket. */
33
- exports.STORAGE_KEYS = Object.freeze({
34
- deviceId: 'fireweave.device-id',
35
- identity: 'fireweave.identity',
36
- deviceRegistered: 'fireweave.device-registered',
37
- });
38
- /** Where the control-points object lives by convention; named in warnings. */
39
- exports.CONTROL_POINTS_FILE = 'src/fireweave/control-points.ts';
40
- /** The identifier the build helpers define and the browser reads behind a typeof guard. */
41
- exports.INJECTED_CONFIG_NAME = '__FIREWEAVE_WEB_CONFIG__';
@@ -1,70 +0,0 @@
1
- export type StartMode = 'remote' | 'local';
2
- export type SdkChannel = 'staging' | 'production';
3
- /** A value and where it came from ('start({ key })', 'FIREWEAVE_BROWSER_KEY', ...). */
4
- export interface Sourced {
5
- readonly value: string;
6
- readonly source: string;
7
- }
8
- export type PolicyReason = 'invalid-mode' | 'missing-key' | 'server-key' | 'wrong-key-family' | 'insecure-url';
9
- export interface PolicyInput {
10
- readonly mode?: unknown;
11
- readonly key?: Sourced | undefined;
12
- readonly url?: Sourced | undefined;
13
- readonly environment?: Sourced | undefined;
14
- readonly channel: SdkChannel;
15
- /** The variable or option a fix should name, e.g. FIREWEAVE_BROWSER_KEY. */
16
- readonly keyVariable: string;
17
- /** Where an environment name was looked for, for the missing-key message. */
18
- readonly environmentChecked: string;
19
- }
20
- export interface PolicyConfig {
21
- readonly mode: StartMode;
22
- /** Why this mode: the mode option, a present key, or the environment name. */
23
- readonly modeSource: 'option' | 'key' | 'environment';
24
- /** Remote only. A value starting with '/' is a same-origin proxy path. */
25
- readonly url?: string;
26
- readonly urlSource?: string;
27
- /** Remote only, and absent for the channel default (the core's own list covers it). */
28
- readonly allowedHosts?: readonly string[];
29
- /** Remote only. Never logged. */
30
- readonly key?: string;
31
- readonly keySource: string;
32
- readonly environment?: string;
33
- readonly environmentSource?: string;
34
- }
35
- export type PolicyResult = {
36
- readonly ok: true;
37
- readonly config: PolicyConfig;
38
- readonly warnings: readonly string[];
39
- } | {
40
- readonly ok: false;
41
- readonly reason: PolicyReason;
42
- readonly variable: string;
43
- readonly message: string;
44
- };
45
- /** A trimmed, non-empty string with its source, or undefined. Non-strings count as unset. */
46
- export declare function sourced(value: unknown, source: string): Sourced | undefined;
47
- /** The first candidate that is set. */
48
- export declare function firstOf(...candidates: ReadonlyArray<Sourced | undefined>): Sourced | undefined;
49
- /**
50
- * Resolve mode, key and endpoint.
51
- *
52
- * An explicit mode wins. Otherwise a key means remote; no key and a
53
- * development environment name means local; anything else fails closed, so a
54
- * missing key never turns into silent local evaluation for real visitors.
55
- */
56
- export declare function resolvePolicy(input: PolicyInput): PolicyResult;
57
- /**
58
- * What the build helpers inject as __FIREWEAVE_WEB_CONFIG__: raw values and
59
- * their source names, re-resolved in the browser so explicit start() options
60
- * still win. Never carries a server key or the control-points object.
61
- */
62
- export interface InjectedConfig {
63
- readonly v: 1;
64
- readonly key?: string;
65
- readonly keySource?: string;
66
- readonly url?: string;
67
- readonly urlSource?: string;
68
- readonly environment?: string;
69
- readonly environmentSource?: string;
70
- }
@@ -1,128 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.sourced = sourced;
4
- exports.firstOf = firstOf;
5
- exports.resolvePolicy = resolvePolicy;
6
- /**
7
- * The web start-profile policy: one pure resolver shared by the browser
8
- * start() and the Node build helpers (node/vite.ts, node/define.ts), so a
9
- * build and the page it produces can never disagree about mode, key or URL.
10
- *
11
- * Pure on purpose: no DOM, no env, no core import. Callers hand in values
12
- * with their source names; the result is a config, or a failure carrying a
13
- * fixed reason code and a message that names sources and variables, never a
14
- * value.
15
- */
16
- const names_js_1 = require("./names.js");
17
- /** A trimmed, non-empty string with its source, or undefined. Non-strings count as unset. */
18
- function sourced(value, source) {
19
- if (typeof value !== 'string')
20
- return undefined;
21
- const trimmed = value.trim();
22
- return trimmed === '' ? undefined : { value: trimmed, source };
23
- }
24
- /** The first candidate that is set. */
25
- function firstOf(...candidates) {
26
- return candidates.find((c) => c !== undefined);
27
- }
28
- const fail = (reason, variable, message) => ({
29
- ok: false,
30
- reason,
31
- variable,
32
- message: `[fireweave] ${message}`,
33
- });
34
- /** Key family check. Messages name the source, never the value. */
35
- function checkKey(key) {
36
- const v = key.value;
37
- if (v.startsWith(names_js_1.BROWSER_KEY_PREFIX))
38
- return undefined;
39
- if (v.startsWith('project-api-key_')) {
40
- return fail('server-key', key.source, `The key from ${key.source} is a server key (project-api-key_…), which must never ship to a browser. Use a browser key (fw_public_…) from Project settings, API keys. If a bundle built with this key was deployed, revoke the key.`);
41
- }
42
- if (/^ph[a-z]_/.test(v)) {
43
- return fail('wrong-key-family', key.source, `The key from ${key.source} is an analytics vendor key, not a FireWeave browser key (fw_public_…).`);
44
- }
45
- if (v.startsWith('fw_org_') || v.startsWith('cli_at_')) {
46
- return fail('wrong-key-family', key.source, `The key from ${key.source} is an organisation or CLI token, not a FireWeave browser key (fw_public_…).`);
47
- }
48
- return fail('wrong-key-family', key.source, `The key from ${key.source} is not a FireWeave browser key (fw_public_…).`);
49
- }
50
- const isLoopback = (hostname) => hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '::1' || hostname === '[::1]';
51
- function resolveUrl(url, channel) {
52
- if (url === undefined)
53
- return { url: names_js_1.CHANNEL_URLS[channel], urlSource: `SDK channel (${channel})` };
54
- const value = url.value.replace(/\/+$/, '');
55
- if (value.startsWith('//')) {
56
- return fail('insecure-url', url.source, `The endpoint from ${url.source} is protocol-relative. Use an https URL or a same-origin path such as '/fw'.`);
57
- }
58
- // A same-origin proxy path: the browser resolves it against location.origin.
59
- if (value.startsWith('/'))
60
- return { url: value, urlSource: url.source };
61
- let parsed;
62
- try {
63
- parsed = new URL(value);
64
- }
65
- catch {
66
- return fail('insecure-url', url.source, `The endpoint from ${url.source} is not a valid URL.`);
67
- }
68
- const httpsOk = parsed.protocol === 'https:';
69
- const loopbackHttp = parsed.protocol === 'http:' && isLoopback(parsed.hostname);
70
- if (!httpsOk && !loopbackHttp) {
71
- return fail('insecure-url', url.source, `The endpoint from ${url.source} must use https (http is allowed only for localhost).`);
72
- }
73
- return { url: value, urlSource: url.source, allowedHosts: [parsed.hostname.replace(/^\[|\]$/g, ''), ...names_js_1.LOOPBACK_HOSTS] };
74
- }
75
- function remote(input, key, modeSource, warnings) {
76
- const keyProblem = checkKey(key);
77
- if (keyProblem !== undefined)
78
- return keyProblem;
79
- const url = resolveUrl(input.url, input.channel);
80
- if ('ok' in url)
81
- return url;
82
- return { ok: true, config: { mode: 'remote', modeSource, ...url, key: key.value, keySource: key.source }, warnings };
83
- }
84
- /**
85
- * Resolve mode, key and endpoint.
86
- *
87
- * An explicit mode wins. Otherwise a key means remote; no key and a
88
- * development environment name means local; anything else fails closed, so a
89
- * missing key never turns into silent local evaluation for real visitors.
90
- */
91
- function resolvePolicy(input) {
92
- const warnings = [];
93
- const { mode, key, environment } = input;
94
- if (mode !== undefined && mode !== 'remote' && mode !== 'local') {
95
- return fail('invalid-mode', 'mode', `start({ mode }) must be 'remote' or 'local'.`);
96
- }
97
- if (mode === 'local') {
98
- if (key !== undefined) {
99
- warnings.push(`[fireweave] mode 'local' ignores the key from ${key.source}; nothing is sent to fw-server.`);
100
- }
101
- return { ok: true, config: { mode: 'local', modeSource: 'option', keySource: 'none' }, warnings };
102
- }
103
- if (mode === 'remote') {
104
- if (key === undefined) {
105
- return fail('missing-key', input.keyVariable, `mode 'remote' needs a browser key. Set ${input.keyVariable} to a browser key (fw_public_…).`);
106
- }
107
- return remote(input, key, 'option', warnings);
108
- }
109
- if (key !== undefined)
110
- return remote(input, key, 'key', warnings);
111
- if (environment !== undefined && names_js_1.DEV_ENVIRONMENTS.has(environment.value.toLowerCase())) {
112
- return {
113
- ok: true,
114
- config: {
115
- mode: 'local',
116
- modeSource: 'environment',
117
- keySource: 'none',
118
- environment: environment.value,
119
- environmentSource: environment.source,
120
- },
121
- warnings,
122
- };
123
- }
124
- const where = environment === undefined
125
- ? `no environment name is set (checked ${input.environmentChecked})`
126
- : `the environment is '${environment.value}' (from ${environment.source}), which is not a development name`;
127
- return fail('missing-key', input.keyVariable, `${input.keyVariable} is not set and ${where}. Set ${input.keyVariable} to a browser key (fw_public_…), or for local work set FIREWEAVE_ENV=development or pass start({ mode: 'local' }).`);
128
- }
@@ -1,51 +0,0 @@
1
- /**
2
- * Evaluation-context merge & normalization — pure, no validation.
3
- *
4
- * Bound/reserved-key ENFORCEMENT (the Validated<T>-returning checks) lives in
5
- * validation.ts (`validateContext`) — spec/control-points.md "Validation,
6
- * before any I/O" rule 3. This module has no throwing/failing surface of its
7
- * own, mirroring the split sdks/node/src/domain/{context,validation}.ts
8
- * makes.
9
- *
10
- * A browser context is smaller than a server one — there is one user per
11
- * page, not one per request — but the same shape applies: `targetingKey` plus
12
- * a flat bag of attributes, later layers winning key by key (the OpenFeature
13
- * merge order).
14
- */
15
- export interface ContextLimits {
16
- readonly maxAttributeCount: number;
17
- readonly maxKeyBytes: number;
18
- readonly maxValueBytes: number;
19
- readonly maxSerializedBytes: number;
20
- readonly maxNestingDepth: number;
21
- }
22
- export declare const DEFAULT_CONTEXT_LIMITS: ContextLimits;
23
- /**
24
- * Keys the backend owns. A client that could set these could claim another
25
- * tenant's identity, so they are rejected rather than dropped — silently
26
- * discarding them would leave the caller believing targeting was applied.
27
- */
28
- export declare const DEFAULT_RESERVED_ATTRIBUTE_KEYS: readonly string[];
29
- export interface ContextPolicy {
30
- readonly limits: ContextLimits;
31
- readonly reservedAttributeKeys: readonly string[];
32
- readonly requireTargetingKey: boolean;
33
- }
34
- export type ContextInput = Record<string, unknown> & {
35
- targetingKey?: string;
36
- };
37
- /** One raw layer → {targetingKey?, attributes}. No validation, no copying beyond Object.entries. */
38
- export declare function normalizeContextInput(input: ContextInput | undefined): {
39
- targetingKey?: string;
40
- attributes: Record<string, unknown>;
41
- };
42
- /**
43
- * Merge context layers with later layers winning at the attribute-key level.
44
- * Layers are raw inputs; output is a plain {targetingKey?, attributes} bag —
45
- * validation (limits, reserved keys, cycle-safety) happens downstream in
46
- * validation.ts's `validateContext`.
47
- */
48
- export declare function mergeContexts(...layers: ReadonlyArray<ContextInput | undefined>): {
49
- targetingKey?: string;
50
- attributes: Record<string, unknown>;
51
- };
@@ -1,72 +0,0 @@
1
- "use strict";
2
- /**
3
- * Evaluation-context merge & normalization — pure, no validation.
4
- *
5
- * Bound/reserved-key ENFORCEMENT (the Validated<T>-returning checks) lives in
6
- * validation.ts (`validateContext`) — spec/control-points.md "Validation,
7
- * before any I/O" rule 3. This module has no throwing/failing surface of its
8
- * own, mirroring the split sdks/node/src/domain/{context,validation}.ts
9
- * makes.
10
- *
11
- * A browser context is smaller than a server one — there is one user per
12
- * page, not one per request — but the same shape applies: `targetingKey` plus
13
- * a flat bag of attributes, later layers winning key by key (the OpenFeature
14
- * merge order).
15
- */
16
- Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.DEFAULT_RESERVED_ATTRIBUTE_KEYS = exports.DEFAULT_CONTEXT_LIMITS = void 0;
18
- exports.normalizeContextInput = normalizeContextInput;
19
- exports.mergeContexts = mergeContexts;
20
- exports.DEFAULT_CONTEXT_LIMITS = Object.freeze({
21
- maxAttributeCount: 100,
22
- maxKeyBytes: 256,
23
- maxValueBytes: 8_192,
24
- maxSerializedBytes: 32_768,
25
- maxNestingDepth: 8,
26
- });
27
- /**
28
- * Keys the backend owns. A client that could set these could claim another
29
- * tenant's identity, so they are rejected rather than dropped — silently
30
- * discarding them would leave the caller believing targeting was applied.
31
- */
32
- exports.DEFAULT_RESERVED_ATTRIBUTE_KEYS = Object.freeze([
33
- 'fireweave.orgId',
34
- 'fireweave.projectId',
35
- 'fireweave.tenant',
36
- ]);
37
- /** One raw layer → {targetingKey?, attributes}. No validation, no copying beyond Object.entries. */
38
- function normalizeContextInput(input) {
39
- if (input === undefined || input === null)
40
- return { attributes: {} };
41
- const attributes = {};
42
- let targetingKey;
43
- for (const [key, value] of Object.entries(input)) {
44
- if (key === 'targetingKey') {
45
- if (typeof value === 'string' && value.length > 0)
46
- targetingKey = value;
47
- continue;
48
- }
49
- if (value !== undefined)
50
- attributes[key] = value;
51
- }
52
- return targetingKey !== undefined ? { targetingKey, attributes } : { attributes };
53
- }
54
- /**
55
- * Merge context layers with later layers winning at the attribute-key level.
56
- * Layers are raw inputs; output is a plain {targetingKey?, attributes} bag —
57
- * validation (limits, reserved keys, cycle-safety) happens downstream in
58
- * validation.ts's `validateContext`.
59
- */
60
- function mergeContexts(...layers) {
61
- let targetingKey;
62
- const attributes = {};
63
- for (const layer of layers) {
64
- const { targetingKey: tk, attributes: attrs } = normalizeContextInput(layer);
65
- if (tk !== undefined)
66
- targetingKey = tk;
67
- for (const [k, v] of Object.entries(attrs)) {
68
- attributes[k] = v;
69
- }
70
- }
71
- return targetingKey !== undefined ? { targetingKey, attributes } : { attributes };
72
- }
@@ -1,36 +0,0 @@
1
- /**
2
- * Error taxonomy for the browser SDK.
3
- *
4
- * Same kinds and same OpenFeature mapping as the server SDK, because the wire
5
- * protocol and the failure modes are the same — a 401 is a 401 whether it is a
6
- * server or a browser asking. Reimplemented here rather than shared so the
7
- * browser package keeps zero cross-package coupling and can be audited on its
8
- * own; `spec/errors.schema.json` is the source both follow.
9
- *
10
- * Messages are FIXED per kind. A browser error is visible in a devtools console
11
- * on a machine we do not control, so nothing host-, key-, or response-derived
12
- * is ever interpolated into one.
13
- */
14
- export type FireweaveErrorKind = 'Configuration' | 'Authentication' | 'Authorization' | 'Network' | 'Timeout' | 'RateLimited' | 'BackendUnavailable' | 'MalformedResponse' | 'InvalidContext' | 'ControlPointNotFound' | 'TypeMismatch' | 'NotReady' | 'AlreadyClosed' | 'UnsupportedCapability' | 'Internal';
15
- export type OpenFeatureErrorCode = 'PROVIDER_NOT_READY' | 'PROVIDER_FATAL' | 'FLAG_NOT_FOUND' | 'PARSE_ERROR' | 'TYPE_MISMATCH' | 'TARGETING_KEY_MISSING' | 'INVALID_CONTEXT' | 'GENERAL';
16
- export interface ErrorKindSpec {
17
- readonly message: string;
18
- readonly openFeatureErrorCode: OpenFeatureErrorCode;
19
- /** Whether an identical retry could plausibly succeed. */
20
- readonly retryable: boolean;
21
- }
22
- export declare const ERROR_TAXONOMY: Readonly<Record<FireweaveErrorKind, ErrorKindSpec>>;
23
- export interface FireweaveErrorOptions {
24
- /** Overrides the taxonomy's OpenFeature code (e.g. a missing targeting key). */
25
- readonly openFeatureErrorCode?: OpenFeatureErrorCode;
26
- readonly metadata?: Record<string, string | number | boolean>;
27
- readonly cause?: unknown;
28
- }
29
- export declare class FireweaveError extends Error {
30
- readonly kind: FireweaveErrorKind;
31
- readonly openFeatureErrorCode: OpenFeatureErrorCode;
32
- readonly retryable: boolean;
33
- readonly metadata: Record<string, string | number | boolean>;
34
- constructor(kind: FireweaveErrorKind, options?: FireweaveErrorOptions);
35
- }
36
- export declare function isFireweaveError(value: unknown): value is FireweaveError;