@coframe/sdk 1.1.56 → 1.1.60

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.
@@ -236,6 +236,8 @@ const ID_FIELDS = ['projectId', 'experimentId', 'userToken'];
236
236
  function invalidAssignmentIdField(body) {
237
237
  for (const field of ID_FIELDS) {
238
238
  const value = body[field];
239
+ if (field === 'experimentId' && value === undefined)
240
+ continue;
239
241
  if (typeof value !== 'string' || !value) {
240
242
  return { field, message: 'is required' };
241
243
  }
@@ -515,6 +517,25 @@ async function flushFeatureFlagEvents() {
515
517
  }
516
518
  }
517
519
 
520
+ const IDLE_MS = 60000;
521
+ let dispatcher;
522
+ function runsOnNode() {
523
+ var _a;
524
+ return (typeof process !== 'undefined' &&
525
+ typeof ((_a = process.versions) === null || _a === void 0 ? void 0 : _a.node) === 'string' &&
526
+ !('EdgeRuntime' in globalThis));
527
+ }
528
+ function edgeDispatcher() {
529
+ if (!runsOnNode())
530
+ return Promise.resolve(undefined);
531
+ dispatcher !== null && dispatcher !== void 0 ? dispatcher : (dispatcher = import('undici').then(({ Agent }) => new Agent({ keepAliveTimeout: IDLE_MS, keepAliveMaxTimeout: IDLE_MS }), () => undefined));
532
+ return dispatcher;
533
+ }
534
+ async function edgeFetch(url, init = {}) {
535
+ const agent = await edgeDispatcher();
536
+ return fetch(url, agent ? { ...init, dispatcher: agent } : init);
537
+ }
538
+
518
539
  const DEFAULT_ASSIGNMENT_TIMEOUT_MS = 1500;
519
540
  function normalizeRequestHeaders(headers) {
520
541
  const normalized = new Headers();
@@ -601,6 +622,13 @@ function effectiveVisitCount(headers) {
601
622
  const next = nextVisitCount(existing, lastVisitTime, Date.now());
602
623
  return Number.isSafeInteger(next) ? next : existing;
603
624
  }
625
+ function isAssignedVariant(value) {
626
+ return (isRecord(value) &&
627
+ typeof value.id === 'string' &&
628
+ typeof value.slug === 'string' &&
629
+ typeof value.experimentId === 'string' &&
630
+ typeof value.iterationId === 'string');
631
+ }
604
632
  function isExperimentAssignmentResult(value, experimentId) {
605
633
  if (!isRecord(value) || !isRecord(value.projectData))
606
634
  return false;
@@ -617,22 +645,32 @@ function isExperimentAssignmentResult(value, experimentId) {
617
645
  context.userVisitCount < 0) {
618
646
  return false;
619
647
  }
648
+ if (value.variants !== undefined && !Array.isArray(value.variants))
649
+ return false;
650
+ if (Array.isArray(value.variants) && !value.variants.every(isAssignedVariant))
651
+ return false;
652
+ if (experimentId === undefined) {
653
+ return value.variant === null && Array.isArray(value.variants);
654
+ }
620
655
  if (value.variant === null) {
621
656
  return typeof value.reason === 'string';
622
657
  }
623
- if (!isRecord(value.variant))
624
- return false;
625
- return (typeof value.variant.id === 'string' &&
626
- typeof value.variant.slug === 'string' &&
627
- value.variant.experimentId === experimentId &&
628
- typeof value.variant.iterationId === 'string');
658
+ return (isAssignedVariant(value.variant) &&
659
+ value.variant.experimentId === experimentId);
629
660
  }
630
661
  function generateUserToken() {
631
662
  return generateUUID();
632
663
  }
633
- async function getExperimentAssignment(options) {
664
+ function getExperimentAssignment(options) {
665
+ var _a;
666
+ return fetchAssignment(options, (_a = options.experimentId) !== null && _a !== void 0 ? _a : '');
667
+ }
668
+ function getAssignments(options) {
669
+ return fetchAssignment(options, undefined);
670
+ }
671
+ async function fetchAssignment(options, experimentId) {
634
672
  var _a, _b, _c, _d, _e, _f;
635
- const idProblem = invalidAssignmentIdField(options);
673
+ const idProblem = invalidAssignmentIdField({ ...options, experimentId });
636
674
  if (idProblem)
637
675
  throw new Error(`${idProblem.field} ${idProblem.message}`);
638
676
  if (options.timeoutMs !== undefined &&
@@ -658,7 +696,7 @@ async function getExperimentAssignment(options) {
658
696
  : clampHeader(incoming.get('referer'), ASSIGNMENT_FIELD_LIMITS.referrer);
659
697
  const requestBody = {
660
698
  projectId: options.projectId,
661
- experimentId: options.experimentId,
699
+ ...(experimentId !== undefined ? { experimentId } : {}),
662
700
  userToken: options.userToken,
663
701
  context: {
664
702
  currentUrl: options.currentUrl,
@@ -672,7 +710,7 @@ async function getExperimentAssignment(options) {
672
710
  userVisitCount,
673
711
  },
674
712
  };
675
- const response = await fetch(`${edgeBaseUrl(options.edgeUrl)}/api/v1/assignment`, {
713
+ const response = await edgeFetch(`${edgeBaseUrl(options.edgeUrl)}/api/v1/assignment`, {
676
714
  method: 'POST',
677
715
  headers: createAssignmentHeaders(incoming),
678
716
  signal: controller.signal,
@@ -681,7 +719,7 @@ async function getExperimentAssignment(options) {
681
719
  if (!response.ok)
682
720
  return undefined;
683
721
  const result = await response.json();
684
- return isExperimentAssignmentResult(result, options.experimentId)
722
+ return isExperimentAssignmentResult(result, experimentId)
685
723
  ? result
686
724
  : undefined;
687
725
  }
@@ -697,7 +735,7 @@ async function requestFeatureFlags(options) {
697
735
  const controller = new AbortController();
698
736
  const timeout = setTimeout(() => controller.abort(), (_a = options.requestTimeoutMs) !== null && _a !== void 0 ? _a : 3000);
699
737
  try {
700
- const response = await fetch(`${edgeBaseUrl(options.edgeUrl)}/api/v1/flags?v=1`, {
738
+ const response = await edgeFetch(`${edgeBaseUrl(options.edgeUrl)}/api/v1/flags?v=1`, {
701
739
  method: 'POST',
702
740
  headers: {
703
741
  'Content-Type': 'application/json',
@@ -784,7 +822,7 @@ async function fetchAntiflickerFromEdge(projectId, currentUrl, referrer, userCon
784
822
  const config = encodeURIComponent(JSON.stringify(payload));
785
823
  const edgeUrl = new URL(`${"https://edge.cofra.me"}/cf.css`);
786
824
  edgeUrl.searchParams.set('config', config);
787
- const response = await fetch(edgeUrl, {
825
+ const response = await edgeFetch(edgeUrl, {
788
826
  headers,
789
827
  });
790
828
  if (!response.ok)
@@ -836,6 +874,7 @@ async function getProjectData(options) {
836
874
  exports.evaluateFlags = evaluateFlags;
837
875
  exports.flushFeatureFlagEvents = flushFeatureFlagEvents;
838
876
  exports.generateUserToken = generateUserToken;
877
+ exports.getAssignments = getAssignments;
839
878
  exports.getExperimentAssignment = getExperimentAssignment;
840
879
  exports.getFeatureFlag = getFeatureFlag;
841
880
  exports.getFeatureFlagPayload = getFeatureFlagPayload;
@@ -234,6 +234,8 @@ const ID_FIELDS = ['projectId', 'experimentId', 'userToken'];
234
234
  function invalidAssignmentIdField(body) {
235
235
  for (const field of ID_FIELDS) {
236
236
  const value = body[field];
237
+ if (field === 'experimentId' && value === undefined)
238
+ continue;
237
239
  if (typeof value !== 'string' || !value) {
238
240
  return { field, message: 'is required' };
239
241
  }
@@ -513,6 +515,25 @@ async function flushFeatureFlagEvents() {
513
515
  }
514
516
  }
515
517
 
518
+ const IDLE_MS = 60000;
519
+ let dispatcher;
520
+ function runsOnNode() {
521
+ var _a;
522
+ return (typeof process !== 'undefined' &&
523
+ typeof ((_a = process.versions) === null || _a === void 0 ? void 0 : _a.node) === 'string' &&
524
+ !('EdgeRuntime' in globalThis));
525
+ }
526
+ function edgeDispatcher() {
527
+ if (!runsOnNode())
528
+ return Promise.resolve(undefined);
529
+ dispatcher !== null && dispatcher !== void 0 ? dispatcher : (dispatcher = import('undici').then(({ Agent }) => new Agent({ keepAliveTimeout: IDLE_MS, keepAliveMaxTimeout: IDLE_MS }), () => undefined));
530
+ return dispatcher;
531
+ }
532
+ async function edgeFetch(url, init = {}) {
533
+ const agent = await edgeDispatcher();
534
+ return fetch(url, agent ? { ...init, dispatcher: agent } : init);
535
+ }
536
+
516
537
  const DEFAULT_ASSIGNMENT_TIMEOUT_MS = 1500;
517
538
  function normalizeRequestHeaders(headers) {
518
539
  const normalized = new Headers();
@@ -599,6 +620,13 @@ function effectiveVisitCount(headers) {
599
620
  const next = nextVisitCount(existing, lastVisitTime, Date.now());
600
621
  return Number.isSafeInteger(next) ? next : existing;
601
622
  }
623
+ function isAssignedVariant(value) {
624
+ return (isRecord(value) &&
625
+ typeof value.id === 'string' &&
626
+ typeof value.slug === 'string' &&
627
+ typeof value.experimentId === 'string' &&
628
+ typeof value.iterationId === 'string');
629
+ }
602
630
  function isExperimentAssignmentResult(value, experimentId) {
603
631
  if (!isRecord(value) || !isRecord(value.projectData))
604
632
  return false;
@@ -615,22 +643,32 @@ function isExperimentAssignmentResult(value, experimentId) {
615
643
  context.userVisitCount < 0) {
616
644
  return false;
617
645
  }
646
+ if (value.variants !== undefined && !Array.isArray(value.variants))
647
+ return false;
648
+ if (Array.isArray(value.variants) && !value.variants.every(isAssignedVariant))
649
+ return false;
650
+ if (experimentId === undefined) {
651
+ return value.variant === null && Array.isArray(value.variants);
652
+ }
618
653
  if (value.variant === null) {
619
654
  return typeof value.reason === 'string';
620
655
  }
621
- if (!isRecord(value.variant))
622
- return false;
623
- return (typeof value.variant.id === 'string' &&
624
- typeof value.variant.slug === 'string' &&
625
- value.variant.experimentId === experimentId &&
626
- typeof value.variant.iterationId === 'string');
656
+ return (isAssignedVariant(value.variant) &&
657
+ value.variant.experimentId === experimentId);
627
658
  }
628
659
  function generateUserToken() {
629
660
  return generateUUID();
630
661
  }
631
- async function getExperimentAssignment(options) {
662
+ function getExperimentAssignment(options) {
663
+ var _a;
664
+ return fetchAssignment(options, (_a = options.experimentId) !== null && _a !== void 0 ? _a : '');
665
+ }
666
+ function getAssignments(options) {
667
+ return fetchAssignment(options, undefined);
668
+ }
669
+ async function fetchAssignment(options, experimentId) {
632
670
  var _a, _b, _c, _d, _e, _f;
633
- const idProblem = invalidAssignmentIdField(options);
671
+ const idProblem = invalidAssignmentIdField({ ...options, experimentId });
634
672
  if (idProblem)
635
673
  throw new Error(`${idProblem.field} ${idProblem.message}`);
636
674
  if (options.timeoutMs !== undefined &&
@@ -656,7 +694,7 @@ async function getExperimentAssignment(options) {
656
694
  : clampHeader(incoming.get('referer'), ASSIGNMENT_FIELD_LIMITS.referrer);
657
695
  const requestBody = {
658
696
  projectId: options.projectId,
659
- experimentId: options.experimentId,
697
+ ...(experimentId !== undefined ? { experimentId } : {}),
660
698
  userToken: options.userToken,
661
699
  context: {
662
700
  currentUrl: options.currentUrl,
@@ -670,7 +708,7 @@ async function getExperimentAssignment(options) {
670
708
  userVisitCount,
671
709
  },
672
710
  };
673
- const response = await fetch(`${edgeBaseUrl(options.edgeUrl)}/api/v1/assignment`, {
711
+ const response = await edgeFetch(`${edgeBaseUrl(options.edgeUrl)}/api/v1/assignment`, {
674
712
  method: 'POST',
675
713
  headers: createAssignmentHeaders(incoming),
676
714
  signal: controller.signal,
@@ -679,7 +717,7 @@ async function getExperimentAssignment(options) {
679
717
  if (!response.ok)
680
718
  return undefined;
681
719
  const result = await response.json();
682
- return isExperimentAssignmentResult(result, options.experimentId)
720
+ return isExperimentAssignmentResult(result, experimentId)
683
721
  ? result
684
722
  : undefined;
685
723
  }
@@ -695,7 +733,7 @@ async function requestFeatureFlags(options) {
695
733
  const controller = new AbortController();
696
734
  const timeout = setTimeout(() => controller.abort(), (_a = options.requestTimeoutMs) !== null && _a !== void 0 ? _a : 3000);
697
735
  try {
698
- const response = await fetch(`${edgeBaseUrl(options.edgeUrl)}/api/v1/flags?v=1`, {
736
+ const response = await edgeFetch(`${edgeBaseUrl(options.edgeUrl)}/api/v1/flags?v=1`, {
699
737
  method: 'POST',
700
738
  headers: {
701
739
  'Content-Type': 'application/json',
@@ -782,7 +820,7 @@ async function fetchAntiflickerFromEdge(projectId, currentUrl, referrer, userCon
782
820
  const config = encodeURIComponent(JSON.stringify(payload));
783
821
  const edgeUrl = new URL(`${"https://edge.cofra.me"}/cf.css`);
784
822
  edgeUrl.searchParams.set('config', config);
785
- const response = await fetch(edgeUrl, {
823
+ const response = await edgeFetch(edgeUrl, {
786
824
  headers,
787
825
  });
788
826
  if (!response.ok)
@@ -831,4 +869,4 @@ async function getProjectData(options) {
831
869
  return null;
832
870
  }
833
871
 
834
- export { evaluateFlags, flushFeatureFlagEvents, generateUserToken, getExperimentAssignment, getFeatureFlag, getFeatureFlagPayload, getFeatureFlagResult, getProjectData, getRemoteConfig, isFeatureEnabled };
872
+ export { evaluateFlags, flushFeatureFlagEvents, generateUserToken, getAssignments, getExperimentAssignment, getFeatureFlag, getFeatureFlagPayload, getFeatureFlagResult, getProjectData, getRemoteConfig, isFeatureEnabled };
@@ -68,6 +68,7 @@ type ModificationAddBanner = {
68
68
  type ModificationExecuteJs = {
69
69
  type: 'execute_js';
70
70
  js: string;
71
+ css?: string | string[];
71
72
  };
72
73
  type ModalTrigger = 'exit' | 'scroll' | 'enter' | 'timer';
73
74
 
@@ -88,6 +88,7 @@ type ModificationAddBanner = {
88
88
  type ModificationExecuteJs = {
89
89
  type: 'execute_js';
90
90
  js: string;
91
+ css?: string | string[];
91
92
  };
92
93
  type ModalTrigger = 'exit' | 'scroll' | 'enter' | 'timer';
93
94
 
@@ -68,6 +68,7 @@ type ModificationAddBanner = {
68
68
  type ModificationExecuteJs = {
69
69
  type: 'execute_js';
70
70
  js: string;
71
+ css?: string | string[];
71
72
  };
72
73
  type ModalTrigger = 'exit' | 'scroll' | 'enter' | 'timer';
73
74
 
@@ -268,7 +269,7 @@ interface AssignedVariant$1 {
268
269
  experimentId: string;
269
270
  iterationId: string;
270
271
  }
271
- type AssignmentMissReason$1 = 'experiment_not_found' | 'not_assigned' | 'unsupported_experiment_type' | 'preview_active';
272
+ type AssignmentMissReason$1 = 'experiment_not_found' | 'not_assigned' | 'unsupported_experiment_type' | 'preview_active' | 'no_target_experiment';
272
273
  type AssignmentOutcome<Reason extends string = AssignmentMissReason$1> = {
273
274
  variant: AssignedVariant$1;
274
275
  } | {
@@ -276,6 +277,7 @@ type AssignmentOutcome<Reason extends string = AssignmentMissReason$1> = {
276
277
  reason: Reason;
277
278
  };
278
279
  type AssignmentResponse<Reason extends string = AssignmentMissReason$1> = AssignmentOutcome<Reason> & {
280
+ variants?: AssignedVariant$1[];
279
281
  projectData: AssignmentProjectData;
280
282
  };
281
283
 
@@ -307,6 +309,7 @@ interface FeatureFlagSelector {
307
309
 
308
310
  declare function flushFeatureFlagEvents(): Promise<void>;
309
311
 
312
+ type AssignmentFetchOptions = Omit<GetExperimentAssignmentOptions, 'experimentId'>;
310
313
  type RequestHeaders = Headers | Record<string, string | string[] | undefined>;
311
314
  interface GetExperimentAssignmentOptions extends AssignmentContext {
312
315
  projectId: string;
@@ -335,7 +338,9 @@ interface CoframeProjectPayload {
335
338
  antiflickerCSS?: string;
336
339
  }
337
340
  declare function generateUserToken(): string;
341
+ type GetAssignmentsOptions = AssignmentFetchOptions;
338
342
  declare function getExperimentAssignment(options: GetExperimentAssignmentOptions): Promise<ExperimentAssignmentResult | undefined>;
343
+ declare function getAssignments(options: GetAssignmentsOptions): Promise<ExperimentAssignmentResult | undefined>;
339
344
  interface EvaluateFlagsOptions {
340
345
  apiKey: string;
341
346
  projectId: string;
@@ -365,5 +370,5 @@ declare const getRemoteConfig: typeof getFeatureFlagPayload;
365
370
 
366
371
  declare function getProjectData(options: GetProjectDataOptions): Promise<CoframeProjectPayload | null>;
367
372
 
368
- export { evaluateFlags, flushFeatureFlagEvents, generateUserToken, getExperimentAssignment, getFeatureFlag, getFeatureFlagPayload, getFeatureFlagResult, getProjectData, getRemoteConfig, isFeatureEnabled };
369
- export type { AssignedVariant, AssignmentMissReason, CoframeProjectPayload, EvaluateFlagsOptions, ExperimentAssignmentResult, FeatureFlagServerReadOptions, GetExperimentAssignmentOptions, GetProjectDataOptions, RequestHeaders };
373
+ export { evaluateFlags, flushFeatureFlagEvents, generateUserToken, getAssignments, getExperimentAssignment, getFeatureFlag, getFeatureFlagPayload, getFeatureFlagResult, getProjectData, getRemoteConfig, isFeatureEnabled };
374
+ export type { AssignedVariant, AssignmentMissReason, CoframeProjectPayload, EvaluateFlagsOptions, ExperimentAssignmentResult, FeatureFlagServerReadOptions, GetAssignmentsOptions, GetExperimentAssignmentOptions, GetProjectDataOptions, RequestHeaders };
package/package.json CHANGED
@@ -1,37 +1,37 @@
1
1
  {
2
2
  "name": "@coframe/sdk",
3
- "version": "1.1.56",
3
+ "version": "1.1.60",
4
4
  "description": "Coframe optimizes your website using generative AI to deliver maximum impact to your audience and allow your site to improve itself as you sleep. Learn more at coframe.com.",
5
5
  "main": "dist/pkg/cf.cjs.js",
6
6
  "module": "dist/pkg/cf.esm.mjs",
7
7
  "types": "dist/pkg/types/index.d.ts",
8
8
  "exports": {
9
9
  ".": {
10
+ "types": "./dist/pkg/types/index.d.ts",
10
11
  "import": "./dist/pkg/cf.esm.mjs",
11
- "require": "./dist/pkg/cf.cjs.js",
12
- "types": "./dist/pkg/types/index.d.ts"
12
+ "require": "./dist/pkg/cf.cjs.js"
13
13
  },
14
14
  "./server": {
15
+ "types": "./dist/pkg/types/server.d.ts",
15
16
  "import": "./dist/pkg/server.esm.mjs",
16
- "require": "./dist/pkg/server.cjs.js",
17
- "types": "./dist/pkg/types/server.d.ts"
17
+ "require": "./dist/pkg/server.cjs.js"
18
18
  },
19
19
  "./feature-flags": {
20
+ "types": "./dist/pkg/types/feature-flags.d.ts",
20
21
  "import": "./dist/pkg/feature-flags.esm.mjs",
21
- "require": "./dist/pkg/feature-flags.cjs.js",
22
- "types": "./dist/pkg/types/feature-flags.d.ts"
22
+ "require": "./dist/pkg/feature-flags.cjs.js"
23
23
  },
24
24
  "./react": {
25
+ "types": "./dist/pkg/types/react.d.ts",
25
26
  "import": "./dist/pkg/react.esm.mjs",
26
- "require": "./dist/pkg/react.cjs.js",
27
- "types": "./dist/pkg/types/react.d.ts"
27
+ "require": "./dist/pkg/react.cjs.js"
28
28
  }
29
29
  },
30
30
  "files": [
31
31
  "dist/pkg"
32
32
  ],
33
33
  "scripts": {
34
- "test:jest": "jest --maxWorkers=2",
34
+ "test:jest": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js --maxWorkers=2",
35
35
  "test:playwright": "npx playwright test --retries=5 --reporter=list",
36
36
  "test:package": "node ./tests/package/scripts/test-package.mjs",
37
37
  "test:browserstack:full": "jest --config jest.browserstack.config.js --suite=full",
@@ -100,7 +100,7 @@
100
100
  "dependencies": {
101
101
  "@rrweb/record": "2.1.0",
102
102
  "fflate": "0.8.3",
103
- "typescript": "^6.0.2"
103
+ "undici": "7.30.1"
104
104
  },
105
105
  "devDependencies": {
106
106
  "@babel/core": "^7.29.6",
@@ -122,9 +122,7 @@
122
122
  "jest-expect-message": "^1.1.3",
123
123
  "jest-localstorage-mock": "^2.4.26",
124
124
  "jest-raw-loader": "^1.0.1",
125
- "path-exists-cli": "^2.0.0",
126
125
  "playwright": "1.58.2",
127
- "prettier": "^3.8.1",
128
126
  "rollup": "^4.60.0",
129
127
  "rollup-plugin-dts": "^6.4.1",
130
128
  "rollup-plugin-json": "^4.0.0",
@@ -132,9 +130,9 @@
132
130
  "rollup-plugin-string": "^3.0.0",
133
131
  "rollup-plugin-typescript2": "^0.36.0",
134
132
  "selenium-webdriver": "^4.41.0",
135
- "terser": "^5.46.1",
136
133
  "ts-jest": "^29.4.6",
137
134
  "tslib": "^2.8.1",
135
+ "typescript": "^6.0.2",
138
136
  "ua-parser-js": "^2.0.10"
139
137
  },
140
138
  "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"