@genesislcap/foundation-utils 14.496.2-alpha-2c4a633.0 → 14.498.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -250,204 +250,6 @@
250
250
  }
251
251
  ]
252
252
  },
253
- {
254
- "kind": "javascript-module",
255
- "path": "src/data/inMemoryDatabase.ts",
256
- "declarations": [
257
- {
258
- "kind": "class",
259
- "description": "An in memory database of specific DatabaseRecord types.",
260
- "name": "InMemoryDatabase",
261
- "members": [
262
- {
263
- "kind": "field",
264
- "name": "isWorking",
265
- "type": {
266
- "text": "boolean"
267
- },
268
- "privacy": "public",
269
- "default": "false"
270
- },
271
- {
272
- "kind": "field",
273
- "name": "records",
274
- "type": {
275
- "text": "Record<string, T>"
276
- },
277
- "privacy": "private",
278
- "default": "{}"
279
- },
280
- {
281
- "kind": "field",
282
- "name": "beforeUpdateListeners",
283
- "privacy": "private"
284
- },
285
- {
286
- "kind": "field",
287
- "name": "afterUpdateListeners",
288
- "privacy": "private"
289
- },
290
- {
291
- "kind": "method",
292
- "name": "create",
293
- "privacy": "public",
294
- "return": {
295
- "type": {
296
- "text": "Promise<DatabaseAccessResult.Create<T>>"
297
- }
298
- },
299
- "parameters": [
300
- {
301
- "name": "newValue",
302
- "type": {
303
- "text": "Omit<T, 'id'>"
304
- }
305
- }
306
- ]
307
- },
308
- {
309
- "kind": "method",
310
- "name": "read",
311
- "privacy": "public",
312
- "return": {
313
- "type": {
314
- "text": "Promise<DatabaseAccessResult.Read<T>>"
315
- }
316
- },
317
- "parameters": [
318
- {
319
- "name": "id",
320
- "type": {
321
- "text": "string"
322
- }
323
- }
324
- ]
325
- },
326
- {
327
- "kind": "method",
328
- "name": "update",
329
- "privacy": "public",
330
- "return": {
331
- "type": {
332
- "text": "Promise<DatabaseAccessResult.Update<T>>"
333
- }
334
- },
335
- "parameters": [
336
- {
337
- "name": "id",
338
- "type": {
339
- "text": "string"
340
- }
341
- },
342
- {
343
- "name": "newValue",
344
- "type": {
345
- "text": "Omit<Partial<T>, 'id'>"
346
- }
347
- }
348
- ]
349
- },
350
- {
351
- "kind": "method",
352
- "name": "delete",
353
- "privacy": "public",
354
- "return": {
355
- "type": {
356
- "text": "Promise<DatabaseAccessResult.Delete>"
357
- }
358
- },
359
- "parameters": [
360
- {
361
- "name": "id",
362
- "type": {
363
- "text": "string"
364
- }
365
- }
366
- ]
367
- },
368
- {
369
- "kind": "method",
370
- "name": "visit",
371
- "privacy": "public",
372
- "return": {
373
- "type": {
374
- "text": "Promise<void>"
375
- }
376
- },
377
- "parameters": [
378
- {
379
- "name": "visitor",
380
- "type": {
381
- "text": "(record: T) => void"
382
- }
383
- }
384
- ]
385
- },
386
- {
387
- "kind": "method",
388
- "name": "onBeforeUpdate",
389
- "privacy": "public",
390
- "return": {
391
- "type": {
392
- "text": "() => void"
393
- }
394
- },
395
- "parameters": [
396
- {
397
- "name": "listener",
398
- "type": {
399
- "text": "Listener<DatabaseEvent.BeforeUpdate<T>>"
400
- }
401
- }
402
- ]
403
- },
404
- {
405
- "kind": "method",
406
- "name": "onAfterUpdate",
407
- "privacy": "public",
408
- "return": {
409
- "type": {
410
- "text": "() => void"
411
- }
412
- },
413
- "parameters": [
414
- {
415
- "name": "listener",
416
- "type": {
417
- "text": "Listener<DatabaseEvent.AfterUpdate<T>>"
418
- }
419
- }
420
- ]
421
- }
422
- ]
423
- }
424
- ],
425
- "exports": [
426
- {
427
- "kind": "js",
428
- "name": "InMemoryDatabase",
429
- "declaration": {
430
- "name": "InMemoryDatabase",
431
- "module": "src/data/inMemoryDatabase.ts"
432
- }
433
- }
434
- ]
435
- },
436
- {
437
- "kind": "javascript-module",
438
- "path": "src/data/index.ts",
439
- "declarations": [],
440
- "exports": [
441
- {
442
- "kind": "js",
443
- "name": "*",
444
- "declaration": {
445
- "name": "*",
446
- "package": "./inMemoryDatabase"
447
- }
448
- }
449
- ]
450
- },
451
253
  {
452
254
  "kind": "javascript-module",
453
255
  "path": "src/design-system/design-system.ts",
@@ -650,6 +452,204 @@
650
452
  }
651
453
  ]
652
454
  },
