@shipeasy/sdk 5.3.0 → 6.0.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/README.md +106 -27
- package/dist/client/index.d.mts +90 -18
- package/dist/client/index.d.ts +90 -18
- package/dist/client/index.js +100 -11
- package/dist/client/index.mjs +96 -10
- package/dist/openfeature-server/index.d.mts +19 -19
- package/dist/openfeature-server/index.d.ts +19 -19
- package/dist/openfeature-web/index.d.mts +12 -12
- package/dist/openfeature-web/index.d.ts +12 -12
- package/dist/server/index.d.mts +134 -45
- package/dist/server/index.d.ts +134 -45
- package/dist/server/index.js +113 -45
- package/dist/server/index.mjs +107 -43
- package/package.json +1 -1
package/dist/server/index.mjs
CHANGED
|
@@ -499,7 +499,7 @@ function parseOverrides(rawUrl) {
|
|
|
499
499
|
}
|
|
500
500
|
return { gates, configs, experiments };
|
|
501
501
|
}
|
|
502
|
-
var
|
|
502
|
+
var Engine = class _Engine {
|
|
503
503
|
apiKey;
|
|
504
504
|
baseUrl;
|
|
505
505
|
env;
|
|
@@ -514,7 +514,7 @@ var FlagsClient = class _FlagsClient {
|
|
|
514
514
|
pollInterval = 30;
|
|
515
515
|
timer = null;
|
|
516
516
|
initialized = false;
|
|
517
|
-
// Test mode: built by `
|
|
517
|
+
// Test mode: built by `Engine.forTesting()`. When set, init()/initOnce()
|
|
518
518
|
// never fetch, track() is a no-op, and telemetry is off — the client is a
|
|
519
519
|
// fully self-contained, network-free seam for unit tests.
|
|
520
520
|
testMode;
|
|
@@ -555,13 +555,13 @@ var FlagsClient = class _FlagsClient {
|
|
|
555
555
|
* with overrideFlag/overrideConfig/overrideExperiment. No SDK key required.
|
|
556
556
|
*
|
|
557
557
|
* ```ts
|
|
558
|
-
* const client =
|
|
558
|
+
* const client = Engine.forTesting();
|
|
559
559
|
* client.overrideFlag("new_checkout", true);
|
|
560
560
|
* client.getFlag("new_checkout", { user_id: "u1" }); // true
|
|
561
561
|
* ```
|
|
562
562
|
*/
|
|
563
563
|
static forTesting(opts) {
|
|
564
|
-
return new
|
|
564
|
+
return new _Engine({ apiKey: "", ...opts, testMode: true });
|
|
565
565
|
}
|
|
566
566
|
/**
|
|
567
567
|
* Build a fully OFFLINE client from a pre-captured snapshot — no network ever.
|
|
@@ -574,7 +574,7 @@ var FlagsClient = class _FlagsClient {
|
|
|
574
574
|
* `{ flags: <GET /sdk/flags body>, experiments: <GET /sdk/experiments body> }`.
|
|
575
575
|
*/
|
|
576
576
|
static fromSnapshot(snapshot) {
|
|
577
|
-
const client = new
|
|
577
|
+
const client = new _Engine({ apiKey: "", testMode: true });
|
|
578
578
|
client.flagsBlob = snapshot.flags;
|
|
579
579
|
client.expsBlob = snapshot.experiments;
|
|
580
580
|
client.initialized = true;
|
|
@@ -584,13 +584,13 @@ var FlagsClient = class _FlagsClient {
|
|
|
584
584
|
* Build a fully OFFLINE client from a snapshot JSON file on disk (Node only —
|
|
585
585
|
* not available in the browser entrypoint). The file must contain
|
|
586
586
|
* `{ "flags": <GET /sdk/flags body>, "experiments": <GET /sdk/experiments body> }`.
|
|
587
|
-
* See {@link
|
|
587
|
+
* See {@link Engine.fromSnapshot}.
|
|
588
588
|
*/
|
|
589
589
|
static fromFile(path) {
|
|
590
590
|
const fs = __require("fs");
|
|
591
591
|
const raw = fs.readFileSync(path, "utf8");
|
|
592
592
|
const snapshot = JSON.parse(raw);
|
|
593
|
-
return
|
|
593
|
+
return _Engine.fromSnapshot(snapshot);
|
|
594
594
|
}
|
|
595
595
|
async init() {
|
|
596
596
|
if (this.testMode) {
|
|
@@ -1050,7 +1050,7 @@ async function fetchLabelsForSSR(opts) {
|
|
|
1050
1050
|
var _server = null;
|
|
1051
1051
|
function configureShipeasyServer(opts) {
|
|
1052
1052
|
if (_server) return _server;
|
|
1053
|
-
_server = new
|
|
1053
|
+
_server = new Engine(opts);
|
|
1054
1054
|
return _server;
|
|
1055
1055
|
}
|
|
1056
1056
|
function getShipeasyServerClient() {
|
|
@@ -1118,46 +1118,62 @@ async function shipeasy(opts) {
|
|
|
1118
1118
|
flags: bootstrap.flags,
|
|
1119
1119
|
configs: bootstrap.configs,
|
|
1120
1120
|
experiments: bootstrap.experiments,
|
|
1121
|
-
|
|
1122
|
-
return
|
|
1123
|
-
editLabels,
|
|
1121
|
+
getBootstrapData(emit) {
|
|
1122
|
+
return getBootstrapData(bootstrap, i18nData, {
|
|
1124
1123
|
i18nProfile: profile,
|
|
1125
|
-
anonId
|
|
1124
|
+
anonId,
|
|
1125
|
+
...emit
|
|
1126
|
+
});
|
|
1127
|
+
},
|
|
1128
|
+
getBootstrapTags(emit) {
|
|
1129
|
+
return getBootstrapTags(bootstrap, i18nData, {
|
|
1130
|
+
i18nProfile: profile,
|
|
1131
|
+
anonId,
|
|
1132
|
+
...emit
|
|
1126
1133
|
});
|
|
1127
1134
|
}
|
|
1128
1135
|
};
|
|
1129
1136
|
}
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
const
|
|
1137
|
+
var DEFAULT_CDN = "https://cdn.shipeasy.ai";
|
|
1138
|
+
function getBootstrapData(bootstrap, i18nData, opts) {
|
|
1139
|
+
const base = (opts.baseUrl ?? DEFAULT_CDN).replace(/\/$/, "");
|
|
1133
1140
|
const profile = opts.i18nProfile ?? "en:prod";
|
|
1134
|
-
const
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1141
|
+
const attrs = {
|
|
1142
|
+
"data-se-bootstrap": "",
|
|
1143
|
+
"data-flags": JSON.stringify(bootstrap?.flags ?? {}),
|
|
1144
|
+
"data-configs": JSON.stringify(bootstrap?.configs ?? {}),
|
|
1145
|
+
"data-experiments": JSON.stringify(bootstrap?.experiments ?? {}),
|
|
1146
|
+
"data-killswitches": JSON.stringify(bootstrap?.killswitches ?? {}),
|
|
1147
|
+
"data-i18n-profile": profile,
|
|
1148
|
+
"data-api-url": base
|
|
1142
1149
|
};
|
|
1143
|
-
if (
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
if (i18nData?.strings && Object.keys(i18nData.strings).length > 0) {
|
|
1156
|
-
parts.push(
|
|
1157
|
-
`(function(){var d=window.__SE_BOOTSTRAP.i18n;if(!d)return;window.i18n={locale:d.locale,t:function(k,v){var r=d.strings[k];if(!r)return k;return v?r.replace(/\\{\\{(\\w+)\\}\\}/g,function(_,p){return v[p]!==undefined?String(v[p]):'{{'+p+'}}'}):r;},on:function(){return function(){};}};})();`
|
|
1158
|
-
);
|
|
1150
|
+
if (opts.anonId) attrs["data-anon-id"] = opts.anonId;
|
|
1151
|
+
const bootstrapTag = { src: `${base}/sdk/bootstrap.js`, attrs };
|
|
1152
|
+
let i18nLoader = null;
|
|
1153
|
+
const hasStrings = !!(i18nData?.strings && Object.keys(i18nData.strings).length > 0);
|
|
1154
|
+
if (hasStrings || opts.clientKey) {
|
|
1155
|
+
const i18nAttrs = { "data-profile": profile };
|
|
1156
|
+
if (opts.clientKey) i18nAttrs["data-key"] = opts.clientKey;
|
|
1157
|
+
if (hasStrings) {
|
|
1158
|
+
i18nAttrs["data-strings"] = JSON.stringify(i18nData.strings);
|
|
1159
|
+
i18nAttrs["data-locale"] = i18nData.locale;
|
|
1160
|
+
}
|
|
1161
|
+
i18nLoader = { src: `${base}/sdk/i18n/loader.js`, attrs: i18nAttrs };
|
|
1159
1162
|
}
|
|
1160
|
-
return
|
|
1163
|
+
return { bootstrap: bootstrapTag, i18nLoader };
|
|
1164
|
+
}
|
|
1165
|
+
function escapeAttr(v) {
|
|
1166
|
+
return v.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
1167
|
+
}
|
|
1168
|
+
function renderScriptTag(spec) {
|
|
1169
|
+
const attrs = Object.entries(spec.attrs).map(([k, v]) => v === "" ? ` ${k}` : ` ${k}="${escapeAttr(v)}"`).join("");
|
|
1170
|
+
return `<script src="${escapeAttr(spec.src)}"${attrs}></script>`;
|
|
1171
|
+
}
|
|
1172
|
+
function getBootstrapTags(bootstrap, i18nData, opts) {
|
|
1173
|
+
const data = getBootstrapData(bootstrap, i18nData, opts);
|
|
1174
|
+
const tags = [data.bootstrap];
|
|
1175
|
+
if (data.i18nLoader) tags.push(data.i18nLoader);
|
|
1176
|
+
return tags.map(renderScriptTag).join("");
|
|
1161
1177
|
}
|
|
1162
1178
|
var flags = {
|
|
1163
1179
|
configure(opts) {
|
|
@@ -1209,7 +1225,7 @@ var flags = {
|
|
|
1209
1225
|
_server?.track(userId, eventName, props);
|
|
1210
1226
|
},
|
|
1211
1227
|
/** Emit an exposure for an enrolled experiment at the decision point. See
|
|
1212
|
-
* {@link
|
|
1228
|
+
* {@link Engine.logExposure}. No-op before configure(). */
|
|
1213
1229
|
logExposure(user, name) {
|
|
1214
1230
|
_server?.logExposure(user, name);
|
|
1215
1231
|
},
|
|
@@ -1227,6 +1243,50 @@ var flags = {
|
|
|
1227
1243
|
};
|
|
1228
1244
|
}
|
|
1229
1245
|
};
|
|
1246
|
+
var _identityAttributes = (user) => user && typeof user === "object" ? user : {};
|
|
1247
|
+
var _attributes = _identityAttributes;
|
|
1248
|
+
function configure(opts) {
|
|
1249
|
+
const { attributes, ...engineOpts } = opts;
|
|
1250
|
+
_attributes = attributes ?? _identityAttributes;
|
|
1251
|
+
const engine = configureShipeasyServer(engineOpts);
|
|
1252
|
+
void engine.initOnce().catch(() => {
|
|
1253
|
+
});
|
|
1254
|
+
return engine;
|
|
1255
|
+
}
|
|
1256
|
+
function _resetConfigureForTests() {
|
|
1257
|
+
_attributes = _identityAttributes;
|
|
1258
|
+
}
|
|
1259
|
+
var Client = class {
|
|
1260
|
+
engine;
|
|
1261
|
+
/** The resolved attribute bag this handle evaluates against. */
|
|
1262
|
+
attributes;
|
|
1263
|
+
constructor(user) {
|
|
1264
|
+
const engine = getShipeasyServerClient();
|
|
1265
|
+
if (!engine) {
|
|
1266
|
+
throw new Error(
|
|
1267
|
+
"[shipeasy] new Client(user) called before configure({ apiKey }). Call configure() once at app boot from @shipeasy/sdk/server."
|
|
1268
|
+
);
|
|
1269
|
+
}
|
|
1270
|
+
this.engine = engine;
|
|
1271
|
+
this.attributes = _attributes(user);
|
|
1272
|
+
}
|
|
1273
|
+
getFlag(name, defaultValue = false) {
|
|
1274
|
+
return this.engine.getFlag(name, this.attributes, defaultValue);
|
|
1275
|
+
}
|
|
1276
|
+
getFlagDetail(name) {
|
|
1277
|
+
return this.engine.getFlagDetail(name, this.attributes);
|
|
1278
|
+
}
|
|
1279
|
+
getConfig(name, decodeOrOpts) {
|
|
1280
|
+
return this.engine.getConfig(name, decodeOrOpts);
|
|
1281
|
+
}
|
|
1282
|
+
getExperiment(name, defaultParams, decode) {
|
|
1283
|
+
return this.engine.getExperiment(name, this.attributes, defaultParams, decode);
|
|
1284
|
+
}
|
|
1285
|
+
/** Read a killswitch (not user-bound; mirrors {@link Engine.getKillswitch}). */
|
|
1286
|
+
getKillswitch(name, switchKey) {
|
|
1287
|
+
return this.engine.getKillswitch(name, switchKey);
|
|
1288
|
+
}
|
|
1289
|
+
};
|
|
1230
1290
|
function dispatchSee(problem, consequence, extras, kind) {
|
|
1231
1291
|
if (!_server) {
|
|
1232
1292
|
console.warn("[shipeasy] see() called before shipeasy({ serverKey }) \u2014 error dropped");
|
|
@@ -1243,15 +1303,19 @@ var see = Object.assign(
|
|
|
1243
1303
|
);
|
|
1244
1304
|
export {
|
|
1245
1305
|
ANON_ID_COOKIE,
|
|
1306
|
+
Client,
|
|
1307
|
+
Engine,
|
|
1246
1308
|
FLAG_REASONS,
|
|
1247
|
-
FlagsClient,
|
|
1248
1309
|
_murmur3ForTests,
|
|
1310
|
+
_resetConfigureForTests,
|
|
1249
1311
|
_resetShipeasyServerForTests,
|
|
1312
|
+
configure,
|
|
1250
1313
|
configureShipeasyServer,
|
|
1251
1314
|
createInMemoryStickyStore,
|
|
1252
1315
|
fetchLabelsForSSR,
|
|
1253
1316
|
flags,
|
|
1254
|
-
|
|
1317
|
+
getBootstrapData,
|
|
1318
|
+
getBootstrapTags,
|
|
1255
1319
|
getShipeasyServerClient,
|
|
1256
1320
|
i18n,
|
|
1257
1321
|
isExpected,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shipeasy/sdk",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.0",
|
|
4
4
|
"description": "Shipeasy SDK — feature gates, runtime configs, experiments, and metrics for the Shipeasy hosted service.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"homepage": "https://shipeasy.ai",
|