@shipeasy/sdk 2.5.2 → 3.0.1
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/dist/client/index.d.mts +18 -7
- package/dist/client/index.d.ts +18 -7
- package/dist/client/index.js +21 -8
- package/dist/client/index.mjs +21 -8
- package/dist/server/index.d.mts +18 -22
- package/dist/server/index.d.ts +18 -22
- package/dist/server/index.js +17 -21
- package/dist/server/index.mjs +17 -21
- package/package.json +1 -1
package/dist/client/index.d.mts
CHANGED
|
@@ -137,12 +137,23 @@ interface AttachDevtoolsOptions {
|
|
|
137
137
|
declare function attachDevtools(client: FlagsClientBrowser, opts?: AttachDevtoolsOptions): () => void;
|
|
138
138
|
/** Configure the singleton. Idempotent — re-calling with the same opts is a no-op. */
|
|
139
139
|
interface ShipeasyClientConfig {
|
|
140
|
-
/**
|
|
141
|
-
|
|
140
|
+
/**
|
|
141
|
+
* Public client key — the ONLY key the browser entrypoint accepts. Authenticates
|
|
142
|
+
* /sdk/evaluate, /collect and the runtime i18n loader (/sdk/i18n/strings). Safe to
|
|
143
|
+
* expose (e.g. NEXT_PUBLIC_ env vars). This is a different key from the server key
|
|
144
|
+
* passed to `shipeasy({ serverKey })` in @shipeasy/sdk/server — never use the
|
|
145
|
+
* server key here.
|
|
146
|
+
*/
|
|
147
|
+
clientKey: string;
|
|
142
148
|
/** Override the ShipEasy CDN/edge base URL. Defaults to https://cdn.shipeasy.ai. */
|
|
143
149
|
baseUrl?: string;
|
|
144
150
|
/** Override the admin URL for the devtools overlay (dev use). */
|
|
145
151
|
adminUrl?: string;
|
|
152
|
+
/**
|
|
153
|
+
* i18n profile for the runtime string loader, e.g. "en:prod". Defaults to the
|
|
154
|
+
* profile the server recorded in window.__SE_BOOTSTRAP, then "en:prod".
|
|
155
|
+
*/
|
|
156
|
+
i18nProfile?: string;
|
|
146
157
|
/**
|
|
147
158
|
* Skip the lazy auto-identify({}) at boot. Defaults to true (auto-identify on).
|
|
148
159
|
* Turn off when the host has its own identify orchestration and wants to
|
|
@@ -158,9 +169,9 @@ interface ShipeasyClientConfig {
|
|
|
158
169
|
* Pass `false` to disable everything, or a per-group object to narrow:
|
|
159
170
|
*
|
|
160
171
|
* ```ts
|
|
161
|
-
* shipeasy({
|
|
162
|
-
* shipeasy({
|
|
163
|
-
* shipeasy({
|
|
172
|
+
* shipeasy({ clientKey, autoCollect: false }); // off
|
|
173
|
+
* shipeasy({ clientKey, autoCollect: { errors: false } }); // vitals + engagement only
|
|
174
|
+
* shipeasy({ clientKey }); // all groups on
|
|
164
175
|
* ```
|
|
165
176
|
*/
|
|
166
177
|
autoCollect?: boolean | Partial<AutoCollectGroups>;
|
|
@@ -198,8 +209,8 @@ interface BootstrapPayload {
|
|
|
198
209
|
* the killswitch is not whole-killed and the map carries per-switch state.
|
|
199
210
|
*/
|
|
200
211
|
killswitches?: Record<string, boolean | Record<string, boolean>>;
|
|
201
|
-
/**
|
|
202
|
-
|
|
212
|
+
/** i18n profile the server rendered with, so the client loader matches. No key is embedded. */
|
|
213
|
+
i18nProfile?: string;
|
|
203
214
|
apiUrl?: string;
|
|
204
215
|
/** When true, tEl() returns marker-wrapped strings for devtools label editing. */
|
|
205
216
|
editLabels?: boolean;
|
package/dist/client/index.d.ts
CHANGED
|
@@ -137,12 +137,23 @@ interface AttachDevtoolsOptions {
|
|
|
137
137
|
declare function attachDevtools(client: FlagsClientBrowser, opts?: AttachDevtoolsOptions): () => void;
|
|
138
138
|
/** Configure the singleton. Idempotent — re-calling with the same opts is a no-op. */
|
|
139
139
|
interface ShipeasyClientConfig {
|
|
140
|
-
/**
|
|
141
|
-
|
|
140
|
+
/**
|
|
141
|
+
* Public client key — the ONLY key the browser entrypoint accepts. Authenticates
|
|
142
|
+
* /sdk/evaluate, /collect and the runtime i18n loader (/sdk/i18n/strings). Safe to
|
|
143
|
+
* expose (e.g. NEXT_PUBLIC_ env vars). This is a different key from the server key
|
|
144
|
+
* passed to `shipeasy({ serverKey })` in @shipeasy/sdk/server — never use the
|
|
145
|
+
* server key here.
|
|
146
|
+
*/
|
|
147
|
+
clientKey: string;
|
|
142
148
|
/** Override the ShipEasy CDN/edge base URL. Defaults to https://cdn.shipeasy.ai. */
|
|
143
149
|
baseUrl?: string;
|
|
144
150
|
/** Override the admin URL for the devtools overlay (dev use). */
|
|
145
151
|
adminUrl?: string;
|
|
152
|
+
/**
|
|
153
|
+
* i18n profile for the runtime string loader, e.g. "en:prod". Defaults to the
|
|
154
|
+
* profile the server recorded in window.__SE_BOOTSTRAP, then "en:prod".
|
|
155
|
+
*/
|
|
156
|
+
i18nProfile?: string;
|
|
146
157
|
/**
|
|
147
158
|
* Skip the lazy auto-identify({}) at boot. Defaults to true (auto-identify on).
|
|
148
159
|
* Turn off when the host has its own identify orchestration and wants to
|
|
@@ -158,9 +169,9 @@ interface ShipeasyClientConfig {
|
|
|
158
169
|
* Pass `false` to disable everything, or a per-group object to narrow:
|
|
159
170
|
*
|
|
160
171
|
* ```ts
|
|
161
|
-
* shipeasy({
|
|
162
|
-
* shipeasy({
|
|
163
|
-
* shipeasy({
|
|
172
|
+
* shipeasy({ clientKey, autoCollect: false }); // off
|
|
173
|
+
* shipeasy({ clientKey, autoCollect: { errors: false } }); // vitals + engagement only
|
|
174
|
+
* shipeasy({ clientKey }); // all groups on
|
|
164
175
|
* ```
|
|
165
176
|
*/
|
|
166
177
|
autoCollect?: boolean | Partial<AutoCollectGroups>;
|
|
@@ -198,8 +209,8 @@ interface BootstrapPayload {
|
|
|
198
209
|
* the killswitch is not whole-killed and the map carries per-switch state.
|
|
199
210
|
*/
|
|
200
211
|
killswitches?: Record<string, boolean | Record<string, boolean>>;
|
|
201
|
-
/**
|
|
202
|
-
|
|
212
|
+
/** i18n profile the server rendered with, so the client loader matches. No key is embedded. */
|
|
213
|
+
i18nProfile?: string;
|
|
203
214
|
apiUrl?: string;
|
|
204
215
|
/** When true, tEl() returns marker-wrapped strings for devtools label editing. */
|
|
205
216
|
editLabels?: boolean;
|
package/dist/client/index.js
CHANGED
|
@@ -711,12 +711,14 @@ function shipeasy(opts) {
|
|
|
711
711
|
const ac = opts.autoCollect;
|
|
712
712
|
const blanket = ac === false ? false : true;
|
|
713
713
|
const groups = ac && typeof ac === "object" ? ac : void 0;
|
|
714
|
+
const baseUrl = opts.baseUrl ?? "https://cdn.shipeasy.ai";
|
|
714
715
|
const client = configureShipeasy({
|
|
715
|
-
sdkKey: opts.
|
|
716
|
-
baseUrl
|
|
716
|
+
sdkKey: opts.clientKey,
|
|
717
|
+
baseUrl,
|
|
717
718
|
autoGuardrails: blanket,
|
|
718
719
|
autoGuardrailGroups: groups
|
|
719
720
|
});
|
|
721
|
+
injectI18nLoader(opts.clientKey, baseUrl, opts.i18nProfile);
|
|
720
722
|
flags.notifyMounted();
|
|
721
723
|
if (opts.autoIdentify !== false) {
|
|
722
724
|
void client.identify({}).catch((err) => {
|
|
@@ -736,6 +738,23 @@ function getShipeasyClient() {
|
|
|
736
738
|
function _resetShipeasyForTests() {
|
|
737
739
|
_client?.destroy();
|
|
738
740
|
_client = null;
|
|
741
|
+
_i18nLoaderInjected = false;
|
|
742
|
+
}
|
|
743
|
+
var _i18nLoaderInjected = false;
|
|
744
|
+
function injectI18nLoader(clientKey, baseUrl, profileOpt) {
|
|
745
|
+
if (_i18nLoaderInjected || typeof document === "undefined") return;
|
|
746
|
+
if (!clientKey || typeof document.createElement !== "function" || !document.head) return;
|
|
747
|
+
_i18nLoaderInjected = true;
|
|
748
|
+
try {
|
|
749
|
+
const bs = getBootstrap();
|
|
750
|
+
const profile = profileOpt ?? bs?.i18nProfile ?? "en:prod";
|
|
751
|
+
const s = document.createElement("script");
|
|
752
|
+
s.src = `${baseUrl}/sdk/i18n/loader.js`;
|
|
753
|
+
s.setAttribute("data-key", clientKey);
|
|
754
|
+
s.setAttribute("data-profile", profile);
|
|
755
|
+
document.head.appendChild(s);
|
|
756
|
+
} catch {
|
|
757
|
+
}
|
|
739
758
|
}
|
|
740
759
|
function getBootstrap() {
|
|
741
760
|
if (typeof window === "undefined") return null;
|
|
@@ -1090,12 +1109,6 @@ var i18n = {
|
|
|
1090
1109
|
};
|
|
1091
1110
|
}
|
|
1092
1111
|
};
|
|
1093
|
-
if (typeof window !== "undefined") {
|
|
1094
|
-
const _initBs = window.__SE_BOOTSTRAP;
|
|
1095
|
-
if (_initBs?.apiKey && !_client) {
|
|
1096
|
-
shipeasy({ apiKey: _initBs.apiKey, baseUrl: _initBs.apiUrl });
|
|
1097
|
-
}
|
|
1098
|
-
}
|
|
1099
1112
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1100
1113
|
0 && (module.exports = {
|
|
1101
1114
|
FlagsClientBrowser,
|
package/dist/client/index.mjs
CHANGED
|
@@ -668,12 +668,14 @@ function shipeasy(opts) {
|
|
|
668
668
|
const ac = opts.autoCollect;
|
|
669
669
|
const blanket = ac === false ? false : true;
|
|
670
670
|
const groups = ac && typeof ac === "object" ? ac : void 0;
|
|
671
|
+
const baseUrl = opts.baseUrl ?? "https://cdn.shipeasy.ai";
|
|
671
672
|
const client = configureShipeasy({
|
|
672
|
-
sdkKey: opts.
|
|
673
|
-
baseUrl
|
|
673
|
+
sdkKey: opts.clientKey,
|
|
674
|
+
baseUrl,
|
|
674
675
|
autoGuardrails: blanket,
|
|
675
676
|
autoGuardrailGroups: groups
|
|
676
677
|
});
|
|
678
|
+
injectI18nLoader(opts.clientKey, baseUrl, opts.i18nProfile);
|
|
677
679
|
flags.notifyMounted();
|
|
678
680
|
if (opts.autoIdentify !== false) {
|
|
679
681
|
void client.identify({}).catch((err) => {
|
|
@@ -693,6 +695,23 @@ function getShipeasyClient() {
|
|
|
693
695
|
function _resetShipeasyForTests() {
|
|
694
696
|
_client?.destroy();
|
|
695
697
|
_client = null;
|
|
698
|
+
_i18nLoaderInjected = false;
|
|
699
|
+
}
|
|
700
|
+
var _i18nLoaderInjected = false;
|
|
701
|
+
function injectI18nLoader(clientKey, baseUrl, profileOpt) {
|
|
702
|
+
if (_i18nLoaderInjected || typeof document === "undefined") return;
|
|
703
|
+
if (!clientKey || typeof document.createElement !== "function" || !document.head) return;
|
|
704
|
+
_i18nLoaderInjected = true;
|
|
705
|
+
try {
|
|
706
|
+
const bs = getBootstrap();
|
|
707
|
+
const profile = profileOpt ?? bs?.i18nProfile ?? "en:prod";
|
|
708
|
+
const s = document.createElement("script");
|
|
709
|
+
s.src = `${baseUrl}/sdk/i18n/loader.js`;
|
|
710
|
+
s.setAttribute("data-key", clientKey);
|
|
711
|
+
s.setAttribute("data-profile", profile);
|
|
712
|
+
document.head.appendChild(s);
|
|
713
|
+
} catch {
|
|
714
|
+
}
|
|
696
715
|
}
|
|
697
716
|
function getBootstrap() {
|
|
698
717
|
if (typeof window === "undefined") return null;
|
|
@@ -1047,12 +1066,6 @@ var i18n = {
|
|
|
1047
1066
|
};
|
|
1048
1067
|
}
|
|
1049
1068
|
};
|
|
1050
|
-
if (typeof window !== "undefined") {
|
|
1051
|
-
const _initBs = window.__SE_BOOTSTRAP;
|
|
1052
|
-
if (_initBs?.apiKey && !_client) {
|
|
1053
|
-
shipeasy({ apiKey: _initBs.apiKey, baseUrl: _initBs.apiUrl });
|
|
1054
|
-
}
|
|
1055
|
-
}
|
|
1056
1069
|
export {
|
|
1057
1070
|
FlagsClientBrowser,
|
|
1058
1071
|
LABEL_MARKER_END,
|
package/dist/server/index.d.mts
CHANGED
|
@@ -132,20 +132,15 @@ declare function getShipeasyServerClient(): FlagsClient | null;
|
|
|
132
132
|
declare function _resetShipeasyServerForTests(): void;
|
|
133
133
|
interface ShipeasyServerConfig {
|
|
134
134
|
/**
|
|
135
|
-
* Server
|
|
136
|
-
* (requireKey("server"))
|
|
137
|
-
*
|
|
138
|
-
*
|
|
135
|
+
* Server key — the ONLY key the server entrypoint accepts. Authenticates
|
|
136
|
+
* flag/experiment fetches (requireKey("server")) AND SSR i18n string fetches
|
|
137
|
+
* (the /sdk/i18n/strings route accepts the server key for server-side use).
|
|
138
|
+
* Never embedded in browser output. The browser uses its own client key via
|
|
139
|
+
* `shipeasy({ clientKey })` from `@shipeasy/sdk/client` — the server never
|
|
140
|
+
* sees or forwards the client key. If omitted, flag/experiment/i18n loading
|
|
141
|
+
* is skipped and an error is logged.
|
|
139
142
|
*/
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Public client key — embedded in window.__SE_BOOTSTRAP and used by the
|
|
143
|
-
* browser SDK, and authenticates i18n string fetches (requireKey("client")).
|
|
144
|
-
* Safe to expose (e.g. NEXT_PUBLIC_ env vars). If omitted, i18n loading is
|
|
145
|
-
* skipped and an error is logged — it is NOT substituted with apiKey (a
|
|
146
|
-
* server key 401s against /sdk/i18n/strings).
|
|
147
|
-
*/
|
|
148
|
-
clientKey?: string;
|
|
143
|
+
serverKey?: string;
|
|
149
144
|
/** Raw URL or query string for applying ?se_ks_* / ?se_cf_* / ?se_exp_* overrides. */
|
|
150
145
|
urlOverrides?: string;
|
|
151
146
|
/** User attributes for flag and experiment evaluation. */
|
|
@@ -168,23 +163,24 @@ interface ShipeasyServerHandle {
|
|
|
168
163
|
*/
|
|
169
164
|
declare function shipeasy(opts: ShipeasyServerConfig): Promise<ShipeasyServerHandle>;
|
|
170
165
|
interface BootstrapHtmlOptions {
|
|
171
|
-
/**
|
|
172
|
-
apiKey: string;
|
|
173
|
-
/** i18n profile fed to the loader script. Defaults to "en:prod". */
|
|
166
|
+
/** i18n profile recorded in the bootstrap so the client loader matches SSR. Defaults to "en:prod". */
|
|
174
167
|
i18nProfile?: string;
|
|
175
168
|
/** When true, tEl() embeds label markers so the devtools can highlight them. */
|
|
176
169
|
editLabels?: boolean;
|
|
177
170
|
}
|
|
178
171
|
/**
|
|
179
|
-
* Returns a vanilla-JS
|
|
180
|
-
*
|
|
172
|
+
* Returns a vanilla-JS string for a single inline <script> tag. Handles
|
|
173
|
+
* everything the client needs at startup EXCEPT the key — no SDK key is ever
|
|
174
|
+
* embedded here (the server only knows the server key, which must stay
|
|
175
|
+
* server-side). The browser supplies its own client key via
|
|
176
|
+
* `shipeasy({ clientKey })` from @shipeasy/sdk/client, which also injects the
|
|
177
|
+
* runtime i18n loader. This script emits:
|
|
181
178
|
* - window.__se_devtools_config (when devtoolsAdminUrl is set)
|
|
182
|
-
* - window.__SE_BOOTSTRAP (flags + configs + experiments + i18n +
|
|
183
|
-
* - window.i18n shim from SSR strings (prevents hydration mismatches)
|
|
184
|
-
* -
|
|
179
|
+
* - window.__SE_BOOTSTRAP (flags + configs + experiments + i18n DATA + i18nProfile, NO key)
|
|
180
|
+
* - window.i18n shim from SSR strings (prevents hydration mismatches / FOUC)
|
|
181
|
+
* - devtools overlay loader when ?se / ?se_devtools is present
|
|
185
182
|
*
|
|
186
183
|
* Framework-agnostic: set innerHTML on a <script> element, nothing else required.
|
|
187
|
-
* Pass null for bootstrap on pages without flag evaluation — client still auto-inits.
|
|
188
184
|
*/
|
|
189
185
|
declare function getBootstrapHtml(bootstrap: BootstrapPayload | null, i18nData: I18nForRequest | null, opts: BootstrapHtmlOptions): string;
|
|
190
186
|
declare const flags: {
|
package/dist/server/index.d.ts
CHANGED
|
@@ -132,20 +132,15 @@ declare function getShipeasyServerClient(): FlagsClient | null;
|
|
|
132
132
|
declare function _resetShipeasyServerForTests(): void;
|
|
133
133
|
interface ShipeasyServerConfig {
|
|
134
134
|
/**
|
|
135
|
-
* Server
|
|
136
|
-
* (requireKey("server"))
|
|
137
|
-
*
|
|
138
|
-
*
|
|
135
|
+
* Server key — the ONLY key the server entrypoint accepts. Authenticates
|
|
136
|
+
* flag/experiment fetches (requireKey("server")) AND SSR i18n string fetches
|
|
137
|
+
* (the /sdk/i18n/strings route accepts the server key for server-side use).
|
|
138
|
+
* Never embedded in browser output. The browser uses its own client key via
|
|
139
|
+
* `shipeasy({ clientKey })` from `@shipeasy/sdk/client` — the server never
|
|
140
|
+
* sees or forwards the client key. If omitted, flag/experiment/i18n loading
|
|
141
|
+
* is skipped and an error is logged.
|
|
139
142
|
*/
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Public client key — embedded in window.__SE_BOOTSTRAP and used by the
|
|
143
|
-
* browser SDK, and authenticates i18n string fetches (requireKey("client")).
|
|
144
|
-
* Safe to expose (e.g. NEXT_PUBLIC_ env vars). If omitted, i18n loading is
|
|
145
|
-
* skipped and an error is logged — it is NOT substituted with apiKey (a
|
|
146
|
-
* server key 401s against /sdk/i18n/strings).
|
|
147
|
-
*/
|
|
148
|
-
clientKey?: string;
|
|
143
|
+
serverKey?: string;
|
|
149
144
|
/** Raw URL or query string for applying ?se_ks_* / ?se_cf_* / ?se_exp_* overrides. */
|
|
150
145
|
urlOverrides?: string;
|
|
151
146
|
/** User attributes for flag and experiment evaluation. */
|
|
@@ -168,23 +163,24 @@ interface ShipeasyServerHandle {
|
|
|
168
163
|
*/
|
|
169
164
|
declare function shipeasy(opts: ShipeasyServerConfig): Promise<ShipeasyServerHandle>;
|
|
170
165
|
interface BootstrapHtmlOptions {
|
|
171
|
-
/**
|
|
172
|
-
apiKey: string;
|
|
173
|
-
/** i18n profile fed to the loader script. Defaults to "en:prod". */
|
|
166
|
+
/** i18n profile recorded in the bootstrap so the client loader matches SSR. Defaults to "en:prod". */
|
|
174
167
|
i18nProfile?: string;
|
|
175
168
|
/** When true, tEl() embeds label markers so the devtools can highlight them. */
|
|
176
169
|
editLabels?: boolean;
|
|
177
170
|
}
|
|
178
171
|
/**
|
|
179
|
-
* Returns a vanilla-JS
|
|
180
|
-
*
|
|
172
|
+
* Returns a vanilla-JS string for a single inline <script> tag. Handles
|
|
173
|
+
* everything the client needs at startup EXCEPT the key — no SDK key is ever
|
|
174
|
+
* embedded here (the server only knows the server key, which must stay
|
|
175
|
+
* server-side). The browser supplies its own client key via
|
|
176
|
+
* `shipeasy({ clientKey })` from @shipeasy/sdk/client, which also injects the
|
|
177
|
+
* runtime i18n loader. This script emits:
|
|
181
178
|
* - window.__se_devtools_config (when devtoolsAdminUrl is set)
|
|
182
|
-
* - window.__SE_BOOTSTRAP (flags + configs + experiments + i18n +
|
|
183
|
-
* - window.i18n shim from SSR strings (prevents hydration mismatches)
|
|
184
|
-
* -
|
|
179
|
+
* - window.__SE_BOOTSTRAP (flags + configs + experiments + i18n DATA + i18nProfile, NO key)
|
|
180
|
+
* - window.i18n shim from SSR strings (prevents hydration mismatches / FOUC)
|
|
181
|
+
* - devtools overlay loader when ?se / ?se_devtools is present
|
|
185
182
|
*
|
|
186
183
|
* Framework-agnostic: set innerHTML on a <script> element, nothing else required.
|
|
187
|
-
* Pass null for bootstrap on pages without flag evaluation — client still auto-inits.
|
|
188
184
|
*/
|
|
189
185
|
declare function getBootstrapHtml(bootstrap: BootstrapPayload | null, i18nData: I18nForRequest | null, opts: BootstrapHtmlOptions): string;
|
|
190
186
|
declare const flags: {
|
package/dist/server/index.js
CHANGED
|
@@ -387,6 +387,7 @@ var _I18N_SSR_SYM = /* @__PURE__ */ Symbol.for("@shipeasy/sdk:ssr-i18n");
|
|
|
387
387
|
var _EDIT_MODE_SSR_SYM = /* @__PURE__ */ Symbol.for("@shipeasy/sdk:ssr-edit-mode");
|
|
388
388
|
var _i18nALS = new import_node_async_hooks.AsyncLocalStorage();
|
|
389
389
|
var _I18N_CACHE_SYM = /* @__PURE__ */ Symbol.for("@shipeasy/sdk:ssr-i18n-cache");
|
|
390
|
+
var I18N_CACHE_TTL_MS = 6e4;
|
|
390
391
|
var _i18nCache = globalThis[_I18N_CACHE_SYM] ?? (globalThis[_I18N_CACHE_SYM] = /* @__PURE__ */ new Map());
|
|
391
392
|
globalThis[_I18N_SSR_SYM] = () => {
|
|
392
393
|
const fromALS = _i18nALS.getStore();
|
|
@@ -432,14 +433,19 @@ var i18n = {
|
|
|
432
433
|
const existingALS = _i18nALS.getStore();
|
|
433
434
|
if (existingALS && Object.keys(existingALS.strings).length > 0) return;
|
|
434
435
|
const cached = _i18nCache.get(profile);
|
|
435
|
-
if (cached && Object.keys(cached.strings).length > 0) {
|
|
436
|
+
if (cached && Object.keys(cached.strings).length > 0 && Date.now() - cached.fetchedAt < I18N_CACHE_TTL_MS) {
|
|
436
437
|
_i18nALS.enterWith(cached);
|
|
437
438
|
return;
|
|
438
439
|
}
|
|
439
440
|
const labels = await fetchLabelsForSSR({ key, profile, cdnBaseUrl }).catch(() => null);
|
|
440
441
|
const locale = profile.split(":")[0] || "en";
|
|
441
442
|
const store = { strings: labels?.strings ?? {}, locale };
|
|
442
|
-
if (Object.keys(store.strings).length > 0)
|
|
443
|
+
if (Object.keys(store.strings).length > 0) {
|
|
444
|
+
_i18nCache.set(profile, { ...store, fetchedAt: Date.now() });
|
|
445
|
+
} else if (cached && Object.keys(cached.strings).length > 0) {
|
|
446
|
+
_i18nALS.enterWith(cached);
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
443
449
|
_i18nALS.enterWith(store);
|
|
444
450
|
},
|
|
445
451
|
/**
|
|
@@ -486,7 +492,6 @@ async function fetchLabelsForSSR(opts) {
|
|
|
486
492
|
}
|
|
487
493
|
}
|
|
488
494
|
var _server = null;
|
|
489
|
-
var _rememberedClientKey = null;
|
|
490
495
|
function configureShipeasyServer(opts) {
|
|
491
496
|
if (_server) return _server;
|
|
492
497
|
_server = new FlagsClient(opts);
|
|
@@ -500,21 +505,14 @@ function _resetShipeasyServerForTests() {
|
|
|
500
505
|
_server = null;
|
|
501
506
|
}
|
|
502
507
|
async function shipeasy(opts) {
|
|
503
|
-
const
|
|
504
|
-
|
|
505
|
-
if (opts.clientKey && !_rememberedClientKey) _rememberedClientKey = opts.clientKey;
|
|
506
|
-
if (!apiKey) {
|
|
507
|
-
console.error(
|
|
508
|
-
"[shipeasy] No server key \u2014 flags & experiments skipped. Pass `apiKey` to shipeasy() with your server key (SHIPEASY_SERVER_KEY). Set it as a Worker secret with `wrangler secret put SHIPEASY_SERVER_KEY` (or add it to .env for local dev). Do not pass a client key here \u2014 /sdk/flags requires a server key and will 401."
|
|
509
|
-
);
|
|
510
|
-
}
|
|
511
|
-
if (!clientKey) {
|
|
508
|
+
const serverKey = opts.serverKey ?? "";
|
|
509
|
+
if (!serverKey) {
|
|
512
510
|
console.error(
|
|
513
|
-
"[shipeasy] No
|
|
511
|
+
"[shipeasy] No server key \u2014 flags, experiments and SSR i18n skipped. Pass `serverKey` to shipeasy() from @shipeasy/sdk/server with your server key (SHIPEASY_SERVER_KEY). Set it as a Worker secret with `wrangler secret put SHIPEASY_SERVER_KEY` (or add it to .env for local dev). Do not pass a client key here \u2014 the server entrypoint only accepts the server key."
|
|
514
512
|
);
|
|
515
513
|
}
|
|
516
514
|
const profile = opts.i18nDefaultProfile ?? "en:prod";
|
|
517
|
-
flags.configure({ apiKey });
|
|
515
|
+
flags.configure({ apiKey: serverKey });
|
|
518
516
|
let resolvedUrlOverrides = opts.urlOverrides;
|
|
519
517
|
if (!resolvedUrlOverrides) {
|
|
520
518
|
try {
|
|
@@ -535,8 +533,8 @@ async function shipeasy(opts) {
|
|
|
535
533
|
const editLabels = resolvedUrlOverrides ? new URLSearchParams(resolvedUrlOverrides).has("se_edit_labels") : false;
|
|
536
534
|
globalThis[_EDIT_MODE_SSR_SYM] = editLabels;
|
|
537
535
|
await Promise.allSettled([
|
|
538
|
-
|
|
539
|
-
|
|
536
|
+
serverKey ? flags.initOnce() : Promise.resolve(),
|
|
537
|
+
serverKey ? i18n.init(serverKey, profile) : Promise.resolve()
|
|
540
538
|
]);
|
|
541
539
|
const bootstrap = flags.evaluate(opts.user ?? {}, resolvedUrlOverrides);
|
|
542
540
|
const i18nData = i18n.getForRequest();
|
|
@@ -546,7 +544,6 @@ async function shipeasy(opts) {
|
|
|
546
544
|
experiments: bootstrap.experiments,
|
|
547
545
|
getBootstrapHtml() {
|
|
548
546
|
return getBootstrapHtml(bootstrap, i18nData, {
|
|
549
|
-
apiKey: clientKey,
|
|
550
547
|
editLabels,
|
|
551
548
|
i18nProfile: profile
|
|
552
549
|
});
|
|
@@ -561,7 +558,9 @@ function getBootstrapHtml(bootstrap, i18nData, opts) {
|
|
|
561
558
|
flags: bootstrap?.flags ?? {},
|
|
562
559
|
configs: bootstrap?.configs ?? {},
|
|
563
560
|
experiments: bootstrap?.experiments ?? {},
|
|
564
|
-
|
|
561
|
+
// No key here — the server only knows the server key, which must never reach
|
|
562
|
+
// the browser. The client supplies its own client key via shipeasy({ clientKey }).
|
|
563
|
+
i18nProfile: profile,
|
|
565
564
|
apiUrl
|
|
566
565
|
};
|
|
567
566
|
if (i18nData) payload.i18n = i18nData;
|
|
@@ -575,9 +574,6 @@ function getBootstrapHtml(bootstrap, i18nData, opts) {
|
|
|
575
574
|
`(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(){};}};})();`
|
|
576
575
|
);
|
|
577
576
|
}
|
|
578
|
-
parts.push(
|
|
579
|
-
`(function(){var s=document.createElement('script');s.src=${JSON.stringify(`${apiUrl}/sdk/i18n/loader.js`)};s.setAttribute('data-key',${JSON.stringify(opts.apiKey)});s.setAttribute('data-profile',${JSON.stringify(profile)});document.head.appendChild(s);})();`
|
|
580
|
-
);
|
|
581
577
|
parts.push(
|
|
582
578
|
`(function(){var p=new URLSearchParams(location.search);if(p.has('se')||p.has('se_devtools')){var d=document.createElement('script');d.src='https://shipeasy.ai/se-devtools.js';document.head.appendChild(d);}})();`
|
|
583
579
|
);
|
package/dist/server/index.mjs
CHANGED
|
@@ -344,6 +344,7 @@ var _I18N_SSR_SYM = /* @__PURE__ */ Symbol.for("@shipeasy/sdk:ssr-i18n");
|
|
|
344
344
|
var _EDIT_MODE_SSR_SYM = /* @__PURE__ */ Symbol.for("@shipeasy/sdk:ssr-edit-mode");
|
|
345
345
|
var _i18nALS = new AsyncLocalStorage();
|
|
346
346
|
var _I18N_CACHE_SYM = /* @__PURE__ */ Symbol.for("@shipeasy/sdk:ssr-i18n-cache");
|
|
347
|
+
var I18N_CACHE_TTL_MS = 6e4;
|
|
347
348
|
var _i18nCache = globalThis[_I18N_CACHE_SYM] ?? (globalThis[_I18N_CACHE_SYM] = /* @__PURE__ */ new Map());
|
|
348
349
|
globalThis[_I18N_SSR_SYM] = () => {
|
|
349
350
|
const fromALS = _i18nALS.getStore();
|
|
@@ -389,14 +390,19 @@ var i18n = {
|
|
|
389
390
|
const existingALS = _i18nALS.getStore();
|
|
390
391
|
if (existingALS && Object.keys(existingALS.strings).length > 0) return;
|
|
391
392
|
const cached = _i18nCache.get(profile);
|
|
392
|
-
if (cached && Object.keys(cached.strings).length > 0) {
|
|
393
|
+
if (cached && Object.keys(cached.strings).length > 0 && Date.now() - cached.fetchedAt < I18N_CACHE_TTL_MS) {
|
|
393
394
|
_i18nALS.enterWith(cached);
|
|
394
395
|
return;
|
|
395
396
|
}
|
|
396
397
|
const labels = await fetchLabelsForSSR({ key, profile, cdnBaseUrl }).catch(() => null);
|
|
397
398
|
const locale = profile.split(":")[0] || "en";
|
|
398
399
|
const store = { strings: labels?.strings ?? {}, locale };
|
|
399
|
-
if (Object.keys(store.strings).length > 0)
|
|
400
|
+
if (Object.keys(store.strings).length > 0) {
|
|
401
|
+
_i18nCache.set(profile, { ...store, fetchedAt: Date.now() });
|
|
402
|
+
} else if (cached && Object.keys(cached.strings).length > 0) {
|
|
403
|
+
_i18nALS.enterWith(cached);
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
400
406
|
_i18nALS.enterWith(store);
|
|
401
407
|
},
|
|
402
408
|
/**
|
|
@@ -443,7 +449,6 @@ async function fetchLabelsForSSR(opts) {
|
|
|
443
449
|
}
|
|
444
450
|
}
|
|
445
451
|
var _server = null;
|
|
446
|
-
var _rememberedClientKey = null;
|
|
447
452
|
function configureShipeasyServer(opts) {
|
|
448
453
|
if (_server) return _server;
|
|
449
454
|
_server = new FlagsClient(opts);
|
|
@@ -457,21 +462,14 @@ function _resetShipeasyServerForTests() {
|
|
|
457
462
|
_server = null;
|
|
458
463
|
}
|
|
459
464
|
async function shipeasy(opts) {
|
|
460
|
-
const
|
|
461
|
-
|
|
462
|
-
if (opts.clientKey && !_rememberedClientKey) _rememberedClientKey = opts.clientKey;
|
|
463
|
-
if (!apiKey) {
|
|
464
|
-
console.error(
|
|
465
|
-
"[shipeasy] No server key \u2014 flags & experiments skipped. Pass `apiKey` to shipeasy() with your server key (SHIPEASY_SERVER_KEY). Set it as a Worker secret with `wrangler secret put SHIPEASY_SERVER_KEY` (or add it to .env for local dev). Do not pass a client key here \u2014 /sdk/flags requires a server key and will 401."
|
|
466
|
-
);
|
|
467
|
-
}
|
|
468
|
-
if (!clientKey) {
|
|
465
|
+
const serverKey = opts.serverKey ?? "";
|
|
466
|
+
if (!serverKey) {
|
|
469
467
|
console.error(
|
|
470
|
-
"[shipeasy] No
|
|
468
|
+
"[shipeasy] No server key \u2014 flags, experiments and SSR i18n skipped. Pass `serverKey` to shipeasy() from @shipeasy/sdk/server with your server key (SHIPEASY_SERVER_KEY). Set it as a Worker secret with `wrangler secret put SHIPEASY_SERVER_KEY` (or add it to .env for local dev). Do not pass a client key here \u2014 the server entrypoint only accepts the server key."
|
|
471
469
|
);
|
|
472
470
|
}
|
|
473
471
|
const profile = opts.i18nDefaultProfile ?? "en:prod";
|
|
474
|
-
flags.configure({ apiKey });
|
|
472
|
+
flags.configure({ apiKey: serverKey });
|
|
475
473
|
let resolvedUrlOverrides = opts.urlOverrides;
|
|
476
474
|
if (!resolvedUrlOverrides) {
|
|
477
475
|
try {
|
|
@@ -492,8 +490,8 @@ async function shipeasy(opts) {
|
|
|
492
490
|
const editLabels = resolvedUrlOverrides ? new URLSearchParams(resolvedUrlOverrides).has("se_edit_labels") : false;
|
|
493
491
|
globalThis[_EDIT_MODE_SSR_SYM] = editLabels;
|
|
494
492
|
await Promise.allSettled([
|
|
495
|
-
|
|
496
|
-
|
|
493
|
+
serverKey ? flags.initOnce() : Promise.resolve(),
|
|
494
|
+
serverKey ? i18n.init(serverKey, profile) : Promise.resolve()
|
|
497
495
|
]);
|
|
498
496
|
const bootstrap = flags.evaluate(opts.user ?? {}, resolvedUrlOverrides);
|
|
499
497
|
const i18nData = i18n.getForRequest();
|
|
@@ -503,7 +501,6 @@ async function shipeasy(opts) {
|
|
|
503
501
|
experiments: bootstrap.experiments,
|
|
504
502
|
getBootstrapHtml() {
|
|
505
503
|
return getBootstrapHtml(bootstrap, i18nData, {
|
|
506
|
-
apiKey: clientKey,
|
|
507
504
|
editLabels,
|
|
508
505
|
i18nProfile: profile
|
|
509
506
|
});
|
|
@@ -518,7 +515,9 @@ function getBootstrapHtml(bootstrap, i18nData, opts) {
|
|
|
518
515
|
flags: bootstrap?.flags ?? {},
|
|
519
516
|
configs: bootstrap?.configs ?? {},
|
|
520
517
|
experiments: bootstrap?.experiments ?? {},
|
|
521
|
-
|
|
518
|
+
// No key here — the server only knows the server key, which must never reach
|
|
519
|
+
// the browser. The client supplies its own client key via shipeasy({ clientKey }).
|
|
520
|
+
i18nProfile: profile,
|
|
522
521
|
apiUrl
|
|
523
522
|
};
|
|
524
523
|
if (i18nData) payload.i18n = i18nData;
|
|
@@ -532,9 +531,6 @@ function getBootstrapHtml(bootstrap, i18nData, opts) {
|
|
|
532
531
|
`(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(){};}};})();`
|
|
533
532
|
);
|
|
534
533
|
}
|
|
535
|
-
parts.push(
|
|
536
|
-
`(function(){var s=document.createElement('script');s.src=${JSON.stringify(`${apiUrl}/sdk/i18n/loader.js`)};s.setAttribute('data-key',${JSON.stringify(opts.apiKey)});s.setAttribute('data-profile',${JSON.stringify(profile)});document.head.appendChild(s);})();`
|
|
537
|
-
);
|
|
538
534
|
parts.push(
|
|
539
535
|
`(function(){var p=new URLSearchParams(location.search);if(p.has('se')||p.has('se_devtools')){var d=document.createElement('script');d.src='https://shipeasy.ai/se-devtools.js';document.head.appendChild(d);}})();`
|
|
540
536
|
);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shipeasy/sdk",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.1",
|
|
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",
|