@uniformdev/next-app-router-shared 20.50.2-alpha.1 → 20.50.2-alpha.39

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
@@ -79,9 +79,18 @@ declare const resolveRuleFromPageState: ({ pageState, rule, }: {
79
79
  pageState: PageState;
80
80
  rule: VisibilityParameterValue;
81
81
  }) => boolean | undefined;
82
- declare const serializeEvaluationResult: ({ payload, encode, }: {
82
+ declare const serializeEvaluationResult: ({ payload, allComponentIds, }: {
83
83
  payload: PageState;
84
- encode?: boolean;
84
+ /**
85
+ * All test and personalization component IDs on the page, including ones inside
86
+ * unselected personalization variants that were not evaluated by the middleware.
87
+ *
88
+ * Not serialized into the output. Only used to widen the prefix compression
89
+ * namespace so that compressed keys are long enough to be unique across every
90
+ * component on the page, preventing false-positive prefix matches during
91
+ * {@link resolveComponentFromPageState} lookups.
92
+ */
93
+ allComponentIds?: string[];
85
94
  }) => string;
86
95
  declare const deserializeEvaluationResult: ({ input: providedInput, decode, }: {
87
96
  input: string;
package/dist/index.d.ts CHANGED
@@ -79,9 +79,18 @@ declare const resolveRuleFromPageState: ({ pageState, rule, }: {
79
79
  pageState: PageState;
80
80
  rule: VisibilityParameterValue;
81
81
  }) => boolean | undefined;
82
- declare const serializeEvaluationResult: ({ payload, encode, }: {
82
+ declare const serializeEvaluationResult: ({ payload, allComponentIds, }: {
83
83
  payload: PageState;
84
- encode?: boolean;
84
+ /**
85
+ * All test and personalization component IDs on the page, including ones inside
86
+ * unselected personalization variants that were not evaluated by the middleware.
87
+ *
88
+ * Not serialized into the output. Only used to widen the prefix compression
89
+ * namespace so that compressed keys are long enough to be unique across every
90
+ * component on the page, preventing false-positive prefix matches during
91
+ * {@link resolveComponentFromPageState} lookups.
92
+ */
93
+ allComponentIds?: string[];
85
94
  }) => string;
86
95
  declare const deserializeEvaluationResult: ({ input: providedInput, decode, }: {
87
96
  input: string;
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,62 +194,213 @@ 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,
223
- pf: payload.isPrefetch ? 1 : void 0
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
224
243
  };
244
+ };
245
+ var serializeV2 = (payload, allComponentIds) => {
246
+ const parts = ["2"];
247
+ parts.push(String(payload.compositionState));
248
+ parts.push(toBase64Url(payload.routePath));
225
249
  const componentKeys = Object.keys(payload.components);
226
250
  const sortedKeys = componentKeys.sort();
227
251
  const sortedComponents = sortedKeys.map((key) => ({
228
252
  _id: isNotARegularUuid(key) ? v5(key, NAMESPACE_UUID) : key,
229
253
  ...payload.components[key]
230
254
  }));
231
- const compressedComponentIds = compressIds(sortedComponents.map((c) => c._id));
255
+ const evaluatedIds = sortedComponents.map((c) => c._id);
256
+ const normalizedAllIds = allComponentIds == null ? void 0 : allComponentIds.map(
257
+ (id) => isNotARegularUuid(id) ? v5(id, NAMESPACE_UUID) : id
258
+ );
259
+ const compressionNamespace = normalizedAllIds ? [.../* @__PURE__ */ new Set([...evaluatedIds, ...normalizedAllIds])].sort() : evaluatedIds;
260
+ const compressedComponentIds = compressIds(compressionNamespace);
261
+ const componentById = new Map(sortedComponents.map((c) => [c._id, c]));
262
+ const componentEntries = [];
232
263
  Object.keys(compressedComponentIds).forEach((compressedId) => {
233
264
  var _a;
234
265
  const originalId = compressedComponentIds[compressedId];
235
- const component = sortedComponents.find((c) => c._id === originalId);
266
+ const component = componentById.get(originalId);
236
267
  if (!component) {
237
- throw new Error(`Component ${originalId} not found`);
268
+ return;
269
+ }
270
+ const indexes = ((_a = component.indexes) == null ? void 0 : _a.length) ? component.indexes : void 0;
271
+ if (indexes) {
272
+ componentEntries.push(`${compressedId}${V2_KV_SEP}${indexes.join(V2_INDEX_SEP)}`);
273
+ } else {
274
+ componentEntries.push(compressedId);
238
275
  }
239
- compressedPageState.c[compressedId] = {
240
- i: ((_a = component.indexes) == null ? void 0 : _a.length) ? component.indexes : void 0
241
- };
242
276
  });
277
+ parts.push(componentEntries.join(V2_ENTRY_SEP));
278
+ let rulesStr = "";
243
279
  if (typeof payload.rules !== "undefined") {
244
280
  const ruleKeys = Object.keys(payload.rules);
245
281
  if (ruleKeys.length !== 0) {
246
- compressedPageState.rl = {};
247
282
  const sortedRuleKeys = ruleKeys.sort();
248
283
  const compressedRuleKeys = compressIds(sortedRuleKeys);
284
+ const ruleEntries = [];
249
285
  Object.keys(compressedRuleKeys).forEach((compressedId) => {
250
286
  const originalId = compressedRuleKeys[compressedId];
251
- compressedPageState.rl[compressedId] = payload.rules[originalId] ? 1 : 0;
287
+ ruleEntries.push(`${compressedId}${V2_KV_SEP}${payload.rules[originalId] ? 1 : 0}`);
252
288
  });
289
+ rulesStr = ruleEntries.join(V2_ENTRY_SEP);
253
290
  }
254
291
  }
255
- const result = JSON.stringify(compressedPageState);
256
- return encode ? encodeWithReplacements(result) : result;
292
+ parts.push(rulesStr);
293
+ let keysStr = "";
294
+ if (payload.keys) {
295
+ 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)}`);
296
+ keysStr = keyEntries.join(V2_ENTRY_SEP);
297
+ }
298
+ parts.push(keysStr);
299
+ parts.push(encodeFlags(payload));
300
+ parts.push(payload.releaseId ? toBase64Url(payload.releaseId) : "");
301
+ parts.push(payload.locale ? toBase64Url(payload.locale) : "");
302
+ return parts.join(V2_FIELD_SEP);
303
+ };
304
+ var UNSAFE_KEYS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
305
+ var isUnsafeKey = (key) => UNSAFE_KEYS.has(key);
306
+ var deserializeV2 = (input) => {
307
+ var _a, _b, _c, _d;
308
+ const fields = input.split(V2_FIELD_SEP);
309
+ if (fields.length < 3) {
310
+ throw new Error(`Invalid serialized PageState: expected at least 3 fields but got ${fields.length}`);
311
+ }
312
+ const compositionStateRaw = fields[1];
313
+ const compositionState = Number.parseInt(compositionStateRaw, 10);
314
+ if (Number.isNaN(compositionState)) {
315
+ throw new Error(
316
+ `Invalid serialized PageState: compositionState '${compositionStateRaw}' is not a valid number`
317
+ );
318
+ }
319
+ const routePath = fromBase64Url(fields[2]);
320
+ const components = {};
321
+ const componentsStr = (_a = fields[3]) != null ? _a : "";
322
+ if (componentsStr) {
323
+ componentsStr.split(V2_ENTRY_SEP).forEach((entry) => {
324
+ const sepIdx = entry.indexOf(V2_KV_SEP);
325
+ if (sepIdx === -1) {
326
+ const id = entry;
327
+ if (isUnsafeKey(id)) {
328
+ return;
329
+ }
330
+ components[id] = {};
331
+ } else {
332
+ const id = entry.slice(0, sepIdx);
333
+ if (isUnsafeKey(id)) {
334
+ return;
335
+ }
336
+ const indexes = entry.slice(sepIdx + 1).split(V2_INDEX_SEP).map(Number);
337
+ components[id] = { indexes };
338
+ }
339
+ });
340
+ }
341
+ const rulesStr = (_b = fields[4]) != null ? _b : "";
342
+ let rules;
343
+ if (rulesStr) {
344
+ rules = {};
345
+ rulesStr.split(V2_ENTRY_SEP).forEach((entry) => {
346
+ const sepIdx = entry.indexOf(V2_KV_SEP);
347
+ if (sepIdx !== -1) {
348
+ const id = entry.slice(0, sepIdx);
349
+ if (isUnsafeKey(id)) {
350
+ return;
351
+ }
352
+ rules[id] = entry.slice(sepIdx + 1) === "1";
353
+ }
354
+ });
355
+ }
356
+ const keysStr = (_c = fields[5]) != null ? _c : "";
357
+ const keys = {};
358
+ if (keysStr) {
359
+ keysStr.split(V2_ENTRY_SEP).forEach((entry) => {
360
+ const sepIdx = entry.indexOf(V2_KV_SEP);
361
+ if (sepIdx !== -1) {
362
+ const k = fromBase64Url(entry.slice(0, sepIdx));
363
+ if (isUnsafeKey(k)) {
364
+ return;
365
+ }
366
+ const v = fromBase64Url(entry.slice(sepIdx + 1));
367
+ keys[k] = v;
368
+ }
369
+ });
370
+ }
371
+ const { defaultConsent, previewMode, isPrefetch } = decodeFlags((_d = fields[6]) != null ? _d : "0");
372
+ const releaseId = fields[7] ? fromBase64Url(fields[7]) : void 0;
373
+ const locale = fields[8] ? fromBase64Url(fields[8]) : void 0;
374
+ return {
375
+ compositionState,
376
+ routePath,
377
+ components,
378
+ rules,
379
+ keys,
380
+ releaseId,
381
+ defaultConsent,
382
+ previewMode,
383
+ locale,
384
+ isPrefetch
385
+ };
386
+ };
387
+ var serializeEvaluationResult = ({
388
+ payload,
389
+ allComponentIds
390
+ }) => {
391
+ return serializeV2(payload, allComponentIds);
257
392
  };
258
393
  var deserializeEvaluationResult = ({
259
394
  input: providedInput,
260
395
  decode = true
261
396
  }) => {
262
397
  var _a, _b, _c;
263
- const input = decode ? decodeWithReplacements(providedInput) : providedInput;
264
- const parsed = JSON.parse(input);
398
+ const input = decode ? decodeURIComponent(providedInput) : providedInput;
399
+ if (input.startsWith("2~")) {
400
+ return deserializeV2(input);
401
+ }
402
+ const jsonInput = decode ? atob(unreplaceReservedCharacters(input)) : input;
403
+ const parsed = JSON.parse(jsonInput);
265
404
  const pageState = {
266
405
  compositionState: parsed.cs,
267
406
  routePath: parsed.r,
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,62 +229,213 @@ 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,
258
- pf: payload.isPrefetch ? 1 : void 0
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
259
278
  };
279
+ };
280
+ var serializeV2 = (payload, allComponentIds) => {
281
+ const parts = ["2"];
282
+ parts.push(String(payload.compositionState));
283
+ parts.push(toBase64Url(payload.routePath));
260
284
  const componentKeys = Object.keys(payload.components);
261
285
  const sortedKeys = componentKeys.sort();
262
286
  const sortedComponents = sortedKeys.map((key) => ({
263
287
  _id: isNotARegularUuid(key) ? (0, import_uuid.v5)(key, NAMESPACE_UUID) : key,
264
288
  ...payload.components[key]
265
289
  }));
266
- const compressedComponentIds = compressIds(sortedComponents.map((c) => c._id));
290
+ const evaluatedIds = sortedComponents.map((c) => c._id);
291
+ const normalizedAllIds = allComponentIds == null ? void 0 : allComponentIds.map(
292
+ (id) => isNotARegularUuid(id) ? (0, import_uuid.v5)(id, NAMESPACE_UUID) : id
293
+ );
294
+ const compressionNamespace = normalizedAllIds ? [.../* @__PURE__ */ new Set([...evaluatedIds, ...normalizedAllIds])].sort() : evaluatedIds;
295
+ const compressedComponentIds = compressIds(compressionNamespace);
296
+ const componentById = new Map(sortedComponents.map((c) => [c._id, c]));
297
+ const componentEntries = [];
267
298
  Object.keys(compressedComponentIds).forEach((compressedId) => {
268
299
  var _a;
269
300
  const originalId = compressedComponentIds[compressedId];
270
- const component = sortedComponents.find((c) => c._id === originalId);
301
+ const component = componentById.get(originalId);
271
302
  if (!component) {
272
- throw new Error(`Component ${originalId} not found`);
303
+ return;
304
+ }
305
+ const indexes = ((_a = component.indexes) == null ? void 0 : _a.length) ? component.indexes : void 0;
306
+ if (indexes) {
307
+ componentEntries.push(`${compressedId}${V2_KV_SEP}${indexes.join(V2_INDEX_SEP)}`);
308
+ } else {
309
+ componentEntries.push(compressedId);
273
310
  }
274
- compressedPageState.c[compressedId] = {
275
- i: ((_a = component.indexes) == null ? void 0 : _a.length) ? component.indexes : void 0
276
- };
277
311
  });
312
+ parts.push(componentEntries.join(V2_ENTRY_SEP));
313
+ let rulesStr = "";
278
314
  if (typeof payload.rules !== "undefined") {
279
315
  const ruleKeys = Object.keys(payload.rules);
280
316
  if (ruleKeys.length !== 0) {
281
- compressedPageState.rl = {};
282
317
  const sortedRuleKeys = ruleKeys.sort();
283
318
  const compressedRuleKeys = compressIds(sortedRuleKeys);
319
+ const ruleEntries = [];
284
320
  Object.keys(compressedRuleKeys).forEach((compressedId) => {
285
321
  const originalId = compressedRuleKeys[compressedId];
286
- compressedPageState.rl[compressedId] = payload.rules[originalId] ? 1 : 0;
322
+ ruleEntries.push(`${compressedId}${V2_KV_SEP}${payload.rules[originalId] ? 1 : 0}`);
287
323
  });
324
+ rulesStr = ruleEntries.join(V2_ENTRY_SEP);
288
325
  }
289
326
  }
290
- const result = JSON.stringify(compressedPageState);
291
- return encode ? encodeWithReplacements(result) : result;
327
+ parts.push(rulesStr);
328
+ let keysStr = "";
329
+ if (payload.keys) {
330
+ 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)}`);
331
+ keysStr = keyEntries.join(V2_ENTRY_SEP);
332
+ }
333
+ parts.push(keysStr);
334
+ parts.push(encodeFlags(payload));
335
+ parts.push(payload.releaseId ? toBase64Url(payload.releaseId) : "");
336
+ parts.push(payload.locale ? toBase64Url(payload.locale) : "");
337
+ return parts.join(V2_FIELD_SEP);
338
+ };
339
+ var UNSAFE_KEYS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
340
+ var isUnsafeKey = (key) => UNSAFE_KEYS.has(key);
341
+ var deserializeV2 = (input) => {
342
+ var _a, _b, _c, _d;
343
+ const fields = input.split(V2_FIELD_SEP);
344
+ if (fields.length < 3) {
345
+ throw new Error(`Invalid serialized PageState: expected at least 3 fields but got ${fields.length}`);
346
+ }
347
+ const compositionStateRaw = fields[1];
348
+ const compositionState = Number.parseInt(compositionStateRaw, 10);
349
+ if (Number.isNaN(compositionState)) {
350
+ throw new Error(
351
+ `Invalid serialized PageState: compositionState '${compositionStateRaw}' is not a valid number`
352
+ );
353
+ }
354
+ const routePath = fromBase64Url(fields[2]);
355
+ const components = {};
356
+ const componentsStr = (_a = fields[3]) != null ? _a : "";
357
+ if (componentsStr) {
358
+ componentsStr.split(V2_ENTRY_SEP).forEach((entry) => {
359
+ const sepIdx = entry.indexOf(V2_KV_SEP);
360
+ if (sepIdx === -1) {
361
+ const id = entry;
362
+ if (isUnsafeKey(id)) {
363
+ return;
364
+ }
365
+ components[id] = {};
366
+ } else {
367
+ const id = entry.slice(0, sepIdx);
368
+ if (isUnsafeKey(id)) {
369
+ return;
370
+ }
371
+ const indexes = entry.slice(sepIdx + 1).split(V2_INDEX_SEP).map(Number);
372
+ components[id] = { indexes };
373
+ }
374
+ });
375
+ }
376
+ const rulesStr = (_b = fields[4]) != null ? _b : "";
377
+ let rules;
378
+ if (rulesStr) {
379
+ rules = {};
380
+ rulesStr.split(V2_ENTRY_SEP).forEach((entry) => {
381
+ const sepIdx = entry.indexOf(V2_KV_SEP);
382
+ if (sepIdx !== -1) {
383
+ const id = entry.slice(0, sepIdx);
384
+ if (isUnsafeKey(id)) {
385
+ return;
386
+ }
387
+ rules[id] = entry.slice(sepIdx + 1) === "1";
388
+ }
389
+ });
390
+ }
391
+ const keysStr = (_c = fields[5]) != null ? _c : "";
392
+ const keys = {};
393
+ if (keysStr) {
394
+ keysStr.split(V2_ENTRY_SEP).forEach((entry) => {
395
+ const sepIdx = entry.indexOf(V2_KV_SEP);
396
+ if (sepIdx !== -1) {
397
+ const k = fromBase64Url(entry.slice(0, sepIdx));
398
+ if (isUnsafeKey(k)) {
399
+ return;
400
+ }
401
+ const v = fromBase64Url(entry.slice(sepIdx + 1));
402
+ keys[k] = v;
403
+ }
404
+ });
405
+ }
406
+ const { defaultConsent, previewMode, isPrefetch } = decodeFlags((_d = fields[6]) != null ? _d : "0");
407
+ const releaseId = fields[7] ? fromBase64Url(fields[7]) : void 0;
408
+ const locale = fields[8] ? fromBase64Url(fields[8]) : void 0;
409
+ return {
410
+ compositionState,
411
+ routePath,
412
+ components,
413
+ rules,
414
+ keys,
415
+ releaseId,
416
+ defaultConsent,
417
+ previewMode,
418
+ locale,
419
+ isPrefetch
420
+ };
421
+ };
422
+ var serializeEvaluationResult = ({
423
+ payload,
424
+ allComponentIds
425
+ }) => {
426
+ return serializeV2(payload, allComponentIds);
292
427
  };
293
428
  var deserializeEvaluationResult = ({
294
429
  input: providedInput,
295
430
  decode = true
296
431
  }) => {
297
432
  var _a, _b, _c;
298
- const input = decode ? decodeWithReplacements(providedInput) : providedInput;
299
- const parsed = JSON.parse(input);
433
+ const input = decode ? decodeURIComponent(providedInput) : providedInput;
434
+ if (input.startsWith("2~")) {
435
+ return deserializeV2(input);
436
+ }
437
+ const jsonInput = decode ? atob(unreplaceReservedCharacters(input)) : input;
438
+ const parsed = JSON.parse(jsonInput);
300
439
  const pageState = {
301
440
  compositionState: parsed.cs,
302
441
  routePath: parsed.r,
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,62 +194,213 @@ 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,
223
- pf: payload.isPrefetch ? 1 : void 0
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
224
243
  };
244
+ };
245
+ var serializeV2 = (payload, allComponentIds) => {
246
+ const parts = ["2"];
247
+ parts.push(String(payload.compositionState));
248
+ parts.push(toBase64Url(payload.routePath));
225
249
  const componentKeys = Object.keys(payload.components);
226
250
  const sortedKeys = componentKeys.sort();
227
251
  const sortedComponents = sortedKeys.map((key) => ({
228
252
  _id: isNotARegularUuid(key) ? v5(key, NAMESPACE_UUID) : key,
229
253
  ...payload.components[key]
230
254
  }));
231
- const compressedComponentIds = compressIds(sortedComponents.map((c) => c._id));
255
+ const evaluatedIds = sortedComponents.map((c) => c._id);
256
+ const normalizedAllIds = allComponentIds == null ? void 0 : allComponentIds.map(
257
+ (id) => isNotARegularUuid(id) ? v5(id, NAMESPACE_UUID) : id
258
+ );
259
+ const compressionNamespace = normalizedAllIds ? [.../* @__PURE__ */ new Set([...evaluatedIds, ...normalizedAllIds])].sort() : evaluatedIds;
260
+ const compressedComponentIds = compressIds(compressionNamespace);
261
+ const componentById = new Map(sortedComponents.map((c) => [c._id, c]));
262
+ const componentEntries = [];
232
263
  Object.keys(compressedComponentIds).forEach((compressedId) => {
233
264
  var _a;
234
265
  const originalId = compressedComponentIds[compressedId];
235
- const component = sortedComponents.find((c) => c._id === originalId);
266
+ const component = componentById.get(originalId);
236
267
  if (!component) {
237
- throw new Error(`Component ${originalId} not found`);
268
+ return;
269
+ }
270
+ const indexes = ((_a = component.indexes) == null ? void 0 : _a.length) ? component.indexes : void 0;
271
+ if (indexes) {
272
+ componentEntries.push(`${compressedId}${V2_KV_SEP}${indexes.join(V2_INDEX_SEP)}`);
273
+ } else {
274
+ componentEntries.push(compressedId);
238
275
  }
239
- compressedPageState.c[compressedId] = {
240
- i: ((_a = component.indexes) == null ? void 0 : _a.length) ? component.indexes : void 0
241
- };
242
276
  });
277
+ parts.push(componentEntries.join(V2_ENTRY_SEP));
278
+ let rulesStr = "";
243
279
  if (typeof payload.rules !== "undefined") {
244
280
  const ruleKeys = Object.keys(payload.rules);
245
281
  if (ruleKeys.length !== 0) {
246
- compressedPageState.rl = {};
247
282
  const sortedRuleKeys = ruleKeys.sort();
248
283
  const compressedRuleKeys = compressIds(sortedRuleKeys);
284
+ const ruleEntries = [];
249
285
  Object.keys(compressedRuleKeys).forEach((compressedId) => {
250
286
  const originalId = compressedRuleKeys[compressedId];
251
- compressedPageState.rl[compressedId] = payload.rules[originalId] ? 1 : 0;
287
+ ruleEntries.push(`${compressedId}${V2_KV_SEP}${payload.rules[originalId] ? 1 : 0}`);
252
288
  });
289
+ rulesStr = ruleEntries.join(V2_ENTRY_SEP);
253
290
  }
254
291
  }
255
- const result = JSON.stringify(compressedPageState);
256
- return encode ? encodeWithReplacements(result) : result;
292
+ parts.push(rulesStr);
293
+ let keysStr = "";
294
+ if (payload.keys) {
295
+ 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)}`);
296
+ keysStr = keyEntries.join(V2_ENTRY_SEP);
297
+ }
298
+ parts.push(keysStr);
299
+ parts.push(encodeFlags(payload));
300
+ parts.push(payload.releaseId ? toBase64Url(payload.releaseId) : "");
301
+ parts.push(payload.locale ? toBase64Url(payload.locale) : "");
302
+ return parts.join(V2_FIELD_SEP);
303
+ };
304
+ var UNSAFE_KEYS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
305
+ var isUnsafeKey = (key) => UNSAFE_KEYS.has(key);
306
+ var deserializeV2 = (input) => {
307
+ var _a, _b, _c, _d;
308
+ const fields = input.split(V2_FIELD_SEP);
309
+ if (fields.length < 3) {
310
+ throw new Error(`Invalid serialized PageState: expected at least 3 fields but got ${fields.length}`);
311
+ }
312
+ const compositionStateRaw = fields[1];
313
+ const compositionState = Number.parseInt(compositionStateRaw, 10);
314
+ if (Number.isNaN(compositionState)) {
315
+ throw new Error(
316
+ `Invalid serialized PageState: compositionState '${compositionStateRaw}' is not a valid number`
317
+ );
318
+ }
319
+ const routePath = fromBase64Url(fields[2]);
320
+ const components = {};
321
+ const componentsStr = (_a = fields[3]) != null ? _a : "";
322
+ if (componentsStr) {
323
+ componentsStr.split(V2_ENTRY_SEP).forEach((entry) => {
324
+ const sepIdx = entry.indexOf(V2_KV_SEP);
325
+ if (sepIdx === -1) {
326
+ const id = entry;
327
+ if (isUnsafeKey(id)) {
328
+ return;
329
+ }
330
+ components[id] = {};
331
+ } else {
332
+ const id = entry.slice(0, sepIdx);
333
+ if (isUnsafeKey(id)) {
334
+ return;
335
+ }
336
+ const indexes = entry.slice(sepIdx + 1).split(V2_INDEX_SEP).map(Number);
337
+ components[id] = { indexes };
338
+ }
339
+ });
340
+ }
341
+ const rulesStr = (_b = fields[4]) != null ? _b : "";
342
+ let rules;
343
+ if (rulesStr) {
344
+ rules = {};
345
+ rulesStr.split(V2_ENTRY_SEP).forEach((entry) => {
346
+ const sepIdx = entry.indexOf(V2_KV_SEP);
347
+ if (sepIdx !== -1) {
348
+ const id = entry.slice(0, sepIdx);
349
+ if (isUnsafeKey(id)) {
350
+ return;
351
+ }
352
+ rules[id] = entry.slice(sepIdx + 1) === "1";
353
+ }
354
+ });
355
+ }
356
+ const keysStr = (_c = fields[5]) != null ? _c : "";
357
+ const keys = {};
358
+ if (keysStr) {
359
+ keysStr.split(V2_ENTRY_SEP).forEach((entry) => {
360
+ const sepIdx = entry.indexOf(V2_KV_SEP);
361
+ if (sepIdx !== -1) {
362
+ const k = fromBase64Url(entry.slice(0, sepIdx));
363
+ if (isUnsafeKey(k)) {
364
+ return;
365
+ }
366
+ const v = fromBase64Url(entry.slice(sepIdx + 1));
367
+ keys[k] = v;
368
+ }
369
+ });
370
+ }
371
+ const { defaultConsent, previewMode, isPrefetch } = decodeFlags((_d = fields[6]) != null ? _d : "0");
372
+ const releaseId = fields[7] ? fromBase64Url(fields[7]) : void 0;
373
+ const locale = fields[8] ? fromBase64Url(fields[8]) : void 0;
374
+ return {
375
+ compositionState,
376
+ routePath,
377
+ components,
378
+ rules,
379
+ keys,
380
+ releaseId,
381
+ defaultConsent,
382
+ previewMode,
383
+ locale,
384
+ isPrefetch
385
+ };
386
+ };
387
+ var serializeEvaluationResult = ({
388
+ payload,
389
+ allComponentIds
390
+ }) => {
391
+ return serializeV2(payload, allComponentIds);
257
392
  };
258
393
  var deserializeEvaluationResult = ({
259
394
  input: providedInput,
260
395
  decode = true
261
396
  }) => {
262
397
  var _a, _b, _c;
263
- const input = decode ? decodeWithReplacements(providedInput) : providedInput;
264
- const parsed = JSON.parse(input);
398
+ const input = decode ? decodeURIComponent(providedInput) : providedInput;
399
+ if (input.startsWith("2~")) {
400
+ return deserializeV2(input);
401
+ }
402
+ const jsonInput = decode ? atob(unreplaceReservedCharacters(input)) : input;
403
+ const parsed = JSON.parse(jsonInput);
265
404
  const pageState = {
266
405
  compositionState: parsed.cs,
267
406
  routePath: parsed.r,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniformdev/next-app-router-shared",
3
- "version": "20.50.2-alpha.1+8798ffc32d",
3
+ "version": "20.50.2-alpha.39+83d081969c",
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.50.2-alpha.1+8798ffc32d",
40
- "@uniformdev/context": "20.50.2-alpha.1+8798ffc32d",
39
+ "@uniformdev/canvas": "20.50.2-alpha.39+83d081969c",
40
+ "@uniformdev/context": "20.50.2-alpha.39+83d081969c",
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": "8798ffc32d405a5d357628861a40cd964e3ea6f0"
54
+ "gitHead": "83d081969cac03a901e60d5c78bad66eef78de80"
55
55
  }