@uniformdev/next-app-router-shared 20.48.0 → 20.48.1-alpha.11

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/index.d.mts CHANGED
@@ -64,6 +64,11 @@ type PageState = {
64
64
  * The locale that was resolved for the current request.
65
65
  */
66
66
  locale: string | undefined;
67
+ /**
68
+ * Whether this page state was generated during a prefetch request.
69
+ * When true, test evaluations are deferred to the client side.
70
+ */
71
+ isPrefetch: true | undefined;
67
72
  };
68
73
  declare const resolveComponentFromPageState: ({ pageState, componentId: providedComponentId, }: {
69
74
  pageState: PageState;
@@ -74,9 +79,8 @@ declare const resolveRuleFromPageState: ({ pageState, rule, }: {
74
79
  pageState: PageState;
75
80
  rule: VisibilityParameterValue;
76
81
  }) => boolean | undefined;
77
- declare const serializeEvaluationResult: ({ payload, encode, }: {
82
+ declare const serializeEvaluationResult: ({ payload }: {
78
83
  payload: PageState;
79
- encode?: boolean;
80
84
  }) => string;
81
85
  declare const deserializeEvaluationResult: ({ input: providedInput, decode, }: {
82
86
  input: string;
@@ -156,7 +160,7 @@ type PersonalizeProps = ComponentProps<{
156
160
  indexes: number[];
157
161
  };
158
162
  type TestProps = ComponentProps & {
159
- index: number;
163
+ index: number | undefined;
160
164
  test: ExtractTestResult;
161
165
  };
162
166
 
package/dist/index.d.ts CHANGED
@@ -64,6 +64,11 @@ type PageState = {
64
64
  * The locale that was resolved for the current request.
65
65
  */
66
66
  locale: string | undefined;
67
+ /**
68
+ * Whether this page state was generated during a prefetch request.
69
+ * When true, test evaluations are deferred to the client side.
70
+ */
71
+ isPrefetch: true | undefined;
67
72
  };
68
73
  declare const resolveComponentFromPageState: ({ pageState, componentId: providedComponentId, }: {
69
74
  pageState: PageState;
@@ -74,9 +79,8 @@ declare const resolveRuleFromPageState: ({ pageState, rule, }: {
74
79
  pageState: PageState;
75
80
  rule: VisibilityParameterValue;
76
81
  }) => boolean | undefined;
77
- declare const serializeEvaluationResult: ({ payload, encode, }: {
82
+ declare const serializeEvaluationResult: ({ payload }: {
78
83
  payload: PageState;
79
- encode?: boolean;
80
84
  }) => string;
81
85
  declare const deserializeEvaluationResult: ({ input: providedInput, decode, }: {
82
86
  input: string;
@@ -156,7 +160,7 @@ type PersonalizeProps = ComponentProps<{
156
160
  indexes: number[];
157
161
  };
158
162
  type TestProps = ComponentProps & {
159
- index: number;
163
+ index: number | undefined;
160
164
  test: ExtractTestResult;
161
165
  };
162
166
 
package/dist/index.esm.js CHANGED
@@ -160,23 +160,11 @@ var resolveRuleFromPageState = ({
160
160
  }
161
161
  return value;
162
162
  };
163
- var replaceReservedCharacters = (value) => {
164
- return ENCODED_RESERVED_CHARACTERS.reduce((acc, char) => {
165
- return acc.replaceAll(char.character, char.replacement);
166
- }, value);
167
- };
168
163
  var unreplaceReservedCharacters = (value) => {
169
164
  return ENCODED_RESERVED_CHARACTERS.reduce((acc, char) => {
170
165
  return acc.replaceAll(char.replacement, char.character);
171
166
  }, value);
172
167
  };
173
- var encodeWithReplacements = (value) => {
174
- return replaceReservedCharacters(btoa(value));
175
- };
176
- var decodeWithReplacements = (value) => {
177
- const urlDecoded = decodeURIComponent(value);
178
- return atob(unreplaceReservedCharacters(urlDecoded));
179
- };
180
168
  var compressIds = (ids) => {
181
169
  const uuidV4Length = 36;
182
170
  let compressedRegistry;
@@ -206,21 +194,58 @@ var compressIds = (ids) => {
206
194
  );
207
195
  };
208
196
  var NAMESPACE_UUID = "00000000-0000-0000-0000-000000000000";
209
- var serializeEvaluationResult = ({
210
- payload,
211
- encode = true
212
- }) => {
213
- const compressedPageState = {
214
- cs: payload.compositionState,
215
- r: payload.routePath,
216
- c: {},
217
- k: payload.keys,
218
- ri: payload.releaseId,
219
- dc: typeof payload.defaultConsent !== "undefined" ? payload.defaultConsent ? 1 : 0 : void 0,
220
- pm: payload.previewMode === "editor" ? "e" : payload.previewMode === "preview" ? "p" : void 0,
221
- rl: void 0,
222
- l: payload.locale
197
+ var V2_FIELD_SEP = "~";
198
+ var V2_ENTRY_SEP = ".";
199
+ var V2_KV_SEP = ":";
200
+ var V2_INDEX_SEP = ",";
201
+ var toBase64Url = (value) => {
202
+ const bytes = new TextEncoder().encode(value);
203
+ const binary = Array.from(bytes, (b) => String.fromCharCode(b)).join("");
204
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
205
+ };
206
+ var fromBase64Url = (value) => {
207
+ let base64 = value.replace(/-/g, "+").replace(/_/g, "/");
208
+ const pad = base64.length % 4;
209
+ if (pad === 2) {
210
+ base64 += "==";
211
+ } else if (pad === 3) {
212
+ base64 += "=";
213
+ }
214
+ const binary = atob(base64);
215
+ const bytes = Uint8Array.from(binary, (c) => c.charCodeAt(0));
216
+ return new TextDecoder().decode(bytes);
217
+ };
218
+ var encodeFlags = (payload) => {
219
+ let flags = 0;
220
+ if (typeof payload.defaultConsent !== "undefined") {
221
+ flags |= 1;
222
+ if (payload.defaultConsent) {
223
+ flags |= 2;
224
+ }
225
+ }
226
+ if (payload.previewMode === "editor") {
227
+ flags |= 4;
228
+ }
229
+ if (payload.previewMode === "preview") {
230
+ flags |= 8;
231
+ }
232
+ if (payload.isPrefetch) {
233
+ flags |= 16;
234
+ }
235
+ return flags.toString(16);
236
+ };
237
+ var decodeFlags = (hex) => {
238
+ const flags = parseInt(hex, 16) || 0;
239
+ return {
240
+ defaultConsent: flags & 1 ? Boolean(flags & 2) : void 0,
241
+ previewMode: flags & 4 ? "editor" : flags & 8 ? "preview" : void 0,
242
+ isPrefetch: flags & 16 ? true : void 0
223
243
  };
244
+ };
245
+ var serializeV2 = (payload) => {
246
+ const parts = ["2"];
247
+ parts.push(String(payload.compositionState));
248
+ parts.push(toBase64Url(payload.routePath));
224
249
  const componentKeys = Object.keys(payload.components);
225
250
  const sortedKeys = componentKeys.sort();
226
251
  const sortedComponents = sortedKeys.map((key) => ({
@@ -228,39 +253,146 @@ var serializeEvaluationResult = ({
228
253
  ...payload.components[key]
229
254
  }));
230
255
  const compressedComponentIds = compressIds(sortedComponents.map((c) => c._id));
256
+ const componentById = new Map(sortedComponents.map((c) => [c._id, c]));
257
+ const componentEntries = [];
231
258
  Object.keys(compressedComponentIds).forEach((compressedId) => {
232
259
  var _a;
233
260
  const originalId = compressedComponentIds[compressedId];
234
- const component = sortedComponents.find((c) => c._id === originalId);
261
+ const component = componentById.get(originalId);
235
262
  if (!component) {
236
263
  throw new Error(`Component ${originalId} not found`);
237
264
  }
238
- compressedPageState.c[compressedId] = {
239
- i: ((_a = component.indexes) == null ? void 0 : _a.length) ? component.indexes : void 0
240
- };
265
+ const indexes = ((_a = component.indexes) == null ? void 0 : _a.length) ? component.indexes : void 0;
266
+ if (indexes) {
267
+ componentEntries.push(`${compressedId}${V2_KV_SEP}${indexes.join(V2_INDEX_SEP)}`);
268
+ } else {
269
+ componentEntries.push(compressedId);
270
+ }
241
271
  });
272
+ parts.push(componentEntries.join(V2_ENTRY_SEP));
273
+ let rulesStr = "";
242
274
  if (typeof payload.rules !== "undefined") {
243
275
  const ruleKeys = Object.keys(payload.rules);
244
276
  if (ruleKeys.length !== 0) {
245
- compressedPageState.rl = {};
246
277
  const sortedRuleKeys = ruleKeys.sort();
247
278
  const compressedRuleKeys = compressIds(sortedRuleKeys);
279
+ const ruleEntries = [];
248
280
  Object.keys(compressedRuleKeys).forEach((compressedId) => {
249
281
  const originalId = compressedRuleKeys[compressedId];
250
- compressedPageState.rl[compressedId] = payload.rules[originalId] ? 1 : 0;
282
+ ruleEntries.push(`${compressedId}${V2_KV_SEP}${payload.rules[originalId] ? 1 : 0}`);
251
283
  });
284
+ rulesStr = ruleEntries.join(V2_ENTRY_SEP);
252
285
  }
253
286
  }
254
- const result = JSON.stringify(compressedPageState);
255
- return encode ? encodeWithReplacements(result) : result;
287
+ parts.push(rulesStr);
288
+ let keysStr = "";
289
+ if (payload.keys) {
290
+ const keyEntries = Object.entries(payload.keys).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([k, v]) => `${toBase64Url(k)}${V2_KV_SEP}${toBase64Url(v)}`);
291
+ keysStr = keyEntries.join(V2_ENTRY_SEP);
292
+ }
293
+ parts.push(keysStr);
294
+ parts.push(encodeFlags(payload));
295
+ parts.push(payload.releaseId ? toBase64Url(payload.releaseId) : "");
296
+ parts.push(payload.locale ? toBase64Url(payload.locale) : "");
297
+ return parts.join(V2_FIELD_SEP);
298
+ };
299
+ var UNSAFE_KEYS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
300
+ var isUnsafeKey = (key) => UNSAFE_KEYS.has(key);
301
+ var deserializeV2 = (input) => {
302
+ var _a, _b, _c, _d;
303
+ const fields = input.split(V2_FIELD_SEP);
304
+ if (fields.length < 3) {
305
+ throw new Error(`Invalid serialized PageState: expected at least 3 fields but got ${fields.length}`);
306
+ }
307
+ const compositionStateRaw = fields[1];
308
+ const compositionState = Number.parseInt(compositionStateRaw, 10);
309
+ if (Number.isNaN(compositionState)) {
310
+ throw new Error(
311
+ `Invalid serialized PageState: compositionState '${compositionStateRaw}' is not a valid number`
312
+ );
313
+ }
314
+ const routePath = fromBase64Url(fields[2]);
315
+ const components = {};
316
+ const componentsStr = (_a = fields[3]) != null ? _a : "";
317
+ if (componentsStr) {
318
+ componentsStr.split(V2_ENTRY_SEP).forEach((entry) => {
319
+ const sepIdx = entry.indexOf(V2_KV_SEP);
320
+ if (sepIdx === -1) {
321
+ const id = entry;
322
+ if (isUnsafeKey(id)) {
323
+ return;
324
+ }
325
+ components[id] = {};
326
+ } else {
327
+ const id = entry.slice(0, sepIdx);
328
+ if (isUnsafeKey(id)) {
329
+ return;
330
+ }
331
+ const indexes = entry.slice(sepIdx + 1).split(V2_INDEX_SEP).map(Number);
332
+ components[id] = { indexes };
333
+ }
334
+ });
335
+ }
336
+ const rulesStr = (_b = fields[4]) != null ? _b : "";
337
+ let rules;
338
+ if (rulesStr) {
339
+ rules = {};
340
+ rulesStr.split(V2_ENTRY_SEP).forEach((entry) => {
341
+ const sepIdx = entry.indexOf(V2_KV_SEP);
342
+ if (sepIdx !== -1) {
343
+ const id = entry.slice(0, sepIdx);
344
+ if (isUnsafeKey(id)) {
345
+ return;
346
+ }
347
+ rules[id] = entry.slice(sepIdx + 1) === "1";
348
+ }
349
+ });
350
+ }
351
+ const keysStr = (_c = fields[5]) != null ? _c : "";
352
+ const keys = {};
353
+ if (keysStr) {
354
+ keysStr.split(V2_ENTRY_SEP).forEach((entry) => {
355
+ const sepIdx = entry.indexOf(V2_KV_SEP);
356
+ if (sepIdx !== -1) {
357
+ const k = fromBase64Url(entry.slice(0, sepIdx));
358
+ if (isUnsafeKey(k)) {
359
+ return;
360
+ }
361
+ const v = fromBase64Url(entry.slice(sepIdx + 1));
362
+ keys[k] = v;
363
+ }
364
+ });
365
+ }
366
+ const { defaultConsent, previewMode, isPrefetch } = decodeFlags((_d = fields[6]) != null ? _d : "0");
367
+ const releaseId = fields[7] ? fromBase64Url(fields[7]) : void 0;
368
+ const locale = fields[8] ? fromBase64Url(fields[8]) : void 0;
369
+ return {
370
+ compositionState,
371
+ routePath,
372
+ components,
373
+ rules,
374
+ keys,
375
+ releaseId,
376
+ defaultConsent,
377
+ previewMode,
378
+ locale,
379
+ isPrefetch
380
+ };
381
+ };
382
+ var serializeEvaluationResult = ({ payload }) => {
383
+ return serializeV2(payload);
256
384
  };
257
385
  var deserializeEvaluationResult = ({
258
386
  input: providedInput,
259
387
  decode = true
260
388
  }) => {
261
389
  var _a, _b, _c;
262
- const input = decode ? decodeWithReplacements(providedInput) : providedInput;
263
- const parsed = JSON.parse(input);
390
+ const input = decode ? decodeURIComponent(providedInput) : providedInput;
391
+ if (input.startsWith("2~")) {
392
+ return deserializeV2(input);
393
+ }
394
+ const jsonInput = decode ? atob(unreplaceReservedCharacters(input)) : input;
395
+ const parsed = JSON.parse(jsonInput);
264
396
  const pageState = {
265
397
  compositionState: parsed.cs,
266
398
  routePath: parsed.r,
@@ -270,7 +402,8 @@ var deserializeEvaluationResult = ({
270
402
  defaultConsent: typeof parsed.dc !== "undefined" ? parsed.dc === 1 : void 0,
271
403
  previewMode: parsed.pm === "e" ? "editor" : parsed.pm === "p" ? "preview" : void 0,
272
404
  rules: parsed.rl ? Object.fromEntries(Object.entries(parsed.rl).map(([key, value]) => [key, value === 1])) : void 0,
273
- locale: (_c = parsed.l) != null ? _c : void 0
405
+ locale: (_c = parsed.l) != null ? _c : void 0,
406
+ isPrefetch: parsed.pf === 1 ? true : void 0
274
407
  };
275
408
  Object.keys(parsed.c).forEach((id) => {
276
409
  const component = parsed.c[id];
package/dist/index.js CHANGED
@@ -195,23 +195,11 @@ var resolveRuleFromPageState = ({
195
195
  }
196
196
  return value;
197
197
  };
198
- var replaceReservedCharacters = (value) => {
199
- return ENCODED_RESERVED_CHARACTERS.reduce((acc, char) => {
200
- return acc.replaceAll(char.character, char.replacement);
201
- }, value);
202
- };
203
198
  var unreplaceReservedCharacters = (value) => {
204
199
  return ENCODED_RESERVED_CHARACTERS.reduce((acc, char) => {
205
200
  return acc.replaceAll(char.replacement, char.character);
206
201
  }, value);
207
202
  };
208
- var encodeWithReplacements = (value) => {
209
- return replaceReservedCharacters(btoa(value));
210
- };
211
- var decodeWithReplacements = (value) => {
212
- const urlDecoded = decodeURIComponent(value);
213
- return atob(unreplaceReservedCharacters(urlDecoded));
214
- };
215
203
  var compressIds = (ids) => {
216
204
  const uuidV4Length = 36;
217
205
  let compressedRegistry;
@@ -241,21 +229,58 @@ var compressIds = (ids) => {
241
229
  );
242
230
  };
243
231
  var NAMESPACE_UUID = "00000000-0000-0000-0000-000000000000";
244
- var serializeEvaluationResult = ({
245
- payload,
246
- encode = true
247
- }) => {
248
- const compressedPageState = {
249
- cs: payload.compositionState,
250
- r: payload.routePath,
251
- c: {},
252
- k: payload.keys,
253
- ri: payload.releaseId,
254
- dc: typeof payload.defaultConsent !== "undefined" ? payload.defaultConsent ? 1 : 0 : void 0,
255
- pm: payload.previewMode === "editor" ? "e" : payload.previewMode === "preview" ? "p" : void 0,
256
- rl: void 0,
257
- l: payload.locale
232
+ var V2_FIELD_SEP = "~";
233
+ var V2_ENTRY_SEP = ".";
234
+ var V2_KV_SEP = ":";
235
+ var V2_INDEX_SEP = ",";
236
+ var toBase64Url = (value) => {
237
+ const bytes = new TextEncoder().encode(value);
238
+ const binary = Array.from(bytes, (b) => String.fromCharCode(b)).join("");
239
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
240
+ };
241
+ var fromBase64Url = (value) => {
242
+ let base64 = value.replace(/-/g, "+").replace(/_/g, "/");
243
+ const pad = base64.length % 4;
244
+ if (pad === 2) {
245
+ base64 += "==";
246
+ } else if (pad === 3) {
247
+ base64 += "=";
248
+ }
249
+ const binary = atob(base64);
250
+ const bytes = Uint8Array.from(binary, (c) => c.charCodeAt(0));
251
+ return new TextDecoder().decode(bytes);
252
+ };
253
+ var encodeFlags = (payload) => {
254
+ let flags = 0;
255
+ if (typeof payload.defaultConsent !== "undefined") {
256
+ flags |= 1;
257
+ if (payload.defaultConsent) {
258
+ flags |= 2;
259
+ }
260
+ }
261
+ if (payload.previewMode === "editor") {
262
+ flags |= 4;
263
+ }
264
+ if (payload.previewMode === "preview") {
265
+ flags |= 8;
266
+ }
267
+ if (payload.isPrefetch) {
268
+ flags |= 16;
269
+ }
270
+ return flags.toString(16);
271
+ };
272
+ var decodeFlags = (hex) => {
273
+ const flags = parseInt(hex, 16) || 0;
274
+ return {
275
+ defaultConsent: flags & 1 ? Boolean(flags & 2) : void 0,
276
+ previewMode: flags & 4 ? "editor" : flags & 8 ? "preview" : void 0,
277
+ isPrefetch: flags & 16 ? true : void 0
258
278
  };
279
+ };
280
+ var serializeV2 = (payload) => {
281
+ const parts = ["2"];
282
+ parts.push(String(payload.compositionState));
283
+ parts.push(toBase64Url(payload.routePath));
259
284
  const componentKeys = Object.keys(payload.components);
260
285
  const sortedKeys = componentKeys.sort();
261
286
  const sortedComponents = sortedKeys.map((key) => ({
@@ -263,39 +288,146 @@ var serializeEvaluationResult = ({
263
288
  ...payload.components[key]
264
289
  }));
265
290
  const compressedComponentIds = compressIds(sortedComponents.map((c) => c._id));
291
+ const componentById = new Map(sortedComponents.map((c) => [c._id, c]));
292
+ const componentEntries = [];
266
293
  Object.keys(compressedComponentIds).forEach((compressedId) => {
267
294
  var _a;
268
295
  const originalId = compressedComponentIds[compressedId];
269
- const component = sortedComponents.find((c) => c._id === originalId);
296
+ const component = componentById.get(originalId);
270
297
  if (!component) {
271
298
  throw new Error(`Component ${originalId} not found`);
272
299
  }
273
- compressedPageState.c[compressedId] = {
274
- i: ((_a = component.indexes) == null ? void 0 : _a.length) ? component.indexes : void 0
275
- };
300
+ const indexes = ((_a = component.indexes) == null ? void 0 : _a.length) ? component.indexes : void 0;
301
+ if (indexes) {
302
+ componentEntries.push(`${compressedId}${V2_KV_SEP}${indexes.join(V2_INDEX_SEP)}`);
303
+ } else {
304
+ componentEntries.push(compressedId);
305
+ }
276
306
  });
307
+ parts.push(componentEntries.join(V2_ENTRY_SEP));
308
+ let rulesStr = "";
277
309
  if (typeof payload.rules !== "undefined") {
278
310
  const ruleKeys = Object.keys(payload.rules);
279
311
  if (ruleKeys.length !== 0) {
280
- compressedPageState.rl = {};
281
312
  const sortedRuleKeys = ruleKeys.sort();
282
313
  const compressedRuleKeys = compressIds(sortedRuleKeys);
314
+ const ruleEntries = [];
283
315
  Object.keys(compressedRuleKeys).forEach((compressedId) => {
284
316
  const originalId = compressedRuleKeys[compressedId];
285
- compressedPageState.rl[compressedId] = payload.rules[originalId] ? 1 : 0;
317
+ ruleEntries.push(`${compressedId}${V2_KV_SEP}${payload.rules[originalId] ? 1 : 0}`);
286
318
  });
319
+ rulesStr = ruleEntries.join(V2_ENTRY_SEP);
287
320
  }
288
321
  }
289
- const result = JSON.stringify(compressedPageState);
290
- return encode ? encodeWithReplacements(result) : result;
322
+ parts.push(rulesStr);
323
+ let keysStr = "";
324
+ if (payload.keys) {
325
+ const keyEntries = Object.entries(payload.keys).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([k, v]) => `${toBase64Url(k)}${V2_KV_SEP}${toBase64Url(v)}`);
326
+ keysStr = keyEntries.join(V2_ENTRY_SEP);
327
+ }
328
+ parts.push(keysStr);
329
+ parts.push(encodeFlags(payload));
330
+ parts.push(payload.releaseId ? toBase64Url(payload.releaseId) : "");
331
+ parts.push(payload.locale ? toBase64Url(payload.locale) : "");
332
+ return parts.join(V2_FIELD_SEP);
333
+ };
334
+ var UNSAFE_KEYS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
335
+ var isUnsafeKey = (key) => UNSAFE_KEYS.has(key);
336
+ var deserializeV2 = (input) => {
337
+ var _a, _b, _c, _d;
338
+ const fields = input.split(V2_FIELD_SEP);
339
+ if (fields.length < 3) {
340
+ throw new Error(`Invalid serialized PageState: expected at least 3 fields but got ${fields.length}`);
341
+ }
342
+ const compositionStateRaw = fields[1];
343
+ const compositionState = Number.parseInt(compositionStateRaw, 10);
344
+ if (Number.isNaN(compositionState)) {
345
+ throw new Error(
346
+ `Invalid serialized PageState: compositionState '${compositionStateRaw}' is not a valid number`
347
+ );
348
+ }
349
+ const routePath = fromBase64Url(fields[2]);
350
+ const components = {};
351
+ const componentsStr = (_a = fields[3]) != null ? _a : "";
352
+ if (componentsStr) {
353
+ componentsStr.split(V2_ENTRY_SEP).forEach((entry) => {
354
+ const sepIdx = entry.indexOf(V2_KV_SEP);
355
+ if (sepIdx === -1) {
356
+ const id = entry;
357
+ if (isUnsafeKey(id)) {
358
+ return;
359
+ }
360
+ components[id] = {};
361
+ } else {
362
+ const id = entry.slice(0, sepIdx);
363
+ if (isUnsafeKey(id)) {
364
+ return;
365
+ }
366
+ const indexes = entry.slice(sepIdx + 1).split(V2_INDEX_SEP).map(Number);
367
+ components[id] = { indexes };
368
+ }
369
+ });
370
+ }
371
+ const rulesStr = (_b = fields[4]) != null ? _b : "";
372
+ let rules;
373
+ if (rulesStr) {
374
+ rules = {};
375
+ rulesStr.split(V2_ENTRY_SEP).forEach((entry) => {
376
+ const sepIdx = entry.indexOf(V2_KV_SEP);
377
+ if (sepIdx !== -1) {
378
+ const id = entry.slice(0, sepIdx);
379
+ if (isUnsafeKey(id)) {
380
+ return;
381
+ }
382
+ rules[id] = entry.slice(sepIdx + 1) === "1";
383
+ }
384
+ });
385
+ }
386
+ const keysStr = (_c = fields[5]) != null ? _c : "";
387
+ const keys = {};
388
+ if (keysStr) {
389
+ keysStr.split(V2_ENTRY_SEP).forEach((entry) => {
390
+ const sepIdx = entry.indexOf(V2_KV_SEP);
391
+ if (sepIdx !== -1) {
392
+ const k = fromBase64Url(entry.slice(0, sepIdx));
393
+ if (isUnsafeKey(k)) {
394
+ return;
395
+ }
396
+ const v = fromBase64Url(entry.slice(sepIdx + 1));
397
+ keys[k] = v;
398
+ }
399
+ });
400
+ }
401
+ const { defaultConsent, previewMode, isPrefetch } = decodeFlags((_d = fields[6]) != null ? _d : "0");
402
+ const releaseId = fields[7] ? fromBase64Url(fields[7]) : void 0;
403
+ const locale = fields[8] ? fromBase64Url(fields[8]) : void 0;
404
+ return {
405
+ compositionState,
406
+ routePath,
407
+ components,
408
+ rules,
409
+ keys,
410
+ releaseId,
411
+ defaultConsent,
412
+ previewMode,
413
+ locale,
414
+ isPrefetch
415
+ };
416
+ };
417
+ var serializeEvaluationResult = ({ payload }) => {
418
+ return serializeV2(payload);
291
419
  };
292
420
  var deserializeEvaluationResult = ({
293
421
  input: providedInput,
294
422
  decode = true
295
423
  }) => {
296
424
  var _a, _b, _c;
297
- const input = decode ? decodeWithReplacements(providedInput) : providedInput;
298
- const parsed = JSON.parse(input);
425
+ const input = decode ? decodeURIComponent(providedInput) : providedInput;
426
+ if (input.startsWith("2~")) {
427
+ return deserializeV2(input);
428
+ }
429
+ const jsonInput = decode ? atob(unreplaceReservedCharacters(input)) : input;
430
+ const parsed = JSON.parse(jsonInput);
299
431
  const pageState = {
300
432
  compositionState: parsed.cs,
301
433
  routePath: parsed.r,
@@ -305,7 +437,8 @@ var deserializeEvaluationResult = ({
305
437
  defaultConsent: typeof parsed.dc !== "undefined" ? parsed.dc === 1 : void 0,
306
438
  previewMode: parsed.pm === "e" ? "editor" : parsed.pm === "p" ? "preview" : void 0,
307
439
  rules: parsed.rl ? Object.fromEntries(Object.entries(parsed.rl).map(([key, value]) => [key, value === 1])) : void 0,
308
- locale: (_c = parsed.l) != null ? _c : void 0
440
+ locale: (_c = parsed.l) != null ? _c : void 0,
441
+ isPrefetch: parsed.pf === 1 ? true : void 0
309
442
  };
310
443
  Object.keys(parsed.c).forEach((id) => {
311
444
  const component = parsed.c[id];
package/dist/index.mjs CHANGED
@@ -160,23 +160,11 @@ var resolveRuleFromPageState = ({
160
160
  }
161
161
  return value;
162
162
  };
163
- var replaceReservedCharacters = (value) => {
164
- return ENCODED_RESERVED_CHARACTERS.reduce((acc, char) => {
165
- return acc.replaceAll(char.character, char.replacement);
166
- }, value);
167
- };
168
163
  var unreplaceReservedCharacters = (value) => {
169
164
  return ENCODED_RESERVED_CHARACTERS.reduce((acc, char) => {
170
165
  return acc.replaceAll(char.replacement, char.character);
171
166
  }, value);
172
167
  };
173
- var encodeWithReplacements = (value) => {
174
- return replaceReservedCharacters(btoa(value));
175
- };
176
- var decodeWithReplacements = (value) => {
177
- const urlDecoded = decodeURIComponent(value);
178
- return atob(unreplaceReservedCharacters(urlDecoded));
179
- };
180
168
  var compressIds = (ids) => {
181
169
  const uuidV4Length = 36;
182
170
  let compressedRegistry;
@@ -206,21 +194,58 @@ var compressIds = (ids) => {
206
194
  );
207
195
  };
208
196
  var NAMESPACE_UUID = "00000000-0000-0000-0000-000000000000";
209
- var serializeEvaluationResult = ({
210
- payload,
211
- encode = true
212
- }) => {
213
- const compressedPageState = {
214
- cs: payload.compositionState,
215
- r: payload.routePath,
216
- c: {},
217
- k: payload.keys,
218
- ri: payload.releaseId,
219
- dc: typeof payload.defaultConsent !== "undefined" ? payload.defaultConsent ? 1 : 0 : void 0,
220
- pm: payload.previewMode === "editor" ? "e" : payload.previewMode === "preview" ? "p" : void 0,
221
- rl: void 0,
222
- l: payload.locale
197
+ var V2_FIELD_SEP = "~";
198
+ var V2_ENTRY_SEP = ".";
199
+ var V2_KV_SEP = ":";
200
+ var V2_INDEX_SEP = ",";
201
+ var toBase64Url = (value) => {
202
+ const bytes = new TextEncoder().encode(value);
203
+ const binary = Array.from(bytes, (b) => String.fromCharCode(b)).join("");
204
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
205
+ };
206
+ var fromBase64Url = (value) => {
207
+ let base64 = value.replace(/-/g, "+").replace(/_/g, "/");
208
+ const pad = base64.length % 4;
209
+ if (pad === 2) {
210
+ base64 += "==";
211
+ } else if (pad === 3) {
212
+ base64 += "=";
213
+ }
214
+ const binary = atob(base64);
215
+ const bytes = Uint8Array.from(binary, (c) => c.charCodeAt(0));
216
+ return new TextDecoder().decode(bytes);
217
+ };
218
+ var encodeFlags = (payload) => {
219
+ let flags = 0;
220
+ if (typeof payload.defaultConsent !== "undefined") {
221
+ flags |= 1;
222
+ if (payload.defaultConsent) {
223
+ flags |= 2;
224
+ }
225
+ }
226
+ if (payload.previewMode === "editor") {
227
+ flags |= 4;
228
+ }
229
+ if (payload.previewMode === "preview") {
230
+ flags |= 8;
231
+ }
232
+ if (payload.isPrefetch) {
233
+ flags |= 16;
234
+ }
235
+ return flags.toString(16);
236
+ };
237
+ var decodeFlags = (hex) => {
238
+ const flags = parseInt(hex, 16) || 0;
239
+ return {
240
+ defaultConsent: flags & 1 ? Boolean(flags & 2) : void 0,
241
+ previewMode: flags & 4 ? "editor" : flags & 8 ? "preview" : void 0,
242
+ isPrefetch: flags & 16 ? true : void 0
223
243
  };
244
+ };
245
+ var serializeV2 = (payload) => {
246
+ const parts = ["2"];
247
+ parts.push(String(payload.compositionState));
248
+ parts.push(toBase64Url(payload.routePath));
224
249
  const componentKeys = Object.keys(payload.components);
225
250
  const sortedKeys = componentKeys.sort();
226
251
  const sortedComponents = sortedKeys.map((key) => ({
@@ -228,39 +253,146 @@ var serializeEvaluationResult = ({
228
253
  ...payload.components[key]
229
254
  }));
230
255
  const compressedComponentIds = compressIds(sortedComponents.map((c) => c._id));
256
+ const componentById = new Map(sortedComponents.map((c) => [c._id, c]));
257
+ const componentEntries = [];
231
258
  Object.keys(compressedComponentIds).forEach((compressedId) => {
232
259
  var _a;
233
260
  const originalId = compressedComponentIds[compressedId];
234
- const component = sortedComponents.find((c) => c._id === originalId);
261
+ const component = componentById.get(originalId);
235
262
  if (!component) {
236
263
  throw new Error(`Component ${originalId} not found`);
237
264
  }
238
- compressedPageState.c[compressedId] = {
239
- i: ((_a = component.indexes) == null ? void 0 : _a.length) ? component.indexes : void 0
240
- };
265
+ const indexes = ((_a = component.indexes) == null ? void 0 : _a.length) ? component.indexes : void 0;
266
+ if (indexes) {
267
+ componentEntries.push(`${compressedId}${V2_KV_SEP}${indexes.join(V2_INDEX_SEP)}`);
268
+ } else {
269
+ componentEntries.push(compressedId);
270
+ }
241
271
  });
272
+ parts.push(componentEntries.join(V2_ENTRY_SEP));
273
+ let rulesStr = "";
242
274
  if (typeof payload.rules !== "undefined") {
243
275
  const ruleKeys = Object.keys(payload.rules);
244
276
  if (ruleKeys.length !== 0) {
245
- compressedPageState.rl = {};
246
277
  const sortedRuleKeys = ruleKeys.sort();
247
278
  const compressedRuleKeys = compressIds(sortedRuleKeys);
279
+ const ruleEntries = [];
248
280
  Object.keys(compressedRuleKeys).forEach((compressedId) => {
249
281
  const originalId = compressedRuleKeys[compressedId];
250
- compressedPageState.rl[compressedId] = payload.rules[originalId] ? 1 : 0;
282
+ ruleEntries.push(`${compressedId}${V2_KV_SEP}${payload.rules[originalId] ? 1 : 0}`);
251
283
  });
284
+ rulesStr = ruleEntries.join(V2_ENTRY_SEP);
252
285
  }
253
286
  }
254
- const result = JSON.stringify(compressedPageState);
255
- return encode ? encodeWithReplacements(result) : result;
287
+ parts.push(rulesStr);
288
+ let keysStr = "";
289
+ if (payload.keys) {
290
+ const keyEntries = Object.entries(payload.keys).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([k, v]) => `${toBase64Url(k)}${V2_KV_SEP}${toBase64Url(v)}`);
291
+ keysStr = keyEntries.join(V2_ENTRY_SEP);
292
+ }
293
+ parts.push(keysStr);
294
+ parts.push(encodeFlags(payload));
295
+ parts.push(payload.releaseId ? toBase64Url(payload.releaseId) : "");
296
+ parts.push(payload.locale ? toBase64Url(payload.locale) : "");
297
+ return parts.join(V2_FIELD_SEP);
298
+ };
299
+ var UNSAFE_KEYS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
300
+ var isUnsafeKey = (key) => UNSAFE_KEYS.has(key);
301
+ var deserializeV2 = (input) => {
302
+ var _a, _b, _c, _d;
303
+ const fields = input.split(V2_FIELD_SEP);
304
+ if (fields.length < 3) {
305
+ throw new Error(`Invalid serialized PageState: expected at least 3 fields but got ${fields.length}`);
306
+ }
307
+ const compositionStateRaw = fields[1];
308
+ const compositionState = Number.parseInt(compositionStateRaw, 10);
309
+ if (Number.isNaN(compositionState)) {
310
+ throw new Error(
311
+ `Invalid serialized PageState: compositionState '${compositionStateRaw}' is not a valid number`
312
+ );
313
+ }
314
+ const routePath = fromBase64Url(fields[2]);
315
+ const components = {};
316
+ const componentsStr = (_a = fields[3]) != null ? _a : "";
317
+ if (componentsStr) {
318
+ componentsStr.split(V2_ENTRY_SEP).forEach((entry) => {
319
+ const sepIdx = entry.indexOf(V2_KV_SEP);
320
+ if (sepIdx === -1) {
321
+ const id = entry;
322
+ if (isUnsafeKey(id)) {
323
+ return;
324
+ }
325
+ components[id] = {};
326
+ } else {
327
+ const id = entry.slice(0, sepIdx);
328
+ if (isUnsafeKey(id)) {
329
+ return;
330
+ }
331
+ const indexes = entry.slice(sepIdx + 1).split(V2_INDEX_SEP).map(Number);
332
+ components[id] = { indexes };
333
+ }
334
+ });
335
+ }
336
+ const rulesStr = (_b = fields[4]) != null ? _b : "";
337
+ let rules;
338
+ if (rulesStr) {
339
+ rules = {};
340
+ rulesStr.split(V2_ENTRY_SEP).forEach((entry) => {
341
+ const sepIdx = entry.indexOf(V2_KV_SEP);
342
+ if (sepIdx !== -1) {
343
+ const id = entry.slice(0, sepIdx);
344
+ if (isUnsafeKey(id)) {
345
+ return;
346
+ }
347
+ rules[id] = entry.slice(sepIdx + 1) === "1";
348
+ }
349
+ });
350
+ }
351
+ const keysStr = (_c = fields[5]) != null ? _c : "";
352
+ const keys = {};
353
+ if (keysStr) {
354
+ keysStr.split(V2_ENTRY_SEP).forEach((entry) => {
355
+ const sepIdx = entry.indexOf(V2_KV_SEP);
356
+ if (sepIdx !== -1) {
357
+ const k = fromBase64Url(entry.slice(0, sepIdx));
358
+ if (isUnsafeKey(k)) {
359
+ return;
360
+ }
361
+ const v = fromBase64Url(entry.slice(sepIdx + 1));
362
+ keys[k] = v;
363
+ }
364
+ });
365
+ }
366
+ const { defaultConsent, previewMode, isPrefetch } = decodeFlags((_d = fields[6]) != null ? _d : "0");
367
+ const releaseId = fields[7] ? fromBase64Url(fields[7]) : void 0;
368
+ const locale = fields[8] ? fromBase64Url(fields[8]) : void 0;
369
+ return {
370
+ compositionState,
371
+ routePath,
372
+ components,
373
+ rules,
374
+ keys,
375
+ releaseId,
376
+ defaultConsent,
377
+ previewMode,
378
+ locale,
379
+ isPrefetch
380
+ };
381
+ };
382
+ var serializeEvaluationResult = ({ payload }) => {
383
+ return serializeV2(payload);
256
384
  };
257
385
  var deserializeEvaluationResult = ({
258
386
  input: providedInput,
259
387
  decode = true
260
388
  }) => {
261
389
  var _a, _b, _c;
262
- const input = decode ? decodeWithReplacements(providedInput) : providedInput;
263
- const parsed = JSON.parse(input);
390
+ const input = decode ? decodeURIComponent(providedInput) : providedInput;
391
+ if (input.startsWith("2~")) {
392
+ return deserializeV2(input);
393
+ }
394
+ const jsonInput = decode ? atob(unreplaceReservedCharacters(input)) : input;
395
+ const parsed = JSON.parse(jsonInput);
264
396
  const pageState = {
265
397
  compositionState: parsed.cs,
266
398
  routePath: parsed.r,
@@ -270,7 +402,8 @@ var deserializeEvaluationResult = ({
270
402
  defaultConsent: typeof parsed.dc !== "undefined" ? parsed.dc === 1 : void 0,
271
403
  previewMode: parsed.pm === "e" ? "editor" : parsed.pm === "p" ? "preview" : void 0,
272
404
  rules: parsed.rl ? Object.fromEntries(Object.entries(parsed.rl).map(([key, value]) => [key, value === 1])) : void 0,
273
- locale: (_c = parsed.l) != null ? _c : void 0
405
+ locale: (_c = parsed.l) != null ? _c : void 0,
406
+ isPrefetch: parsed.pf === 1 ? true : void 0
274
407
  };
275
408
  Object.keys(parsed.c).forEach((id) => {
276
409
  const component = parsed.c[id];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniformdev/next-app-router-shared",
3
- "version": "20.48.0",
3
+ "version": "20.48.1-alpha.11+129de094d4",
4
4
  "license": "SEE LICENSE IN LICENSE.txt",
5
5
  "scripts": {
6
6
  "build": "tsup",
@@ -36,8 +36,8 @@
36
36
  "vitest": "3.2.4"
37
37
  },
38
38
  "dependencies": {
39
- "@uniformdev/canvas": "20.48.0",
40
- "@uniformdev/context": "20.48.0",
39
+ "@uniformdev/canvas": "20.48.1-alpha.11+129de094d4",
40
+ "@uniformdev/context": "20.48.1-alpha.11+129de094d4",
41
41
  "uuid": "9.0.1"
42
42
  },
43
43
  "engines": {
@@ -51,5 +51,5 @@
51
51
  "publishConfig": {
52
52
  "access": "public"
53
53
  },
54
- "gitHead": "20645902724e610cbd4f75fe062aa5034db0a7db"
54
+ "gitHead": "129de094d4db58d4adfcae07d6ab0fe506ca739e"
55
55
  }