455
+ {
456
+ "kind": "javascript-module",
457
+ "path": "src/data/inMemoryDatabase.ts",
458
+ "declarations": [
459
+ {
460
+ "kind": "class",
461
+ "description": "An in memory database of specific DatabaseRecord types.",
462
+ "name": "InMemoryDatabase",
463
+ "members": [
464
+ {
465
+ "kind": "field",
466
+ "name": "isWorking",
467
+ "type": {
468
+ "text": "boolean"
469
+ },
470
+ "privacy": "public",
471
+ "default": "false"
472
+ },
473
+ {
474
+ "kind": "field",
475
+ "name": "records",
476
+ "type": {
477
+ "text": "Record<string, T>"
478
+ },
479
+ "privacy": "private",
480
+ "default": "{}"
481
+ },
482
+ {
483
+ "kind": "field",
484
+ "name": "beforeUpdateListeners",
485
+ "privacy": "private"
486
+ },
487
+ {
488
+ "kind": "field",
489
+ "name": "afterUpdateListeners",
490
+ "privacy": "private"
491
+ },
492
+ {
493
+ "kind": "method",
494
+ "name": "create",
495
+ "privacy": "public",
496
+ "return": {
497
+ "type": {
498
+ "text": "Promise<DatabaseAccessResult.Create<T>>"
499
+ }
500
+ },
501
+ "parameters": [
502
+ {
503
+ "name": "newValue",
504
+ "type": {
505
+ "text": "Omit<T, 'id'>"
506
+ }
507
+ }
508
+ ]
509
+ },
510
+ {
511
+ "kind": "method",
512
+ "name": "read",
513
+ "privacy": "public",
514
+ "return": {
515
+ "type": {
516
+ "text": "Promise<DatabaseAccessResult.Read<T>>"
517
+ }
518
+ },
519
+ "parameters": [
520
+ {
521
+ "name": "id",
522
+ "type": {
523
+ "text": "string"
524
+ }
525
+ }
526
+ ]
527
+ },
528
+ {
529
+ "kind": "method",
530
+ "name": "update",
531
+ "privacy": "public",
532
+ "return": {
533
+ "type": {
534
+ "text": "Promise<DatabaseAccessResult.Update<T>>"
535
+ }
536
+ },
537
+ "parameters": [
538
+ {
539
+ "name": "id",
540
+ "type": {
541
+ "text": "string"
542
+ }
543
+ },
544
+ {
545
+ "name": "newValue",
546
+ "type": {
547
+ "text": "Omit<Partial<T>, 'id'>"
548
+ }
549
+ }
550
+ ]
551
+ },
552
+ {
553
+ "kind": "method",
554
+ "name": "delete",
555
+ "privacy": "public",
556
+ "return": {
557
+ "type": {
558
+ "text": "Promise<DatabaseAccessResult.Delete>"
559
+ }
560
+ },
561
+ "parameters": [
562
+ {
563
+ "name": "id",
564
+ "type": {
565
+ "text": "string"
566
+ }
567
+ }
568
+ ]
569
+ },
570
+ {
571
+ "kind": "method",
572
+ "name": "visit",
573
+ "privacy": "public",
574
+ "return": {
575
+ "type": {
576
+ "text": "Promise<void>"
577
+ }
578
+ },
579
+ "parameters": [
580
+ {
581
+ "name": "visitor",
582
+ "type": {
583
+ "text": "(record: T) => void"
584
+ }
585
+ }
586
+ ]
587
+ },
588
+ {
589
+ "kind": "method",
590
+ "name": "onBeforeUpdate",
591
+ "privacy": "public",
592
+ "return": {
593
+ "type": {
594
+ "text": "() => void"
595
+ }
596
+ },
597
+ "parameters": [
598
+ {
599
+ "name": "listener",
600
+ "type": {
601
+ "text": "Listener<DatabaseEvent.BeforeUpdate<T>>"
602
+ }
603
+ }
604
+ ]
605
+ },
606
+ {
607
+ "kind": "method",
608
+ "name": "onAfterUpdate",
609
+ "privacy": "public",
610
+ "return": {
611
+ "type": {
612
+ "text": "() => void"
613
+ }
614
+ },
615
+ "parameters": [
616
+ {
617
+ "name": "listener",
618
+ "type": {
619
+ "text": "Listener<DatabaseEvent.AfterUpdate<T>>"
620
+ }
621
+ }
622
+ ]
623
+ }
624
+ ]
625
+ }
626
+ ],
627
+ "exports": [
628
+ {
629
+ "kind": "js",
630
+ "name": "InMemoryDatabase",
631
+ "declaration": {
632
+ "name": "InMemoryDatabase",
633
+ "module": "src/data/inMemoryDatabase.ts"
634
+ }
635
+ }
636
+ ]
637
+ },
638
+ {
639
+ "kind": "javascript-module",
640
+ "path": "src/data/index.ts",
641
+ "declarations": [],
642
+ "exports": [
643
+ {
644
+ "kind": "js",
645
+ "name": "*",
646
+ "declaration": {
647
+ "name": "*",
648
+ "package": "./inMemoryDatabase"
649
+ }
650
+ }
651
+ ]
652
+ },
653
653
  {
654
654
  "kind": "javascript-module",
655
655
  "path": "src/directives/index.ts",
@@ -1434,7 +1434,46 @@
1434
1434
  "text": "string"
1435
1435
  }
1436
1436
  }
