@createcms/core 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/README.md +169 -0
  2. package/dist/ab-edge/index.cjs +214 -0
  3. package/dist/ab-edge/index.d.cts +121 -0
  4. package/dist/ab-edge/index.d.ts +121 -0
  5. package/dist/ab-edge/index.js +205 -0
  6. package/dist/bin/createcms.js +3082 -0
  7. package/dist/db.cjs +496 -0
  8. package/dist/db.d.cts +128 -0
  9. package/dist/db.d.ts +128 -0
  10. package/dist/db.js +488 -0
  11. package/dist/index.cjs +13789 -0
  12. package/dist/index.d.cts +10277 -0
  13. package/dist/index.d.ts +10277 -0
  14. package/dist/index.js +13737 -0
  15. package/dist/nanoid.cjs +50 -0
  16. package/dist/nanoid.d.cts +29 -0
  17. package/dist/nanoid.d.ts +29 -0
  18. package/dist/nanoid.js +47 -0
  19. package/dist/next/index.cjs +60 -0
  20. package/dist/next/index.d.cts +141 -0
  21. package/dist/next/index.d.ts +141 -0
  22. package/dist/next/index.js +58 -0
  23. package/dist/next/middleware.cjs +113 -0
  24. package/dist/next/middleware.d.cts +77 -0
  25. package/dist/next/middleware.d.ts +77 -0
  26. package/dist/next/middleware.js +111 -0
  27. package/dist/plugins/ab-test/analytics/upstash.cjs +345 -0
  28. package/dist/plugins/ab-test/analytics/upstash.d.cts +193 -0
  29. package/dist/plugins/ab-test/analytics/upstash.d.ts +193 -0
  30. package/dist/plugins/ab-test/analytics/upstash.js +343 -0
  31. package/dist/plugins/ab-test/client.cjs +686 -0
  32. package/dist/plugins/ab-test/client.d.cts +233 -0
  33. package/dist/plugins/ab-test/client.d.ts +233 -0
  34. package/dist/plugins/ab-test/client.js +684 -0
  35. package/dist/plugins/ab-test/index.cjs +3400 -0
  36. package/dist/plugins/ab-test/index.d.cts +1131 -0
  37. package/dist/plugins/ab-test/index.d.ts +1131 -0
  38. package/dist/plugins/ab-test/index.js +3367 -0
  39. package/dist/plugins/client.cjs +20 -0
  40. package/dist/plugins/client.d.cts +3 -0
  41. package/dist/plugins/client.d.ts +3 -0
  42. package/dist/plugins/client.js +3 -0
  43. package/dist/plugins/consent/client.cjs +315 -0
  44. package/dist/plugins/consent/client.d.cts +145 -0
  45. package/dist/plugins/consent/client.d.ts +145 -0
  46. package/dist/plugins/consent/client.js +313 -0
  47. package/dist/plugins/consent/index.cjs +267 -0
  48. package/dist/plugins/consent/index.d.cts +618 -0
  49. package/dist/plugins/consent/index.d.ts +618 -0
  50. package/dist/plugins/consent/index.js +258 -0
  51. package/dist/plugins/i18n/index.cjs +2177 -0
  52. package/dist/plugins/i18n/index.d.cts +562 -0
  53. package/dist/plugins/i18n/index.d.ts +562 -0
  54. package/dist/plugins/i18n/index.js +2150 -0
  55. package/dist/plugins/media-optimize/index.cjs +315 -0
  56. package/dist/plugins/media-optimize/index.d.cts +144 -0
  57. package/dist/plugins/media-optimize/index.d.ts +144 -0
  58. package/dist/plugins/media-optimize/index.js +311 -0
  59. package/dist/plugins/multi-tenant/index.cjs +210 -0
  60. package/dist/plugins/multi-tenant/index.d.cts +431 -0
  61. package/dist/plugins/multi-tenant/index.d.ts +431 -0
  62. package/dist/plugins/multi-tenant/index.js +207 -0
  63. package/dist/plugins/server.cjs +24 -0
  64. package/dist/plugins/server.d.cts +3 -0
  65. package/dist/plugins/server.d.ts +3 -0
  66. package/dist/plugins/server.js +3 -0
  67. package/dist/react/blocks.cjs +233 -0
  68. package/dist/react/blocks.d.cts +320 -0
  69. package/dist/react/blocks.d.ts +320 -0
  70. package/dist/react/blocks.js +226 -0
  71. package/dist/react/index.cjs +901 -0
  72. package/dist/react/index.d.cts +992 -0
  73. package/dist/react/index.d.ts +992 -0
  74. package/dist/react/index.js +872 -0
  75. package/dist/react/tracking.cjs +243 -0
  76. package/dist/react/tracking.d.cts +364 -0
  77. package/dist/react/tracking.d.ts +364 -0
  78. package/dist/react/tracking.js +216 -0
  79. package/dist/react/variant.cjs +59 -0
  80. package/dist/react/variant.d.cts +26 -0
  81. package/dist/react/variant.d.ts +26 -0
  82. package/dist/react/variant.js +57 -0
  83. package/package.json +303 -0
