@fireweaveai/web-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.
- package/LICENSE +21 -0
- package/README.md +52 -0
- package/dist/application/client.d.ts +104 -0
- package/dist/application/client.js +147 -0
- package/dist/application/mode.d.ts +99 -0
- package/dist/application/mode.js +101 -0
- package/dist/application/ports.d.ts +101 -0
- package/dist/application/ports.js +2 -0
- package/dist/application/runtime.d.ts +70 -0
- package/dist/application/runtime.js +348 -0
- package/dist/build/node/define.d.ts +14 -0
- package/dist/build/node/define.js +38 -0
- package/dist/build/node/shared.d.ts +19 -0
- package/dist/build/node/shared.js +39 -0
- package/dist/build/node/vite.d.ts +53 -0
- package/dist/build/node/vite.js +106 -0
- package/dist/build/src/start/build-info.d.ts +2 -0
- package/dist/build/src/start/build-info.js +9 -0
- package/dist/build/src/start/names.d.ts +38 -0
- package/dist/build/src/start/names.js +41 -0
- package/dist/build/src/start/policy.d.ts +70 -0
- package/dist/build/src/start/policy.js +128 -0
- package/dist/domain/context.d.ts +51 -0
- package/dist/domain/context.js +72 -0
- package/dist/domain/errors.d.ts +36 -0
- package/dist/domain/errors.js +97 -0
- package/dist/domain/target.d.ts +2 -0
- package/dist/domain/target.js +2 -0
- package/dist/domain/types.d.ts +89 -0
- package/dist/domain/types.js +10 -0
- package/dist/domain/validation.d.ts +128 -0
- package/dist/domain/validation.js +358 -0
- package/dist/index.d.ts +50 -0
- package/dist/index.js +66 -0
- package/dist/infrastructure/adapters/inmemory.d.ts +50 -0
- package/dist/infrastructure/adapters/inmemory.js +94 -0
- package/dist/infrastructure/adapters/local.d.ts +79 -0
- package/dist/infrastructure/adapters/local.js +98 -0
- package/dist/infrastructure/adapters/remote.d.ts +44 -0
- package/dist/infrastructure/adapters/remote.js +329 -0
- package/dist/infrastructure/hosts.d.ts +10 -0
- package/dist/infrastructure/hosts.js +84 -0
- package/dist/start/build-info.d.ts +2 -0
- package/dist/start/build-info.js +9 -0
- package/dist/start/control-points.d.ts +24 -0
- package/dist/start/control-points.js +61 -0
- package/dist/start/fw.d.ts +42 -0
- package/dist/start/fw.js +137 -0
- package/dist/start/identity.d.ts +19 -0
- package/dist/start/identity.js +93 -0
- package/dist/start/index.d.ts +34 -0
- package/dist/start/index.js +38 -0
- package/dist/start/names.d.ts +38 -0
- package/dist/start/names.js +41 -0
- package/dist/start/policy.d.ts +70 -0
- package/dist/start/policy.js +128 -0
- package/dist/start/state.d.ts +103 -0
- package/dist/start/state.js +375 -0
- package/package.json +68 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* start() and the page-wide singleton behind `fw`.
|
|
3
|
+
*
|
|
4
|
+
* One slot per page (or worker) at a Symbol.for key on globalThis, so HMR
|
|
5
|
+
* re-evaluation and two bundled copies of this package share one client.
|
|
6
|
+
*
|
|
7
|
+
* Nothing here throws or rejects. A browser that fails to start must still
|
|
8
|
+
* render: a configuration fault logs one console.error, sets the state to
|
|
9
|
+
* FAILED and `problem`, and every read serves its default. The build helpers
|
|
10
|
+
* (`/vite`, `/define`) are where a bad configuration fails loudly.
|
|
11
|
+
*/
|
|
12
|
+
import { FireweaveError, type FireweaveWebClient } from '../index.js';
|
|
13
|
+
import { type ControlPointMap } from './control-points.js';
|
|
14
|
+
import { type Identity, type Persistence } from './identity.js';
|
|
15
|
+
import { type InjectedConfig, type PolicyConfig, type PolicyReason, type SdkChannel, type StartMode } from './policy.js';
|
|
16
|
+
export interface StartOptions {
|
|
17
|
+
/** Control points and their local values; import from src/fireweave/control-points.ts. */
|
|
18
|
+
readonly controlPoints?: ControlPointMap;
|
|
19
|
+
/** Force a mode. Without it: a key means remote; no key means local only in a dev environment. */
|
|
20
|
+
readonly mode?: StartMode;
|
|
21
|
+
/** Environment name used to infer the mode. Default: what the fireweave() build plugin injected. */
|
|
22
|
+
readonly environment?: string;
|
|
23
|
+
/** fw-server URL, or a same-origin proxy path such as '/fw'. Default: the build config, else this SDK build's channel host. */
|
|
24
|
+
readonly url?: string;
|
|
25
|
+
/** Browser key (fw_public_…). Default: FIREWEAVE_BROWSER_KEY, injected by the build plugin. */
|
|
26
|
+
readonly key?: string;
|
|
27
|
+
/** Initial storage mode. 'memory' writes nothing until fw.setPersistence('localStorage'). Default 'localStorage'. */
|
|
28
|
+
readonly persistence?: Persistence;
|
|
29
|
+
/** An app-supplied anonymous id (for example the analytics id), used verbatim and not stored. */
|
|
30
|
+
readonly deviceId?: string;
|
|
31
|
+
/** Where [fireweave] lines go. Default: console. */
|
|
32
|
+
readonly log?: (line: string) => void;
|
|
33
|
+
/** Transport override for tests. */
|
|
34
|
+
readonly fetch?: typeof fetch;
|
|
35
|
+
}
|
|
36
|
+
export type StartState = 'NOT_STARTED' | 'INITIALIZING' | 'READY' | 'STALE' | 'ERROR' | 'FAILED' | 'SHUTDOWN';
|
|
37
|
+
export interface StartProblem {
|
|
38
|
+
readonly reason: PolicyReason | 'invalid-control-points' | 'start-failed' | 'key-rejected' | 'unreachable';
|
|
39
|
+
/** The variable or option at fault, when there is one. Never a value. */
|
|
40
|
+
readonly variable?: string;
|
|
41
|
+
}
|
|
42
|
+
export interface FireweaveWebStatus {
|
|
43
|
+
readonly state: StartState;
|
|
44
|
+
readonly mode?: StartMode;
|
|
45
|
+
readonly modeSource?: PolicyConfig['modeSource'];
|
|
46
|
+
readonly channel: SdkChannel;
|
|
47
|
+
readonly sdkVersion: string;
|
|
48
|
+
/** fw-server host name only: never a path or a credential. */
|
|
49
|
+
readonly host?: string;
|
|
50
|
+
readonly endpointSource?: string;
|
|
51
|
+
readonly keySource?: string;
|
|
52
|
+
readonly environment?: string;
|
|
53
|
+
readonly controlPointCount?: number;
|
|
54
|
+
readonly problem?: StartProblem;
|
|
55
|
+
}
|
|
56
|
+
export interface Slot {
|
|
57
|
+
readonly protocol: 1;
|
|
58
|
+
state: StartState;
|
|
59
|
+
/** Bumped by every start and shutdown, so a late result from an older attempt is ignored. */
|
|
60
|
+
generation: number;
|
|
61
|
+
fingerprint?: string | undefined;
|
|
62
|
+
config?: PolicyConfig | undefined;
|
|
63
|
+
controlPoints: ControlPointMap;
|
|
64
|
+
persistence: Persistence;
|
|
65
|
+
appDeviceId: boolean;
|
|
66
|
+
identity?: Identity | undefined;
|
|
67
|
+
/** The key the current decisions were prefetched under. */
|
|
68
|
+
currentKey?: string | undefined;
|
|
69
|
+
client?: FireweaveWebClient | undefined;
|
|
70
|
+
problem?: StartProblem | undefined;
|
|
71
|
+
ready: Promise<void>;
|
|
72
|
+
/** Serializes identify / reset / forget, so the last call wins. */
|
|
73
|
+
chain: Promise<unknown>;
|
|
74
|
+
log: (line: string) => void;
|
|
75
|
+
readonly warned: Set<string>;
|
|
76
|
+
readonly listeners: Set<(state: StartState) => void>;
|
|
77
|
+
detachRuntime?: (() => void) | undefined;
|
|
78
|
+
}
|
|
79
|
+
export declare function slot(): Slot;
|
|
80
|
+
export declare function warnOnce(s: Slot, line: string): void;
|
|
81
|
+
/** console.error once per line. Errors bypass the log sink so they always reach the console. */
|
|
82
|
+
export declare function errorOnce(s: Slot, line: string): void;
|
|
83
|
+
/** Change state and notify. `always` re-notifies an unchanged state (a re-prefetch after identify). */
|
|
84
|
+
export declare function setState(s: Slot, next: StartState, always?: boolean): void;
|
|
85
|
+
/** What the fireweave() build plugin or fireweaveDefine() injected, if anything. */
|
|
86
|
+
export declare function injectedConfig(): InjectedConfig | undefined;
|
|
87
|
+
/**
|
|
88
|
+
* Start FireWeave for this page. Call once, from src/fireweave/start.ts.
|
|
89
|
+
*
|
|
90
|
+
* Resolves when the first prefetch settles (READY, STALE or ERROR) and never
|
|
91
|
+
* rejects, so `await start(...)` cannot blank a page. Check `fw.status()` for
|
|
92
|
+
* the outcome.
|
|
93
|
+
*/
|
|
94
|
+
export declare function start(options?: StartOptions): Promise<void>;
|
|
95
|
+
export declare function currentStatus(): FireweaveWebStatus;
|
|
96
|
+
/** Switch storage at runtime, for consent. Never part of the start() configuration check. */
|
|
97
|
+
export declare function setPersistenceFor(mode: Persistence): void;
|
|
98
|
+
/** Store (or clear) the signed-in key, when persistence allows it. */
|
|
99
|
+
export declare function rememberUser(s: Slot, user: string | undefined): void;
|
|
100
|
+
export declare function shutdownSlot(): Promise<void>;
|
|
101
|
+
/** Test only: shut down and forget the singleton so the next start() begins fresh. */
|
|
102
|
+
export declare function resetForTests(): Promise<void>;
|
|
103
|
+
export declare const notStartedError: (state: StartState) => FireweaveError;
|
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.notStartedError = void 0;
|
|
4
|
+
exports.slot = slot;
|
|
5
|
+
exports.warnOnce = warnOnce;
|
|
6
|
+
exports.errorOnce = errorOnce;
|
|
7
|
+
exports.setState = setState;
|
|
8
|
+
exports.injectedConfig = injectedConfig;
|
|
9
|
+
exports.start = start;
|
|
10
|
+
exports.currentStatus = currentStatus;
|
|
11
|
+
exports.setPersistenceFor = setPersistenceFor;
|
|
12
|
+
exports.rememberUser = rememberUser;
|
|
13
|
+
exports.shutdownSlot = shutdownSlot;
|
|
14
|
+
exports.resetForTests = resetForTests;
|
|
15
|
+
/**
|
|
16
|
+
* start() and the page-wide singleton behind `fw`.
|
|
17
|
+
*
|
|
18
|
+
* One slot per page (or worker) at a Symbol.for key on globalThis, so HMR
|
|
19
|
+
* re-evaluation and two bundled copies of this package share one client.
|
|
20
|
+
*
|
|
21
|
+
* Nothing here throws or rejects. A browser that fails to start must still
|
|
22
|
+
* render: a configuration fault logs one console.error, sets the state to
|
|
23
|
+
* FAILED and `problem`, and every read serves its default. The build helpers
|
|
24
|
+
* (`/vite`, `/define`) are where a bad configuration fails loudly.
|
|
25
|
+
*/
|
|
26
|
+
const index_js_1 = require("../index.js");
|
|
27
|
+
const build_info_js_1 = require("./build-info.js");
|
|
28
|
+
const control_points_js_1 = require("./control-points.js");
|
|
29
|
+
const identity_js_1 = require("./identity.js");
|
|
30
|
+
const names_js_1 = require("./names.js");
|
|
31
|
+
const policy_js_1 = require("./policy.js");
|
|
32
|
+
const SLOT_KEY = Symbol.for('@fireweaveai/web-sdk/start');
|
|
33
|
+
let protocolWarned = false;
|
|
34
|
+
const defaultLog = (line) => {
|
|
35
|
+
if (line.startsWith('[fireweave:local]'))
|
|
36
|
+
console.info(line);
|
|
37
|
+
else
|
|
38
|
+
console.warn(line);
|
|
39
|
+
};
|
|
40
|
+
const freshSlot = () => ({
|
|
41
|
+
protocol: 1,
|
|
42
|
+
state: 'NOT_STARTED',
|
|
43
|
+
generation: 0,
|
|
44
|
+
controlPoints: Object.freeze({}),
|
|
45
|
+
persistence: 'localStorage',
|
|
46
|
+
appDeviceId: false,
|
|
47
|
+
ready: Promise.resolve(),
|
|
48
|
+
chain: Promise.resolve(),
|
|
49
|
+
log: defaultLog,
|
|
50
|
+
warned: new Set(),
|
|
51
|
+
listeners: new Set(),
|
|
52
|
+
});
|
|
53
|
+
function slot() {
|
|
54
|
+
const g = globalThis;
|
|
55
|
+
const existing = g[SLOT_KEY];
|
|
56
|
+
if (existing !== undefined) {
|
|
57
|
+
if (existing.protocol !== 1 && !protocolWarned) {
|
|
58
|
+
protocolWarned = true;
|
|
59
|
+
console.warn('[fireweave] Two incompatible copies of @fireweaveai/web-sdk are loaded. Deduplicate the dependency.');
|
|
60
|
+
}
|
|
61
|
+
return existing;
|
|
62
|
+
}
|
|
63
|
+
const created = freshSlot();
|
|
64
|
+
g[SLOT_KEY] = created;
|
|
65
|
+
return created;
|
|
66
|
+
}
|
|
67
|
+
function warnOnce(s, line) {
|
|
68
|
+
if (s.warned.has(line))
|
|
69
|
+
return;
|
|
70
|
+
s.warned.add(line);
|
|
71
|
+
s.log(line);
|
|
72
|
+
}
|
|
73
|
+
/** console.error once per line. Errors bypass the log sink so they always reach the console. */
|
|
74
|
+
function errorOnce(s, line) {
|
|
75
|
+
if (s.warned.has(line))
|
|
76
|
+
return;
|
|
77
|
+
s.warned.add(line);
|
|
78
|
+
console.error(line);
|
|
79
|
+
}
|
|
80
|
+
/** Change state and notify. `always` re-notifies an unchanged state (a re-prefetch after identify). */
|
|
81
|
+
function setState(s, next, always = false) {
|
|
82
|
+
if (s.state === next && !always)
|
|
83
|
+
return;
|
|
84
|
+
s.state = next;
|
|
85
|
+
for (const listener of s.listeners) {
|
|
86
|
+
try {
|
|
87
|
+
listener(next);
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
// a listener's bug must not break the SDK
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const fromLifecycle = (state) => (state === 'UNINITIALIZED' ? 'INITIALIZING' : state);
|
|
95
|
+
/** What the fireweave() build plugin or fireweaveDefine() injected, if anything. */
|
|
96
|
+
function injectedConfig() {
|
|
97
|
+
let raw;
|
|
98
|
+
try {
|
|
99
|
+
raw = typeof __FIREWEAVE_WEB_CONFIG__ !== 'undefined' ? __FIREWEAVE_WEB_CONFIG__ : undefined;
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
raw = undefined;
|
|
103
|
+
}
|
|
104
|
+
if (typeof raw !== 'object' || raw === null || raw.v !== 1)
|
|
105
|
+
return undefined;
|
|
106
|
+
return raw;
|
|
107
|
+
}
|
|
108
|
+
const hasDom = () => typeof window !== 'undefined' && typeof document !== 'undefined';
|
|
109
|
+
/** Sorted-key JSON, so the fingerprint does not depend on property order. */
|
|
110
|
+
function stableJson(value) {
|
|
111
|
+
if (Array.isArray(value))
|
|
112
|
+
return `[${value.map(stableJson).join(',')}]`;
|
|
113
|
+
if (value !== null && typeof value === 'object') {
|
|
114
|
+
const entries = Object.entries(value)
|
|
115
|
+
.filter(([, v]) => v !== undefined)
|
|
116
|
+
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
|
|
117
|
+
return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${stableJson(v)}`).join(',')}}`;
|
|
118
|
+
}
|
|
119
|
+
return JSON.stringify(value ?? null);
|
|
120
|
+
}
|
|
121
|
+
function fingerprintOf(config, controlPoints, deviceId) {
|
|
122
|
+
return stableJson({
|
|
123
|
+
mode: config.mode,
|
|
124
|
+
url: config.url,
|
|
125
|
+
key: config.key,
|
|
126
|
+
allowedHosts: config.allowedHosts,
|
|
127
|
+
deviceId,
|
|
128
|
+
// Local values only matter in local mode.
|
|
129
|
+
controlPoints: config.mode === 'local' ? (0, control_points_js_1.toLocalControlPoints)(controlPoints) : undefined,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
const isActive = (state) => state === 'INITIALIZING' || state === 'READY' || state === 'STALE' || state === 'ERROR';
|
|
133
|
+
function fail(s, problem, message) {
|
|
134
|
+
// A bad repeat start() must not take down the client that is already running.
|
|
135
|
+
if (isActive(s.state)) {
|
|
136
|
+
errorOnce(s, `${message} Keeping the running configuration.`);
|
|
137
|
+
return s.ready;
|
|
138
|
+
}
|
|
139
|
+
s.problem = problem;
|
|
140
|
+
errorOnce(s, `${message} FireWeave is not running; reads serve their defaults.`);
|
|
141
|
+
setState(s, 'FAILED');
|
|
142
|
+
s.ready = Promise.resolve();
|
|
143
|
+
return s.ready;
|
|
144
|
+
}
|
|
145
|
+
/** Same-origin proxy paths ('/fw') resolve against the page origin. */
|
|
146
|
+
function absolutize(config) {
|
|
147
|
+
if (config.url === undefined || !config.url.startsWith('/'))
|
|
148
|
+
return config;
|
|
149
|
+
const origin = globalThis.location.origin;
|
|
150
|
+
return { ...config, url: `${origin}${config.url}`, allowedHosts: [globalThis.location.hostname, ...names_js_1.LOOPBACK_HOSTS] };
|
|
151
|
+
}
|
|
152
|
+
function localLine(config, controlPoints) {
|
|
153
|
+
const why = config.modeSource === 'option'
|
|
154
|
+
? "mode 'local'"
|
|
155
|
+
: `no key; environment '${config.environment ?? ''}' from ${config.environmentSource ?? 'the build config'}`;
|
|
156
|
+
const n = Object.keys(controlPoints).length;
|
|
157
|
+
return `[fireweave:local] Local mode (${why}). Serving ${n} control point${n === 1 ? '' : 's'} from your control-points object; nothing is sent to fw-server.`;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Wrap the transport so faults the core folds into STALE keep their cause:
|
|
161
|
+
* a 401/403 means the key was refused, a rejected fetch means fw-server was
|
|
162
|
+
* not reachable from this page.
|
|
163
|
+
*/
|
|
164
|
+
function observedFetch(s, generation, base, config) {
|
|
165
|
+
const host = config.url !== undefined ? new URL(config.url).host : 'fw-server';
|
|
166
|
+
const wrapped = async (input, init) => {
|
|
167
|
+
try {
|
|
168
|
+
const response = await base(input, init);
|
|
169
|
+
if (s.generation === generation) {
|
|
170
|
+
if (response.status === 401 || response.status === 403) {
|
|
171
|
+
s.problem = { reason: 'key-rejected', variable: config.keySource };
|
|
172
|
+
errorOnce(s, `[fireweave] fw-server refused the browser key from ${config.keySource} (HTTP ${response.status}). Check the key is an active browser key (fw_public_…) for this project. Reads serve their defaults.`);
|
|
173
|
+
}
|
|
174
|
+
else if (response.ok && (s.problem?.reason === 'key-rejected' || s.problem?.reason === 'unreachable')) {
|
|
175
|
+
s.problem = undefined;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return response;
|
|
179
|
+
}
|
|
180
|
+
catch (err) {
|
|
181
|
+
if (s.generation === generation) {
|
|
182
|
+
s.problem = { reason: 'unreachable' };
|
|
183
|
+
warnOnce(s, `[fireweave] Could not reach fw-server at ${host}: offline, an ad or tracker blocker, a firewall, or a Content-Security-Policy connect-src rule. Reads serve their defaults. A same-origin proxy set through FIREWEAVE_URL avoids most of these.`);
|
|
184
|
+
}
|
|
185
|
+
throw err;
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
return wrapped;
|
|
189
|
+
}
|
|
190
|
+
function registerDevice(s, generation) {
|
|
191
|
+
const { client, identity, config } = s;
|
|
192
|
+
if (client === undefined || identity === undefined || config?.mode !== 'remote' || !identity.durable)
|
|
193
|
+
return;
|
|
194
|
+
if ((0, identity_js_1.readItem)(names_js_1.STORAGE_KEYS.deviceRegistered) === identity.deviceId)
|
|
195
|
+
return;
|
|
196
|
+
void client.registerTarget(identity.deviceId, { kind: 'device' }).then((result) => {
|
|
197
|
+
if (result.ok && s.generation === generation && s.persistence === 'localStorage') {
|
|
198
|
+
(0, identity_js_1.writeItem)(names_js_1.STORAGE_KEYS.deviceRegistered, identity.deviceId);
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Start FireWeave for this page. Call once, from src/fireweave/start.ts.
|
|
204
|
+
*
|
|
205
|
+
* Resolves when the first prefetch settles (READY, STALE or ERROR) and never
|
|
206
|
+
* rejects, so `await start(...)` cannot blank a page. Check `fw.status()` for
|
|
207
|
+
* the outcome.
|
|
208
|
+
*/
|
|
209
|
+
function start(options = {}) {
|
|
210
|
+
const s = slot();
|
|
211
|
+
if (options.log !== undefined && (s.state === 'NOT_STARTED' || s.state === 'FAILED' || s.state === 'SHUTDOWN')) {
|
|
212
|
+
s.log = options.log;
|
|
213
|
+
}
|
|
214
|
+
let controlPoints;
|
|
215
|
+
try {
|
|
216
|
+
controlPoints = (0, control_points_js_1.normalizeControlPoints)(options.controlPoints);
|
|
217
|
+
}
|
|
218
|
+
catch (err) {
|
|
219
|
+
return fail(s, { reason: 'invalid-control-points', variable: 'controlPoints' }, err.message);
|
|
220
|
+
}
|
|
221
|
+
const injected = injectedConfig();
|
|
222
|
+
const policy = (0, policy_js_1.resolvePolicy)({
|
|
223
|
+
mode: options.mode,
|
|
224
|
+
key: (0, policy_js_1.firstOf)((0, policy_js_1.sourced)(options.key, 'start({ key })'), (0, policy_js_1.sourced)(injected?.key, injected?.keySource ?? names_js_1.BUILD_ENV.key)),
|
|
225
|
+
url: (0, policy_js_1.firstOf)((0, policy_js_1.sourced)(options.url, 'start({ url })'), (0, policy_js_1.sourced)(injected?.url, injected?.urlSource ?? names_js_1.BUILD_ENV.url)),
|
|
226
|
+
environment: (0, policy_js_1.firstOf)((0, policy_js_1.sourced)(options.environment, 'start({ environment })'), (0, policy_js_1.sourced)(injected?.environment, injected?.environmentSource ?? names_js_1.BUILD_ENV.environment)),
|
|
227
|
+
channel: build_info_js_1.SDK_CHANNEL,
|
|
228
|
+
keyVariable: injected !== undefined ? names_js_1.BUILD_ENV.key : `the browser key (start({ key }), or ${names_js_1.BUILD_ENV.key} through the fireweave() build plugin)`,
|
|
229
|
+
environmentChecked: 'start({ environment }) and the build config',
|
|
230
|
+
});
|
|
231
|
+
if (!policy.ok)
|
|
232
|
+
return fail(s, { reason: policy.reason, variable: policy.variable }, policy.message);
|
|
233
|
+
// Remote mode without a DOM (SSR, a worker, a DOM-less test) does nothing:
|
|
234
|
+
// a server-side singleton would share one visitor's identity across requests.
|
|
235
|
+
if (policy.config.mode === 'remote' && !hasDom())
|
|
236
|
+
return Promise.resolve();
|
|
237
|
+
const config = absolutize(policy.config);
|
|
238
|
+
const fingerprint = fingerprintOf(config, controlPoints, options.deviceId);
|
|
239
|
+
if (isActive(s.state)) {
|
|
240
|
+
if (fingerprint !== s.fingerprint) {
|
|
241
|
+
errorOnce(s, '[fireweave] start() was already called with a different configuration; keeping the first one. Call start() once, from src/fireweave/start.ts.');
|
|
242
|
+
}
|
|
243
|
+
return s.ready;
|
|
244
|
+
}
|
|
245
|
+
const generation = s.generation + 1;
|
|
246
|
+
s.generation = generation;
|
|
247
|
+
s.fingerprint = fingerprint;
|
|
248
|
+
s.config = config;
|
|
249
|
+
s.controlPoints = controlPoints;
|
|
250
|
+
s.problem = undefined;
|
|
251
|
+
s.client = undefined;
|
|
252
|
+
s.persistence = options.persistence === 'memory' ? 'memory' : 'localStorage';
|
|
253
|
+
s.appDeviceId = options.deviceId !== undefined;
|
|
254
|
+
s.chain = Promise.resolve();
|
|
255
|
+
for (const w of policy.warnings)
|
|
256
|
+
warnOnce(s, w);
|
|
257
|
+
// Local mode stores nothing and registers nothing: the id lives for this page only.
|
|
258
|
+
const identity = config.mode === 'local'
|
|
259
|
+
? { deviceId: options.deviceId ?? (0, identity_js_1.mintDeviceId)(), durable: false }
|
|
260
|
+
: (0, identity_js_1.loadIdentity)(s.persistence, options.deviceId);
|
|
261
|
+
s.identity = identity;
|
|
262
|
+
s.currentKey = identity.user ?? identity.deviceId;
|
|
263
|
+
if (config.mode === 'local')
|
|
264
|
+
s.log(localLine(config, controlPoints));
|
|
265
|
+
setState(s, 'INITIALIZING');
|
|
266
|
+
const base = options.fetch ?? globalThis.fetch?.bind(globalThis);
|
|
267
|
+
const init = config.mode === 'local'
|
|
268
|
+
? (0, index_js_1.initFireweave)({
|
|
269
|
+
mode: 'local',
|
|
270
|
+
local: { controlPoints: (0, control_points_js_1.toLocalControlPoints)(controlPoints), log: (line) => s.log(line) },
|
|
271
|
+
context: { targetingKey: s.currentKey },
|
|
272
|
+
})
|
|
273
|
+
: (0, index_js_1.initFireweave)({
|
|
274
|
+
mode: 'remote',
|
|
275
|
+
apiKey: config.key,
|
|
276
|
+
apiUrl: config.url,
|
|
277
|
+
...(config.allowedHosts !== undefined ? { allowedHosts: config.allowedHosts } : {}),
|
|
278
|
+
...(base !== undefined ? { fetch: observedFetch(s, generation, base, config) } : {}),
|
|
279
|
+
context: { targetingKey: s.currentKey },
|
|
280
|
+
});
|
|
281
|
+
s.ready = init.then((client) => {
|
|
282
|
+
if (s.generation !== generation) {
|
|
283
|
+
void client.shutdown();
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
s.client = client;
|
|
287
|
+
s.detachRuntime = client.runtime.onStateChange((state) => {
|
|
288
|
+
if (s.generation === generation)
|
|
289
|
+
setState(s, fromLifecycle(state), true);
|
|
290
|
+
});
|
|
291
|
+
setState(s, fromLifecycle(client.runtime.getState()));
|
|
292
|
+
registerDevice(s, generation);
|
|
293
|
+
}, () => {
|
|
294
|
+
if (s.generation !== generation)
|
|
295
|
+
return;
|
|
296
|
+
s.problem = { reason: 'start-failed' };
|
|
297
|
+
errorOnce(s, '[fireweave] start failed: the core rejected the configuration. FireWeave is not running; reads serve their defaults.');
|
|
298
|
+
setState(s, 'FAILED');
|
|
299
|
+
});
|
|
300
|
+
return s.ready;
|
|
301
|
+
}
|
|
302
|
+
function currentStatus() {
|
|
303
|
+
const s = slot();
|
|
304
|
+
const c = s.config;
|
|
305
|
+
const active = s.state !== 'NOT_STARTED' && s.state !== 'FAILED';
|
|
306
|
+
return {
|
|
307
|
+
state: s.state,
|
|
308
|
+
channel: build_info_js_1.SDK_CHANNEL,
|
|
309
|
+
sdkVersion: build_info_js_1.SDK_VERSION,
|
|
310
|
+
...(c !== undefined && active
|
|
311
|
+
? {
|
|
312
|
+
mode: c.mode,
|
|
313
|
+
modeSource: c.modeSource,
|
|
314
|
+
keySource: c.keySource,
|
|
315
|
+
controlPointCount: Object.keys(s.controlPoints).length,
|
|
316
|
+
...(c.url !== undefined && c.urlSource !== undefined ? { host: new URL(c.url).hostname, endpointSource: c.urlSource } : {}),
|
|
317
|
+
...(c.environment !== undefined ? { environment: c.environment } : {}),
|
|
318
|
+
}
|
|
319
|
+
: {}),
|
|
320
|
+
...(s.problem !== undefined ? { problem: s.problem } : {}),
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
/** Switch storage at runtime, for consent. Never part of the start() configuration check. */
|
|
324
|
+
function setPersistenceFor(mode) {
|
|
325
|
+
const s = slot();
|
|
326
|
+
if (mode !== 'localStorage' && mode !== 'memory')
|
|
327
|
+
return;
|
|
328
|
+
const previous = s.persistence;
|
|
329
|
+
s.persistence = mode;
|
|
330
|
+
if (s.identity === undefined || s.config?.mode !== 'remote' || previous === mode)
|
|
331
|
+
return;
|
|
332
|
+
if (mode === 'memory') {
|
|
333
|
+
(0, identity_js_1.clearStoredIdentity)();
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
const user = s.currentKey !== s.identity.deviceId ? s.currentKey : undefined;
|
|
337
|
+
s.identity.durable = (0, identity_js_1.persistIdentity)(s.identity, user, s.appDeviceId);
|
|
338
|
+
registerDevice(s, s.generation);
|
|
339
|
+
}
|
|
340
|
+
/** Store (or clear) the signed-in key, when persistence allows it. */
|
|
341
|
+
function rememberUser(s, user) {
|
|
342
|
+
if (s.config?.mode !== 'remote' || s.persistence !== 'localStorage')
|
|
343
|
+
return;
|
|
344
|
+
if (user === undefined)
|
|
345
|
+
(0, identity_js_1.removeItem)(names_js_1.STORAGE_KEYS.identity);
|
|
346
|
+
else
|
|
347
|
+
(0, identity_js_1.writeItem)(names_js_1.STORAGE_KEYS.identity, user);
|
|
348
|
+
}
|
|
349
|
+
async function shutdownSlot() {
|
|
350
|
+
const s = slot();
|
|
351
|
+
await s.ready;
|
|
352
|
+
const client = s.client;
|
|
353
|
+
s.generation += 1;
|
|
354
|
+
s.detachRuntime?.();
|
|
355
|
+
s.client = undefined;
|
|
356
|
+
s.fingerprint = undefined;
|
|
357
|
+
s.ready = Promise.resolve();
|
|
358
|
+
setState(s, 'SHUTDOWN');
|
|
359
|
+
if (client !== undefined)
|
|
360
|
+
await client.shutdown();
|
|
361
|
+
}
|
|
362
|
+
/** Test only: shut down and forget the singleton so the next start() begins fresh. */
|
|
363
|
+
async function resetForTests() {
|
|
364
|
+
const g = globalThis;
|
|
365
|
+
const s = g[SLOT_KEY];
|
|
366
|
+
if (s !== undefined) {
|
|
367
|
+
s.generation += 1;
|
|
368
|
+
s.detachRuntime?.();
|
|
369
|
+
if (s.client !== undefined)
|
|
370
|
+
await s.client.shutdown().catch(() => undefined);
|
|
371
|
+
}
|
|
372
|
+
delete g[SLOT_KEY];
|
|
373
|
+
}
|
|
374
|
+
const notStartedError = (state) => new index_js_1.FireweaveError(state === 'FAILED' ? 'Configuration' : state === 'SHUTDOWN' ? 'AlreadyClosed' : 'NotReady');
|
|
375
|
+
exports.notStartedError = notStartedError;
|
package/package.json
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@fireweaveai/web-sdk-cjs",
|
|
3
|
+
"version": "0.0.0-reserve.0",
|
|
4
|
+
"description": "CommonJS build of @fireweaveai/web-sdk, for require() and toolchains without ESM support. Fireweave SDK for browsers: control points and target registration, in remote (fw-server) or local (in-process, no network) mode. Holds no secrets.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/FireWeave-HQ/fireweave-sdk.git",
|
|
9
|
+
"directory": "sdks/web"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/FireWeave-HQ/fireweave-sdk/tree/master/sdks/web#readme",
|
|
12
|
+
"bugs": "https://github.com/FireWeave-HQ/fireweave-sdk/issues",
|
|
13
|
+
"sideEffects": false,
|
|
14
|
+
"type": "commonjs",
|
|
15
|
+
"main": "./dist/index.js",
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"typesVersions": {
|
|
18
|
+
"*": {
|
|
19
|
+
"start": [
|
|
20
|
+
"./dist/start/index.d.ts"
|
|
21
|
+
],
|
|
22
|
+
"vite": [
|
|
23
|
+
"./dist/build/node/vite.d.ts"
|
|
24
|
+
],
|
|
25
|
+
"define": [
|
|
26
|
+
"./dist/build/node/define.d.ts"
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"exports": {
|
|
31
|
+
".": {
|
|
32
|
+
"types": "./dist/index.d.ts",
|
|
33
|
+
"default": "./dist/index.js"
|
|
34
|
+
},
|
|
35
|
+
"./start": {
|
|
36
|
+
"types": "./dist/start/index.d.ts",
|
|
37
|
+
"default": "./dist/start/index.js"
|
|
38
|
+
},
|
|
39
|
+
"./vite": {
|
|
40
|
+
"types": "./dist/build/node/vite.d.ts",
|
|
41
|
+
"default": "./dist/build/node/vite.js"
|
|
42
|
+
},
|
|
43
|
+
"./define": {
|
|
44
|
+
"types": "./dist/build/node/define.d.ts",
|
|
45
|
+
"default": "./dist/build/node/define.js"
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"files": [
|
|
49
|
+
"dist"
|
|
50
|
+
],
|
|
51
|
+
"peerDependencies": {
|
|
52
|
+
"vite": ">=5.0.0"
|
|
53
|
+
},
|
|
54
|
+
"peerDependenciesMeta": {
|
|
55
|
+
"vite": {
|
|
56
|
+
"optional": true
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"keywords": [
|
|
60
|
+
"fireweave",
|
|
61
|
+
"release-engineering",
|
|
62
|
+
"progressive-delivery",
|
|
63
|
+
"control-points",
|
|
64
|
+
"feature-flags",
|
|
65
|
+
"browser",
|
|
66
|
+
"web"
|
|
67
|
+
]
|
|
68
|
+
}
|