1437
- ]
1437
+ ],
1438
+ "description": "Returns true when `feature.<name>` is present in the current URL query string.",
1439
+ "privacy": "public"
1440
+ },
1441
+ {
1442
+ "kind": "function",
1443
+ "name": "getActivatedFeatures",
1444
+ "return": {
1445
+ "type": {
1446
+ "text": "string[]"
1447
+ }
1448
+ },
1449
+ "description": "Returns the names of all feature flags currently present in the URL query string.",
1450
+ "privacy": "public"
1451
+ },
1452
+ {
1453
+ "kind": "function",
1454
+ "name": "setActivatedFeatures",
1455
+ "return": {
1456
+ "type": {
1457
+ "text": "void"
1458
+ }
1459
+ },
1460
+ "parameters": [
1461
+ {
1462
+ "name": "features",
1463
+ "type": {
1464
+ "text": "string[]"
1465
+ }
1466
+ },
1467
+ {
1468
+ "name": "options",
1469
+ "default": "{}",
1470
+ "type": {
1471
+ "text": "SetActivatedFeaturesOptions"
1472
+ }
1473
+ }
1474
+ ],
1475
+ "description": "Rewrites URL `feature.*` query params to match the given feature list (presence-only).\nRemoves any existing `feature.*` params, then adds each selected feature as `feature.<name>`.\nReloads by default so boot-time consumers (e.g. AI registration) pick up the change.",
1476
+ "privacy": "public"
1438
1477
  }
1439
1478
  ],
1440
1479
  "exports": [
@@ -1445,6 +1484,22 @@
1445
1484
  "name": "isFeatureActivated",
1446
1485
  "module": "src/feature-flags/featureFlags.ts"
1447
1486
  }
1487
+ },
1488
+ {
1489
+ "kind": "js",
1490
+ "name": "getActivatedFeatures",
1491
+ "declaration": {
1492
+ "name": "getActivatedFeatures",
1493
+ "module": "src/feature-flags/featureFlags.ts"
1494
+ }
1495
+ },
1496
+ {
1497
+ "kind": "js",
1498
+ "name": "setActivatedFeatures",
1499
+ "declaration": {
1500
+ "name": "setActivatedFeatures",
1501
+ "module": "src/feature-flags/featureFlags.ts"
1502
+ }
1448
1503
  }
1449
1504
  ]
1450
1505
  },
@@ -1,2 +1,47 @@
1
+ /**
2
+ * Options for {@link setActivatedFeatures}.
3
+ * @public
4
+ */
5
+ export type SetActivatedFeaturesOptions = {
6
+ /**
7
+ * When true (default), navigates to the updated URL so boot-time consumers re-evaluate.
8
+ * When false, updates the URL via `history.replaceState` without reloading.
9
+ */
10
+ reload?: boolean;
11
+ };
12
+ /**
13
+ * Returns true when `feature.<name>` is present in the given search string.
14
+ * @internal
15
+ */
16
+ export declare const isFeatureActivatedInSearch: (search: string, feature: string) => boolean;
17
+ /**
18
+ * Returns the names of all feature flags present in the given search string.
19
+ * @internal
20
+ */
21
+ export declare const getActivatedFeaturesFromSearch: (search: string) => string[];
22
+ /**
23
+ * Returns a new URL with `feature.*` query params rewritten to match the given feature list.
24
+ * @internal
25
+ */
26
+ export declare const applyActivatedFeaturesToUrl: (url: URL, features: string[]) => URL;
27
+ /**
28
+ * Returns true when `feature.<name>` is present in the current URL query string.
29
+ * @public
30
+ */
1
31
  export declare const isFeatureActivated: (feature: string) => boolean;
32
+ /**
33
+ * Returns the names of all feature flags currently present in the URL query string.
34
+ * @example
35
+ * // URL: ?feature.ai&feature.forceHttp=true
36
+ * getActivatedFeatures(); // ['ai', 'forceHttp']
37
+ * @public
38
+ */
39
+ export declare const getActivatedFeatures: () => string[];
40
+ /**
41
+ * Rewrites URL `feature.*` query params to match the given feature list (presence-only).
42
+ * Removes any existing `feature.*` params, then adds each selected feature as `feature.<name>`.
43
+ * Reloads by default so boot-time consumers (e.g. AI registration) pick up the change.
44
+ * @public
45
+ */
46
+ export declare const setActivatedFeatures: (features: string[], options?: SetActivatedFeaturesOptions) => void;
2
47
  //# sourceMappingURL=featureFlags.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"featureFlags.d.ts","sourceRoot":"","sources":["../../../src/feature-flags/featureFlags.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,kBAAkB,GAAI,SAAS,MAAM,KAAG,OAapD,CAAC"}