@@ -0,0 +1,205 @@
1
+ /**
2
+ * MurmurHash3 (32-bit) for deterministic variant assignment.
3
+ * Inlined to avoid an external dependency for ~30 lines of code.
4
+ */ function murmur3(key, seed = 0) {
5
+ let h = seed >>> 0;
6
+ const len = key.length;
7
+ let i = 0;
8
+ while(i + 4 <= len){
9
+ let k = key.charCodeAt(i) & 0xff | (key.charCodeAt(i + 1) & 0xff) << 8 | (key.charCodeAt(i + 2) & 0xff) << 16 | (key.charCodeAt(i + 3) & 0xff) << 24;
10
+ k = Math.imul(k, 0xcc9e2d51);
11
+ k = k << 15 | k >>> 17;
12
+ k = Math.imul(k, 0x1b873593);
13
+ h ^= k;
14
+ h = h << 13 | h >>> 19;
15
+ h = Math.imul(h, 5) + 0xe6546b64;
16
+ i += 4;
17
+ }
18
+ let k = 0;
19
+ switch(len & 3){
20
+ case 3:
21
+ k ^= (key.charCodeAt(i + 2) & 0xff) << 16;
22
+ // falls through
23
+ case 2:
24
+ k ^= (key.charCodeAt(i + 1) & 0xff) << 8;
25
+ // falls through
26
+ case 1:
27
+ k ^= key.charCodeAt(i) & 0xff;
28
+ k = Math.imul(k, 0xcc9e2d51);
29
+ k = k << 15 | k >>> 17;
30
+ k = Math.imul(k, 0x1b873593);
31
+ h ^= k;
32
+ }
33
+ h ^= len;
34
+ h ^= h >>> 16;
35
+ h = Math.imul(h, 0x85ebca6b);
36
+ h ^= h >>> 13;
37
+ h = Math.imul(h, 0xc2b2ae35);
38
+ h ^= h >>> 16;
39
+ return h >>> 0;
40
+ }
41
+ /**
42
+ * Deterministic variant assignment.
43
+ *
44
+ * The same `contextKey + testId` always produces the same variant.
45
+ * No DB writes needed -- pure function.
46
+ *
47
+ * @param contextKey - Visitor identifier (user ID or anonymous key)
48
+ * @param testId - The A/B test ID
49
+ * @param trafficPercentage - 0-100, how much total traffic enters the test
50
+ * @param variants - Must be sorted by id for stability
51
+ */ function resolveVariant(contextKey, testId, trafficPercentage, variants) {
52
+ const hash = murmur3(contextKey + ':' + testId);
53
+ const bucket = hash % 10000;
54
+ const control = variants.find((v)=>v.isControl);
55
+ if (bucket >= trafficPercentage * 100) {
56
+ return {
57
+ variantId: control.id,
58
+ inTest: false
59
+ };
60
+ }
61
+ const sorted = [
62
+ ...variants
63
+ ].sort((a, b)=>a.id.localeCompare(b.id));
64
+ const normalizedBucket = Math.floor(bucket * 100 / (trafficPercentage * 100));
65
+ let cumulative = 0;
66
+ for (const v of sorted){
67
+ cumulative += v.weight;
68
+ if (normalizedBucket < cumulative) {
69
+ return {
70
+ variantId: v.id,
71
+ inTest: true
72
+ };
73
+ }
74
+ }
75
+ return {
76
+ variantId: control.id,
77
+ inTest: true
78
+ };
79
+ }
80
+
81
+ /**
82
+ * The control sentinel code. Pattern A ALWAYS rewrites (Vercel "precompute"
83
+ * model): a request with no running test / no consent / outside traffic is
84
+ * rewritten to `/<CONTROL_CODE><pathname>` so it still lands on the single
85
+ * `[abVariant]` route — there is no un-coded passthrough. It is never a real
86
+ * branch id (ids are prefixed), and the render route maps it to the control
87
+ * tree (pickVariant fails closed to control for any non-variant code anyway).
88
+ */ const CONTROL_CODE = 'control';
89
+ /**
90
+ * The static path prefix the render route lives under (`app/<prefix>/[abVariant]/
91
+ * [[...rest]]`). Pattern A rewrites every public request UNDER this prefix so the
92
+ * variant-coded route never sits at the URL root — otherwise a root catch-all
93
+ * would shadow sibling app routes (e.g. a `/app/*` dashboard). It is internal +
94
+ * transparent (the browser keeps the original URL), so the value only needs to
95
+ * differ from your real top-level app prefixes; with always-rewrite even a real
96
+ * page slugged `/ab` still works (it is rewritten THROUGH the prefix).
97
+ */ const DEFAULT_VARIANT_PREFIX = '/ab';
98
+ /**
99
+ * Build the variant-coded rewrite path: `<prefix>/<code><pathname>`. The variant
100
+ * code is the first segment AFTER the static prefix — the cache key — never a
101
+ * header/searchParam, so each variant is its own CDN/ISR cache entry (Vercel
102
+ * Flags precompute pattern, nested under `<prefix>` to coexist with other app
103
+ * routes). The matching render route reads the code segment back.
104
+ */ function variantRewritePath(prefix, code, pathname) {
105
+ // For the root path, omit the trailing slash (`<prefix>/<code>`, not
106
+ // `<prefix>/<code>/`) so the optional-catch-all variant route matches cleanly.
107
+ const suffix = pathname === '/' ? '' : pathname;
108
+ return `${prefix}/${encodeURIComponent(code)}${suffix}`;
109
+ }
110
+ /** Parse a first-party consent cookie value; analytics granted → true. */ function parseConsentCookie(value) {
111
+ if (!value) return false;
112
+ try {
113
+ const state = JSON.parse(decodeURIComponent(value));
114
+ return state.analytics_storage === 'granted';
115
+ } catch {
116
+ return false;
117
+ }
118
+ }
119
+ /**
120
+ * Edge-safe random visitor id (mirrors the client `anon_` key shape). Uses the
121
+ * `crypto` global (Web Crypto), available in every edge/worker/browser runtime.
122
+ */ function generateEdgeVisitorId() {
123
+ const bytes = new Uint8Array(16);
124
+ crypto.getRandomValues(bytes);
125
+ let hex = '';
126
+ for (const b of bytes)hex += b.toString(16).padStart(2, '0');
127
+ return `anon_${hex}`;
128
+ }
129
+ /**
130
+ * PURE bucketing: given a one-shot `key`, the branch to render IN-TEST, or null =
131
+ * serve control (no running test, or the roll lands outside the test traffic).
132
+ * NO consent / NO persistent identifier — the caller passes an EPHEMERAL random
133
+ * key for a first assignment and then persists only the chosen branch (a
134
+ * variant-only cookie), so nothing identifying is stored. Reuses the same
135
+ * weighted hash as the server pick.
136
+ */ function pickEdgeVariant(opts) {
137
+ const test = opts.resolve.test;
138
+ if (!test) return {
139
+ branchId: null
140
+ };
141
+ const result = resolveVariant(opts.key, test.testId, test.trafficPercentage, test.variants.map((v)=>({
142
+ id: v.variantId,
143
+ weight: v.weight,
144
+ isControl: v.isControl
145
+ })));
146
+ if (!result.inTest) return {
147
+ branchId: null
148
+ }; // outside traffic → control
149
+ const picked = test.variants.find((v)=>v.variantId === result.variantId);
150
+ return {
151
+ branchId: picked?.branchId ?? null
152
+ };
153
+ }
154
+ /**
155
+ * The framework-agnostic edge decision (ALWAYS-rewrite / Vercel precompute).
156
+ * CONSENT-FREE by design: serving a variant + a VARIANT-ONLY cookie (no visitor
157
+ * id, no behavioural data, no third-party transmission) falls under the ePrivacy
158
+ * "strictly necessary" exemption, so fresh ad traffic gets real variants (not
159
+ * always control).
160
+ *
161
+ * - `assignedCode` is the value of the test's variant cookie (`ab_<testId>`) — a
162
+ * branch id, or the control sentinel — from a previous visit. If still valid
163
+ * it is REUSED (consistent variant across requests, no re-roll).
164
+ * - Otherwise a FIRST assignment is rolled from an ephemeral random key; the
165
+ * chosen code is returned in `assignCode` for the adapter to persist in the
166
+ * variant cookie. Out-of-traffic → the control sentinel (no impression).
167
+ *
168
+ * `rewritePath` is always non-null. Returns the `testId` so the adapter knows
169
+ * which cookie to set. No persistent identifier is ever minted here — the
170
+ * consent-gated visitor id + GA4 forwarding live in the client pipeline.
171
+ */ function decideEdgeVariant(input) {
172
+ const controlCode = input.controlCode ?? CONTROL_CODE;
173
+ const prefix = input.variantPrefix ?? DEFAULT_VARIANT_PREFIX;
174
+ const test = input.resolve.test;
175
+ if (!test) {
176
+ return {
177
+ rewritePath: variantRewritePath(prefix, controlCode, input.pathname),
178
+ assignCode: null,
179
+ testId: null
180
+ };
181
+ }
182
+ // Reuse a still-valid prior assignment (a published variant branch, or the
183
+ // control sentinel for an out-of-traffic visitor) → consistent, no re-roll.
184
+ const reusable = input.assignedCode === controlCode || input.assignedCode != null && test.variants.some((v)=>v.branchId === input.assignedCode);
185
+ if (reusable) {
186
+ return {
187
+ rewritePath: variantRewritePath(prefix, input.assignedCode, input.pathname),
188
+ assignCode: null,
189
+ testId: test.testId
190
+ };
191
+ }
192
+ // First assignment: roll once from an ephemeral (non-persisted) random key.
193
+ const { branchId } = pickEdgeVariant({
194
+ key: generateEdgeVisitorId(),
195
+ resolve: input.resolve
196
+ });
197
+ const code = branchId ?? controlCode; // out-of-traffic → control sentinel
198
+ return {
199
+ rewritePath: variantRewritePath(prefix, code, input.pathname),
200
+ assignCode: code,
201
+ testId: test.testId
202
+ };
203
+ }
204
+
205
+ export { CONTROL_CODE, DEFAULT_VARIANT_PREFIX, decideEdgeVariant, generateEdgeVisitorId, parseConsentCookie, pickEdgeVariant, resolveVariant, variantRewritePath };