1
+ {"version":3,"file":"featureFlags.d.ts","sourceRoot":"","sources":["../../../src/feature-flags/featureFlags.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,MAAM,2BAA2B,GAAG;IACxC;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAIF;;;GAGG;AACH,eAAO,MAAM,0BAA0B,GAAI,QAAQ,MAAM,EAAE,SAAS,MAAM,KAAG,OAe5E,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,8BAA8B,GAAI,QAAQ,MAAM,KAAG,MAAM,EAcrE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,2BAA2B,GAAI,KAAK,GAAG,EAAE,UAAU,MAAM,EAAE,KAAG,GAmB1E,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,GAAI,SAAS,MAAM,KAAG,OACQ,CAAC;AAE9D;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,QAAO,MAAM,EACU,CAAC;AAEzD;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,GAC/B,UAAU,MAAM,EAAE,EAClB,UAAS,2BAAgC,KACxC,IAUF,CAAC"}
@@ -70,15 +70,6 @@ export declare const LifecycleMixin: <T extends ConstructableLifecycleHandler>(B
70
70
  * @internal
71
71
  **/
72
72
  #_shouldForceLifecycle: boolean;
73
- /**
74
- * Set once a short grace period (`setTimeout`) has elapsed since a blocked ('reflow')
75
- * disconnect. Some consumers run defensive cleanup after the block via schedulers that can
76
- * outlast a microtask (e.g. FAST's `DOM.queueUpdate`), so a macrotask-scale grace period is
77
- * needed - once it has passed, the paired reconnect must not stay blocked, or the consumer
78
- * never re-initializes.
79
- * @internal
80
- **/
81
- #_blockedDisconnectSurvivedGracePeriod: boolean;
82
73
  /**
83
74
  * @privateRemarks
84
75
  * Rather than using the basic implementation of cloning we run a copy constructor (deepClone)
@@ -1 +1 @@
1
- {"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../../../src/mixins/lifecycle/lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAErE;;GAEG;AACH,MAAM,MAAM,6BAA6B,GAAG,aAAa,CAAC,WAAW,GAAG,WAAW,CAAC,CAAC;AAErF;;;;GAIG;AACH,MAAM,MAAM,yBAAyB,GAAG;IACtC,QAAQ,EAAE,OAAO,CAAC;IAClB,cAAc,EAAE,OAAO,CAAC;IACxB,oBAAoB,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,IAAI,EAAE,mBAAmB,CAAC;CAC3B,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,yBAAyB,CAAC;CACjC,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,KAAK,CAAC;CACb,CAAC;AAEF,KAAK,SAAS,GAAG,yBAAyB,GAAG,oBAAoB,GAAG,YAAY,CAAC;AAEjF,KAAK,aAAa,GAAG,SAAS,GAAG,YAAY,GAAG,WAAW,CAAC;AAK5D;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,GAAI,CAAC,SAAS,6BAA6B,EAAE,MAAM,CAAC;kBAwCtD,GAAG,EAAE;QAtC1B,iBAAiB;qBACJ,SAAS;QAEtB;;;YAGI;2BACe,MAAM;QACzB;;;YAGI;0BACc,OAAO;QACzB;;;;;;;YAOI;0BACc,MAAM,CAAC,OAAO;QAChC;;;;YAII;gCACoB,OAAO;QAC/B;;;;;;;YAOI;gDACoC,OAAO;QAM/C;;;;;WAKG;yBACuB,OAAO,GAAG,IAAI;QAUxC;;;;;;WAMG;qBACU,IAAI;QAMjB;;;WAGG;;QAmBH;;;;;WAKG;;wDAwB6C,aAAa,GAAG,OAAO;QAyCvE;;;;;;WAMG;qCAC0B,OAAO,GAAG,SAAS;6BA4B3B,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAM1B,CAAC;AAEJ;;;GAGG;AACH,eAAO,MAAM,mBAAmB,eAAkC,CAAC"}
1
+ {"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../../../src/mixins/lifecycle/lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAErE;;GAEG;AACH,MAAM,MAAM,6BAA6B,GAAG,aAAa,CAAC,WAAW,GAAG,WAAW,CAAC,CAAC;AAErF;;;;GAIG;AACH,MAAM,MAAM,yBAAyB,GAAG;IACtC,QAAQ,EAAE,OAAO,CAAC;IAClB,cAAc,EAAE,OAAO,CAAC;IACxB,oBAAoB,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,IAAI,EAAE,mBAAmB,CAAC;CAC3B,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,yBAAyB,CAAC;CACjC,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,KAAK,CAAC;CACb,CAAC;AAEF,KAAK,SAAS,GAAG,yBAAyB,GAAG,oBAAoB,GAAG,YAAY,CAAC;AAEjF,KAAK,aAAa,GAAG,SAAS,GAAG,YAAY,GAAG,WAAW,CAAC;AAK5D;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,GAAI,CAAC,SAAS,6BAA6B,EAAE,MAAM,CAAC;kBA+BtD,GAAG,EAAE;QA7B1B,iBAAiB;qBACJ,SAAS;QAEtB;;;YAGI;2BACe,MAAM;QACzB;;;YAGI;0BACc,OAAO;QACzB;;;;;;;YAOI;0BACc,MAAM,CAAC,OAAO;QAChC;;;;YAII;gCACoB,OAAO;QAM/B;;;;;WAKG;yBACuB,OAAO,GAAG,IAAI;QAUxC;;;;;;WAMG;qBACU,IAAI;QAMjB;;;WAGG;;QAmBH;;;;;WAKG;;wDAwB6C,aAAa,GAAG,OAAO;QAkCvE;;;;;;WAMG;qCAC0B,OAAO,GAAG,SAAS;6BA4B3B,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAM1B,CAAC;AAEJ;;;GAGG;AACH,eAAO,MAAM,mBAAmB,eAAkC,CAAC"}
@@ -1,8 +1,15 @@
1
- export const isFeatureActivated = (feature) => {
2
- const currentUrl = window.location.href;
3
- const queryString = new URL(currentUrl).search;
4
- const params = new URLSearchParams(queryString);
5
- const featureKey = `feature.${feature}`.toLowerCase();
1
+ const FEATURE_PREFIX = 'feature.';
2
+ const isFeatureParamKey = (key) => key.toLowerCase().startsWith(FEATURE_PREFIX);
3
+ /**
4
+ * Returns true when `feature.<name>` is present in the given search string.
5
+ * @internal
6
+ */
7
+ export const isFeatureActivatedInSearch = (search, feature) => {
8
+ if (!feature) {
9
+ return false;
10
+ }
11
+ const featureKey = `${FEATURE_PREFIX}${feature}`.toLowerCase();
12
+ const params = new URLSearchParams(search);
6
13
  for (const [key] of params.entries()) {
7
14
  if (key.toLowerCase() === featureKey) {
8
15
  return true;
@@ -10,3 +17,69 @@ export const isFeatureActivated = (feature) => {
10
17
  }
11
18
  return false;
12
19
  };
20
+ /**
21
+ * Returns the names of all feature flags present in the given search string.
22
+ * @internal
23
+ */
24
+ export const getActivatedFeaturesFromSearch = (search) => {
25
+ const params = new URLSearchParams(search);
26
+ const features = [];
27
+ for (const [key] of params.entries()) {
28
+ if (isFeatureParamKey(key)) {
29
+ const feature = key.slice(FEATURE_PREFIX.length);
30
+ if (feature) {
31
+ features.push(feature);
32
+ }
33
+ }
34
+ }
35
+ return features;
36
+ };
37
+ /**
38
+ * Returns a new URL with `feature.*` query params rewritten to match the given feature list.
39
+ * @internal
40
+ */
41
+ export const applyActivatedFeaturesToUrl = (url, features) => {
42
+ const next = new URL(url.href);
43
+ const params = next.searchParams;
44
+ for (const key of [...params.keys()]) {
45
+ if (isFeatureParamKey(key)) {
46
+ params.delete(key);
47
+ }
48
+ }
49
+ for (const feature of features) {
50
+ if (feature) {
51
+ // Use an explicit value so routers/normalizers that drop empty query values
52
+ // (e.g. some React Router redirect paths) still preserve the flag.
53
+ params.set(`${FEATURE_PREFIX}${feature}`, 'true');
54
+ }
55
+ }
56
+ return next;
57
+ };
58
+ /**
59
+ * Returns true when `feature.<name>` is present in the current URL query string.
60
+ * @public
61
+ */
62
+ export const isFeatureActivated = (feature) => isFeatureActivatedInSearch(window.location.search, feature);
63
+ /**
64
+ * Returns the names of all feature flags currently present in the URL query string.
65
+ * @example
66
+ * // URL: ?feature.ai&feature.forceHttp=true
67
+ * getActivatedFeatures(); // ['ai', 'forceHttp']
68
+ * @public
69
+ */
70
+ export const getActivatedFeatures = () => getActivatedFeaturesFromSearch(window.location.search);
71
+ /**
72
+ * Rewrites URL `feature.*` query params to match the given feature list (presence-only).
73
+ * Removes any existing `feature.*` params, then adds each selected feature as `feature.<name>`.
74
+ * Reloads by default so boot-time consumers (e.g. AI registration) pick up the change.
75
+ * @public
76
+ */
77
+ export const setActivatedFeatures = (features, options = {}) => {
78
+ const { reload = true } = options;
79
+ const url = applyActivatedFeaturesToUrl(new URL(window.location.href), features);
80
+ if (reload) {
81
+ window.location.assign(url.toString());
82
+ return;
83
+ }
84
+ window.history.replaceState({}, '', url.toString());
85
+ };
@@ -11,7 +11,7 @@ const CLEANUP_TIMEOUT_MS = 10000;
11
11
  * #_ syntax is used for a javascript private method/variable
12
12
  * we can't use private/protected from typescript in an anonymous class like this
13
13
  */
14
- export const LifecycleMixin = (Base) => { var _instances, __container, __latestTokenCode, __hasFirstLoaded, __cleanupTimeout, __shouldForceLifecycle, __blockedDisconnectSurvivedGracePeriod, __blockLifecycleDueToTokenChange, __tryFindContainingLayout, _a; return _a = class extends Base {
14
+ export const LifecycleMixin = (Base) => { var _instances, __container, __latestTokenCode, __hasFirstLoaded, __cleanupTimeout, __shouldForceLifecycle, __blockLifecycleDueToTokenChange, __tryFindContainingLayout, _a; return _a = class extends Base {
15
15
  constructor(...args) {
16
16
  super(args);
17
17
  _instances.add(this);
@@ -42,15 +42,6 @@ export const LifecycleMixin = (Base) => { var _instances, __container, __latestT
42
42
  * @internal
43
43
  **/
44
44
  __shouldForceLifecycle.set(this, false);
45
- /**
46
- * Set once a short grace period (`setTimeout`) has elapsed since a blocked ('reflow')
47
- * disconnect. Some consumers run defensive cleanup after the block via schedulers that can
48
- * outlast a microtask (e.g. FAST's `DOM.queueUpdate`), so a macrotask-scale grace period is
49
- * needed - once it has passed, the paired reconnect must not stay blocked, or the consumer
50
- * never re-initializes.
51
- * @internal
52
- **/
53
- __blockedDisconnectSurvivedGracePeriod.set(this, false);
54
45
  }
55
46
  /**
56
47
  * @privateRemarks
@@ -136,7 +127,6 @@ export const LifecycleMixin = (Base) => { var _instances, __container, __latestT
136
127
  __hasFirstLoaded = new WeakMap(),
137
128
  __cleanupTimeout = new WeakMap(),
138
129
  __shouldForceLifecycle = new WeakMap(),
139
- __blockedDisconnectSurvivedGracePeriod = new WeakMap(),
140
130
  _instances = new WeakSet(),
141
131
  __blockLifecycleDueToTokenChange = function __blockLifecycleDueToTokenChange(lifecycleType) {
142
132
  var _b, _c;
@@ -156,14 +146,6 @@ export const LifecycleMixin = (Base) => { var _instances, __container, __latestT
156
146
  // 'reconnect' - otherwise unrelated layout changes while disconnected can block it forever.
157
147
  __classPrivateFieldSet(this, __hasFirstLoaded, false, "f");
158
148
  }
159
- else {
160
- // Blocked disconnect: track whether a grace period elapses before the paired reconnect
161
- // arrives. See `#_blockedDisconnectSurvivedGracePeriod` for why this matters.
162
- __classPrivateFieldSet(this, __blockedDisconnectSurvivedGracePeriod, false, "f");
163
- setTimeout(() => {
164
- __classPrivateFieldSet(this, __blockedDisconnectSurvivedGracePeriod, true, "f");
165
- }, 0);
166
- }
167
149
  __classPrivateFieldSet(this, __cleanupTimeout, setTimeout(() => {
168
150
  __classPrivateFieldSet(this, __shouldForceLifecycle, result, "f");
169
151
  if (__classPrivateFieldGet(this, __shouldForceLifecycle, "f")) {
@@ -172,7 +154,7 @@ export const LifecycleMixin = (Base) => { var _instances, __container, __latestT
172
154
  }, CLEANUP_TIMEOUT_MS), "f");
173
155
  }
174
156
  else if (lifecycleType === 'reconnect') {
175
- result = tokensDifferent && !__classPrivateFieldGet(this, __blockedDisconnectSurvivedGracePeriod, "f");
157
+ result = tokensDifferent;
176
158
  __classPrivateFieldSet(this, __latestTokenCode, (_c = __classPrivateFieldGet(this, __container, "f").lifecycleUpdateToken) !== null && _c !== void 0 ? _c : '', "f");
177
159
  clearTimeout(__classPrivateFieldGet(this, __cleanupTimeout, "f"));
178
160
  }
@@ -5829,6 +5829,30 @@
5829
5829
  }
5830
5830
  ]
5831
5831
  },
5832
+ {
5833
+ "kind": "Function",
5834
+ "canonicalReference": "@genesislcap/foundation-utils!getActivatedFeatures:function(1)",
5835
+ "docComment": "/**\n * Returns the names of all feature flags currently present in the URL query string.\n *\n * @example\n *\n * // URL: ?feature.ai&feature.forceHttp=true getActivatedFeatures(); // ['ai', 'forceHttp']\n *\n * @public\n */\n",
5836
+ "excerptTokens": [
5837
+ {
5838
+ "kind": "Content",
5839
+ "text": "getActivatedFeatures: () => "
5840
+ },
5841
+ {
5842
+ "kind": "Content",
5843
+ "text": "string[]"
5844
+ }
5845
+ ],
5846
+ "fileUrlPath": "src/feature-flags/featureFlags.ts",
5847
+ "returnTypeTokenRange": {
5848
+ "startIndex": 1,
5849
+ "endIndex": 2
5850
+ },
5851
+ "releaseTag": "Public",
5852
+ "overloadIndex": 1,
5853
+ "parameters": [],
5854
+ "name": "getActivatedFeatures"
5855
+ },
5832
5856
  {
5833
5857
  "kind": "Function",
5834
5858
  "canonicalReference": "@genesislcap/foundation-utils!getAllElements:function(1)",
@@ -8246,7 +8270,7 @@
8246
8270
  {
8247
8271
  "kind": "Function",
8248
8272
  "canonicalReference": "@genesislcap/foundation-utils!isFeatureActivated:function(1)",
8249
- "docComment": "",
8273
+ "docComment": "/**\n * Returns true when `feature.<name>` is present in the current URL query string.\n *\n * @public\n */\n",
8250
8274
  "excerptTokens": [
8251
8275
  {
8252
8276
  "kind": "Content",
@@ -9094,7 +9118,7 @@
9094
9118
  },
9095
9119
  {
9096
9120
  "kind": "Content",
9097
- "text": ";\n #_shouldForceLifecycle: boolean;\n #_blockedDisconnectSurvivedGracePeriod: boolean;\n cloneNode(deep?: boolean): "
9121
+ "text": ";\n #_shouldForceLifecycle: boolean;\n cloneNode(deep?: boolean): "
9098
9122
  },
9099
9123
  {
9100
9124
  "kind": "Reference",
@@ -17879,6 +17903,90 @@
17879
17903
  "endIndex": 4
17880
17904
  }
17881
17905
  },
17906
+ {
17907
+ "kind": "Function",
17908
+ "canonicalReference": "@genesislcap/foundation-utils!setActivatedFeatures:function(1)",
17909
+ "docComment": "/**\n * Rewrites URL `feature.*` query params to match the given feature list (presence-only). Removes any existing `feature.*` params, then adds each selected feature as `feature.<name>`. Reloads by default so boot-time consumers (e.g. AI registration) pick up the change.\n *\n * @public\n */\n",
17910
+ "excerptTokens": [
17911
+ {
17912
+ "kind": "Content",
17913
+ "text": "setActivatedFeatures: (features: "
17914
+ },
17915
+ {
17916
+ "kind": "Content",
17917
+ "text": "string[]"
17918
+ },
17919
+ {
17920
+ "kind": "Content",
17921
+ "text": ", options?: "
17922
+ },
17923
+ {
17924
+ "kind": "Reference",
17925
+ "text": "SetActivatedFeaturesOptions",
17926
+ "canonicalReference": "@genesislcap/foundation-utils!SetActivatedFeaturesOptions:type"
17927
+ },
17928
+ {
17929
+ "kind": "Content",
17930
+ "text": ") => "
17931
+ },
17932
+ {
17933
+ "kind": "Content",
17934
+ "text": "void"
17935
+ }
17936
+ ],
17937
+ "fileUrlPath": "src/feature-flags/featureFlags.ts",
17938
+ "returnTypeTokenRange": {
17939
+ "startIndex": 5,
17940
+ "endIndex": 6
17941
+ },
17942
+ "releaseTag": "Public",
17943
+ "overloadIndex": 1,
17944
+ "parameters": [
17945
+ {
17946
+ "parameterName": "features",
17947
+ "parameterTypeTokenRange": {
17948
+ "startIndex": 1,
17949
+ "endIndex": 2
17950
+ },
17951
+ "isOptional": false
17952
+ },
17953
+ {
17954
+ "parameterName": "options",
17955
+ "parameterTypeTokenRange": {
17956
+ "startIndex": 3,
17957
+ "endIndex": 4
17958
+ },
17959
+ "isOptional": true
17960
+ }
17961
+ ],
17962
+ "name": "setActivatedFeatures"
17963
+ },
17964
+ {
17965
+ "kind": "TypeAlias",
17966
+ "canonicalReference": "@genesislcap/foundation-utils!SetActivatedFeaturesOptions:type",
17967
+ "docComment": "/**\n * Options for {@link setActivatedFeatures}.\n *\n * @public\n */\n",
17968
+ "excerptTokens": [
17969
+ {
17970
+ "kind": "Content",
17971
+ "text": "export type SetActivatedFeaturesOptions = "
17972
+ },
17973
+ {
17974
+ "kind": "Content",
17975
+ "text": "{\n reload?: boolean;\n}"
17976
+ },
17977
+ {
17978
+ "kind": "Content",
17979
+ "text": ";"
17980
+ }
17981
+ ],
17982
+ "fileUrlPath": "src/feature-flags/featureFlags.ts",
17983
+ "releaseTag": "Public",
17984
+ "name": "SetActivatedFeaturesOptions",
17985
+ "typeTokenRange": {
17986
+ "startIndex": 1,
17987
+ "endIndex": 2
17988
+ }
17989
+ },
17882
17990
  {
17883
17991
  "kind": "Variable",
17884
17992
  "canonicalReference": "@genesislcap/foundation-utils!SHORTCUT_BLOCKED_DEFAULT_MESSAGE:var",
@@ -38,6 +38,12 @@ export declare const activeColorScheme: CSSDesignToken<string>;
38
38
  */
39
39
  export declare let API_HOST: string;
40
40
 
41
+ /**
42
+ * Returns a new URL with `feature.*` query params rewritten to match the given feature list.
43
+ * @internal
44
+ */
45
+ export declare const applyActivatedFeaturesToUrl: (url: URL, features: string[]) => URL;
46
+
41
47
  /**
42
48
  * assureDesignSystem.
43
49
  *
@@ -682,6 +688,21 @@ export declare namespace Genesis {
682
688
  */
683
689
  export declare const GENESIS_SOCKET_URL: string;
684
690
 
691
+ /**
692
+ * Returns the names of all feature flags currently present in the URL query string.
693
+ * @example
694
+ * // URL: ?feature.ai&feature.forceHttp=true
695
+ * getActivatedFeatures(); // ['ai', 'forceHttp']
696
+ * @public
697
+ */
698
+ export declare const getActivatedFeatures: () => string[];
699
+
700
+ /**
701
+ * Returns the names of all feature flags present in the given search string.
702
+ * @internal
703
+ */
704
+ export declare const getActivatedFeaturesFromSearch: (search: string) => string[];
705
+
685
706
  /**
686
707
  * Recursively gather all elements including those in shadow DOM
687
708
  * @public
@@ -890,8 +911,18 @@ export declare const inSymphonyDesktop: () => boolean;
890
911
  */
891
912
  export declare const isDev: () => boolean;
892
913
 
914
+ /**
915
+ * Returns true when `feature.<name>` is present in the current URL query string.
916
+ * @public
917
+ */
893
918
  export declare const isFeatureActivated: (feature: string) => boolean;
894
919
 
920
+ /**
921
+ * Returns true when `feature.<name>` is present in the given search string.
922
+ * @internal
923
+ */
924
+ export declare const isFeatureActivatedInSearch: (search: string, feature: string) => boolean;
925
+
895
926
  /**
896
927
  * JSON replacer function.
897
928
  * @param key - The object key.
@@ -1018,15 +1049,6 @@ export declare const LifecycleMixin: <T extends ConstructableLifecycleHandler>(B
1018
1049
  * @internal
1019
1050
  **/
1020
1051
  #_shouldForceLifecycle: boolean;
1021
- /**
1022
- * Set once a short grace period (`setTimeout`) has elapsed since a blocked ('reflow')
1023
- * disconnect. Some consumers run defensive cleanup after the block via schedulers that can
1024
- * outlast a microtask (e.g. FAST's `DOM.queueUpdate`), so a macrotask-scale grace period is
1025
- * needed - once it has passed, the paired reconnect must not stay blocked, or the consumer
1026
- * never re-initializes.
1027
- * @internal
1028
- **/
1029
- #_blockedDisconnectSurvivedGracePeriod: boolean;
1030
1052
  /**
1031
1053
  * @privateRemarks
1032
1054
  * Rather than using the basic implementation of cloning we run a copy constructor (deepClone)
@@ -2273,6 +2295,26 @@ export declare type ServerRowEntity = {
2273
2295
  };
2274
2296
  };
2275
2297
 
2298
+ /**
2299
+ * Rewrites URL `feature.*` query params to match the given feature list (presence-only).
2300
+ * Removes any existing `feature.*` params, then adds each selected feature as `feature.<name>`.
2301
+ * Reloads by default so boot-time consumers (e.g. AI registration) pick up the change.
2302
+ * @public
2303
+ */
2304
+ export declare const setActivatedFeatures: (features: string[], options?: SetActivatedFeaturesOptions) => void;
2305
+
2306
+ /**
2307
+ * Options for {@link setActivatedFeatures}.
2308
+ * @public
2309
+ */
2310
+ export declare type SetActivatedFeaturesOptions = {
2311
+ /**
2312
+ * When true (default), navigates to the updated URL so boot-time consumers re-evaluate.
2313
+ * When false, updates the URL via `history.replaceState` without reloading.
2314
+ */
2315
+ reload?: boolean;
2316
+ };
2317
+
2276
2318
  export declare const SHORTCUT_BLOCKED_DEFAULT_MESSAGE = "This shortcut is currently unavailable in this context.";
2277
2319
 
2278
2320
  export declare const SHORTCUT_BLOCKED_DEFAULT_TITLE = "Shortcut blocked";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@genesislcap/foundation-utils",
3
3
  "description": "Genesis Foundation Utils",
4
- "version": "14.496.2-alpha-2c4a633.0",
4
+ "version": "14.498.0",
5
5
  "sideEffects": false,
6
6
  "license": "SEE LICENSE IN license.txt",
7
7
  "main": "dist/esm/index.js",
@@ -29,18 +29,18 @@
29
29
  }
30
30
  },
31
31
  "devDependencies": {
32
- "@genesislcap/foundation-testing": "14.496.2-alpha-2c4a633.0",
33
- "@genesislcap/genx": "14.496.2-alpha-2c4a633.0",
34
- "@genesislcap/rollup-builder": "14.496.2-alpha-2c4a633.0",
35
- "@genesislcap/ts-builder": "14.496.2-alpha-2c4a633.0",
36
- "@genesislcap/uvu-playwright-builder": "14.496.2-alpha-2c4a633.0",
37
- "@genesislcap/vite-builder": "14.496.2-alpha-2c4a633.0",
38
- "@genesislcap/webpack-builder": "14.496.2-alpha-2c4a633.0",
32
+ "@genesislcap/foundation-testing": "14.498.0",
33
+ "@genesislcap/genx": "14.498.0",
34
+ "@genesislcap/rollup-builder": "14.498.0",
35
+ "@genesislcap/ts-builder": "14.498.0",
36
+ "@genesislcap/uvu-playwright-builder": "14.498.0",
37
+ "@genesislcap/vite-builder": "14.498.0",
38
+ "@genesislcap/webpack-builder": "14.498.0",
39
39
  "@types/json-schema": "^7.0.11"
40
40
  },
41
41
  "dependencies": {
42
- "@genesislcap/expression-builder": "14.496.2-alpha-2c4a633.0",
43
- "@genesislcap/foundation-logger": "14.496.2-alpha-2c4a633.0",
42
+ "@genesislcap/expression-builder": "14.498.0",
43
+ "@genesislcap/foundation-logger": "14.498.0",
44
44
  "@microsoft/fast-components": "2.30.6",
45
45
  "@microsoft/fast-element": "1.14.0",
46
46
  "@microsoft/fast-foundation": "2.50.0",
@@ -58,5 +58,5 @@
58
58
  "access": "public"
59
59
  },
60
60
  "customElements": "dist/custom-elements.json",
61
- "gitHead": "5df68bd8b1d25f3435e5a8e45da2c79c12579f3f"
61
+ "gitHead": "88a07e46851fa94efc9399c50975c9eb2c21168c"
62
62
  }