@cloudcart/nitro 0.1.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.
Files changed (49) hide show
  1. package/dist/analytics/index.d.ts +39 -0
  2. package/dist/analytics/index.js +13 -0
  3. package/dist/analytics/index.js.map +1 -0
  4. package/dist/cache/index.d.ts +39 -0
  5. package/dist/cache/index.js +19 -0
  6. package/dist/cache/index.js.map +1 -0
  7. package/dist/cart/index.d.ts +73 -0
  8. package/dist/cart/index.js +9 -0
  9. package/dist/cart/index.js.map +1 -0
  10. package/dist/chunk-2VLIYNGM.js +51 -0
  11. package/dist/chunk-2VLIYNGM.js.map +1 -0
  12. package/dist/chunk-3YTOR4GE.js +25 -0
  13. package/dist/chunk-3YTOR4GE.js.map +1 -0
  14. package/dist/chunk-6DOUKEDG.js +555 -0
  15. package/dist/chunk-6DOUKEDG.js.map +1 -0
  16. package/dist/chunk-IZ4Y4UBN.js +279 -0
  17. package/dist/chunk-IZ4Y4UBN.js.map +1 -0
  18. package/dist/chunk-JHZ4UBA2.js +57 -0
  19. package/dist/chunk-JHZ4UBA2.js.map +1 -0
  20. package/dist/chunk-LVUU4ZBR.js +65 -0
  21. package/dist/chunk-LVUU4ZBR.js.map +1 -0
  22. package/dist/chunk-RKT3VKU2.js +94 -0
  23. package/dist/chunk-RKT3VKU2.js.map +1 -0
  24. package/dist/chunk-RTJ73SCX.js +45 -0
  25. package/dist/chunk-RTJ73SCX.js.map +1 -0
  26. package/dist/chunk-WJCN2EO3.js +47 -0
  27. package/dist/chunk-WJCN2EO3.js.map +1 -0
  28. package/dist/csp/index.d.ts +38 -0
  29. package/dist/csp/index.js +11 -0
  30. package/dist/csp/index.js.map +1 -0
  31. package/dist/errors-a5iG09pc.d.ts +42 -0
  32. package/dist/i18n/index.d.ts +23 -0
  33. package/dist/i18n/index.js +9 -0
  34. package/dist/i18n/index.js.map +1 -0
  35. package/dist/index.d.ts +54 -0
  36. package/dist/index.js +108 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/seo/index.d.ts +59 -0
  39. package/dist/seo/index.js +11 -0
  40. package/dist/seo/index.js.map +1 -0
  41. package/dist/session/index.d.ts +17 -0
  42. package/dist/session/index.js +7 -0
  43. package/dist/session/index.js.map +1 -0
  44. package/dist/storefront/index.d.ts +10 -0
  45. package/dist/storefront/index.js +14 -0
  46. package/dist/storefront/index.js.map +1 -0
  47. package/dist/types-BIsRLMWU.d.ts +152 -0
  48. package/dist/types-CaRAihQJ.d.ts +9 -0
  49. package/package.json +47 -0
@@ -0,0 +1,279 @@
1
+ // src/cart/queries.ts
2
+ var CART_FRAGMENT = `#graphql
3
+ fragment CartFields on Cart {
4
+ id
5
+ totalQuantity
6
+ lines(first: 100) {
7
+ nodes {
8
+ id
9
+ quantity
10
+ merchandise {
11
+ ... on ProductVariant {
12
+ id
13
+ title
14
+ product { title handle }
15
+ price { amount currencyCode }
16
+ image { id url altText width height }
17
+ selectedOptions { name value }
18
+ }
19
+ }
20
+ cost {
21
+ totalAmount { amount currencyCode }
22
+ }
23
+ }
24
+ }
25
+ cost {
26
+ subtotalAmount { amount currencyCode }
27
+ totalAmount { amount currencyCode }
28
+ }
29
+ discountCodes { code }
30
+ }
31
+ `;
32
+ var CART_QUERY = `#graphql
33
+ ${CART_FRAGMENT}
34
+ query CartQuery($cartId: ID!) {
35
+ cart(id: $cartId) {
36
+ ...CartFields
37
+ }
38
+ }
39
+ `;
40
+ var CART_CREATE_MUTATION = `#graphql
41
+ ${CART_FRAGMENT}
42
+ mutation CartCreate($input: CartInput!) {
43
+ cartCreate(input: $input) {
44
+ cart { ...CartFields }
45
+ userErrors { message field code }
46
+ }
47
+ }
48
+ `;
49
+ var CART_LINES_ADD_MUTATION = `#graphql
50
+ ${CART_FRAGMENT}
51
+ mutation CartLinesAdd($cartId: ID!, $lines: [CartLineInput!]!) {
52
+ cartLinesAdd(cartId: $cartId, lines: $lines) {
53
+ cart { ...CartFields }
54
+ userErrors { message field code }
55
+ }
56
+ }
57
+ `;
58
+ var CART_LINES_UPDATE_MUTATION = `#graphql
59
+ ${CART_FRAGMENT}
60
+ mutation CartLinesUpdate($cartId: ID!, $lines: [CartLineUpdateInput!]!) {
61
+ cartLinesUpdate(cartId: $cartId, lines: $lines) {
62
+ cart { ...CartFields }
63
+ userErrors { message field code }
64
+ }
65
+ }
66
+ `;
67
+ var CART_LINES_REMOVE_MUTATION = `#graphql
68
+ ${CART_FRAGMENT}
69
+ mutation CartLinesRemove($cartId: ID!, $lineIds: [ID!]!) {
70
+ cartLinesRemove(cartId: $cartId, lineIds: $lineIds) {
71
+ cart { ...CartFields }
72
+ userErrors { message field code }
73
+ }
74
+ }
75
+ `;
76
+ var CART_DISCOUNT_CODES_UPDATE_MUTATION = `#graphql
77
+ ${CART_FRAGMENT}
78
+ mutation CartDiscountCodesUpdate($cartId: ID!, $discountCodes: [String!]!) {
79
+ cartDiscountCodesUpdate(cartId: $cartId, discountCodes: $discountCodes) {
80
+ cart { ...CartFields }
81
+ userErrors { message field code }
82
+ }
83
+ }
84
+ `;
85
+ var CART_BUYER_IDENTITY_UPDATE_MUTATION = `#graphql
86
+ ${CART_FRAGMENT}
87
+ mutation CartBuyerIdentityUpdate($cartId: ID!, $buyerIdentity: CartBuyerIdentityInput!) {
88
+ cartBuyerIdentityUpdate(cartId: $cartId, buyerIdentity: $buyerIdentity) {
89
+ cart { ...CartFields }
90
+ userErrors { message field code }
91
+ }
92
+ }
93
+ `;
94
+ var CART_NOTE_UPDATE_MUTATION = `#graphql
95
+ ${CART_FRAGMENT}
96
+ mutation CartNoteUpdate($cartId: ID!, $note: String!) {
97
+ cartNoteUpdate(cartId: $cartId, note: $note) {
98
+ cart { ...CartFields }
99
+ userErrors { message field code }
100
+ }
101
+ }
102
+ `;
103
+
104
+ // src/cart/handler.ts
105
+ function createCartHandler(storefront, config) {
106
+ async function getOrCreateCartId() {
107
+ const existing = config.getCartId();
108
+ if (existing) return existing;
109
+ const data = await storefront.mutate(
110
+ CART_CREATE_MUTATION,
111
+ { variables: { input: {} } }
112
+ );
113
+ const cartId = data.cartCreate.cart.id;
114
+ config.setCartId(cartId);
115
+ return cartId;
116
+ }
117
+ return {
118
+ async get() {
119
+ const cartId = config.getCartId();
120
+ if (!cartId) return emptyCart();
121
+ const data = await storefront.query(
122
+ CART_QUERY,
123
+ { variables: { cartId } }
124
+ );
125
+ return data.cart ?? emptyCart();
126
+ },
127
+ async addLines(lines) {
128
+ const cartId = await getOrCreateCartId();
129
+ const data = await storefront.mutate(
130
+ CART_LINES_ADD_MUTATION,
131
+ { variables: { cartId, lines } }
132
+ );
133
+ return data.cartLinesAdd.cart;
134
+ },
135
+ async updateLines(lines) {
136
+ const cartId = config.getCartId();
137
+ if (!cartId) return emptyCart();
138
+ const data = await storefront.mutate(
139
+ CART_LINES_UPDATE_MUTATION,
140
+ { variables: { cartId, lines } }
141
+ );
142
+ return data.cartLinesUpdate.cart;
143
+ },
144
+ async removeLines(lineIds) {
145
+ const cartId = config.getCartId();
146
+ if (!cartId) return emptyCart();
147
+ const data = await storefront.mutate(
148
+ CART_LINES_REMOVE_MUTATION,
149
+ { variables: { cartId, lineIds } }
150
+ );
151
+ return data.cartLinesRemove.cart;
152
+ },
153
+ async updateDiscountCodes(codes) {
154
+ const cartId = await getOrCreateCartId();
155
+ const data = await storefront.mutate(
156
+ CART_DISCOUNT_CODES_UPDATE_MUTATION,
157
+ { variables: { cartId, discountCodes: codes } }
158
+ );
159
+ return data.cartDiscountCodesUpdate.cart;
160
+ },
161
+ async updateBuyerIdentity(buyerIdentity) {
162
+ const cartId = await getOrCreateCartId();
163
+ const data = await storefront.mutate(
164
+ CART_BUYER_IDENTITY_UPDATE_MUTATION,
165
+ { variables: { cartId, buyerIdentity } }
166
+ );
167
+ return data.cartBuyerIdentityUpdate.cart;
168
+ },
169
+ async updateNote(note) {
170
+ const cartId = await getOrCreateCartId();
171
+ const data = await storefront.mutate(
172
+ CART_NOTE_UPDATE_MUTATION,
173
+ { variables: { cartId, note } }
174
+ );
175
+ return data.cartNoteUpdate.cart;
176
+ }
177
+ };
178
+ }
179
+ function emptyCart() {
180
+ return {
181
+ id: "",
182
+ totalQuantity: 0,
183
+ lines: { nodes: [] },
184
+ cost: {
185
+ subtotalAmount: { amount: "0.00", currencyCode: "USD" },
186
+ totalAmount: { amount: "0.00", currencyCode: "USD" }
187
+ },
188
+ discountCodes: []
189
+ };
190
+ }
191
+
192
+ // src/cart/mock.ts
193
+ function createMockCartHandler() {
194
+ let cart = emptyCart2();
195
+ function recalculate() {
196
+ let total = 0;
197
+ for (const line of cart.lines.nodes) {
198
+ total += parseFloat(line.cost.totalAmount.amount);
199
+ }
200
+ cart.cost.subtotalAmount.amount = total.toFixed(2);
201
+ cart.cost.totalAmount.amount = total.toFixed(2);
202
+ cart.totalQuantity = cart.lines.nodes.reduce((sum, l) => sum + l.quantity, 0);
203
+ }
204
+ return {
205
+ async get() {
206
+ return cart;
207
+ },
208
+ async addLines(lines) {
209
+ for (const { merchandiseId, quantity } of lines) {
210
+ const existing = cart.lines.nodes.find((l) => l.merchandise.id === merchandiseId);
211
+ if (existing) {
212
+ existing.quantity += quantity;
213
+ existing.cost.totalAmount.amount = (parseFloat(existing.merchandise.price.amount) * existing.quantity).toFixed(2);
214
+ } else {
215
+ cart.lines.nodes.push({
216
+ id: `line-${Date.now()}-${Math.random().toString(36).slice(2)}`,
217
+ quantity,
218
+ merchandise: {
219
+ id: merchandiseId,
220
+ title: "Product Variant",
221
+ product: { title: "Product", handle: "product" },
222
+ price: { amount: "19.99", currencyCode: "USD" },
223
+ image: null,
224
+ selectedOptions: []
225
+ },
226
+ cost: { totalAmount: { amount: (19.99 * quantity).toFixed(2), currencyCode: "USD" } }
227
+ });
228
+ }
229
+ }
230
+ recalculate();
231
+ return cart;
232
+ },
233
+ async updateLines(lines) {
234
+ for (const { id, quantity } of lines) {
235
+ const line = cart.lines.nodes.find((l) => l.id === id);
236
+ if (line) {
237
+ line.quantity = quantity;
238
+ line.cost.totalAmount.amount = (parseFloat(line.merchandise.price.amount) * quantity).toFixed(2);
239
+ }
240
+ }
241
+ cart.lines.nodes = cart.lines.nodes.filter((l) => l.quantity > 0);
242
+ recalculate();
243
+ return cart;
244
+ },
245
+ async removeLines(lineIds) {
246
+ cart.lines.nodes = cart.lines.nodes.filter((l) => !lineIds.includes(l.id));
247
+ recalculate();
248
+ return cart;
249
+ },
250
+ async updateDiscountCodes(codes) {
251
+ cart.discountCodes = codes.map((code) => ({ code }));
252
+ return cart;
253
+ },
254
+ async updateBuyerIdentity() {
255
+ return cart;
256
+ },
257
+ async updateNote() {
258
+ return cart;
259
+ }
260
+ };
261
+ }
262
+ function emptyCart2() {
263
+ return {
264
+ id: "mock-cart",
265
+ totalQuantity: 0,
266
+ lines: { nodes: [] },
267
+ cost: {
268
+ subtotalAmount: { amount: "0.00", currencyCode: "USD" },
269
+ totalAmount: { amount: "0.00", currencyCode: "USD" }
270
+ },
271
+ discountCodes: []
272
+ };
273
+ }
274
+
275
+ export {
276
+ createCartHandler,
277
+ createMockCartHandler
278
+ };
279
+ //# sourceMappingURL=chunk-IZ4Y4UBN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cart/queries.ts","../src/cart/handler.ts","../src/cart/mock.ts"],"sourcesContent":["/**\n * Cart GraphQL queries and mutations for CloudCart Storefront API.\n * Update these when the real API schema is finalized.\n */\n\nconst CART_FRAGMENT = `#graphql\n fragment CartFields on Cart {\n id\n totalQuantity\n lines(first: 100) {\n nodes {\n id\n quantity\n merchandise {\n ... on ProductVariant {\n id\n title\n product { title handle }\n price { amount currencyCode }\n image { id url altText width height }\n selectedOptions { name value }\n }\n }\n cost {\n totalAmount { amount currencyCode }\n }\n }\n }\n cost {\n subtotalAmount { amount currencyCode }\n totalAmount { amount currencyCode }\n }\n discountCodes { code }\n }\n`;\n\nexport const CART_QUERY = `#graphql\n ${CART_FRAGMENT}\n query CartQuery($cartId: ID!) {\n cart(id: $cartId) {\n ...CartFields\n }\n }\n`;\n\nexport const CART_CREATE_MUTATION = `#graphql\n ${CART_FRAGMENT}\n mutation CartCreate($input: CartInput!) {\n cartCreate(input: $input) {\n cart { ...CartFields }\n userErrors { message field code }\n }\n }\n`;\n\nexport const CART_LINES_ADD_MUTATION = `#graphql\n ${CART_FRAGMENT}\n mutation CartLinesAdd($cartId: ID!, $lines: [CartLineInput!]!) {\n cartLinesAdd(cartId: $cartId, lines: $lines) {\n cart { ...CartFields }\n userErrors { message field code }\n }\n }\n`;\n\nexport const CART_LINES_UPDATE_MUTATION = `#graphql\n ${CART_FRAGMENT}\n mutation CartLinesUpdate($cartId: ID!, $lines: [CartLineUpdateInput!]!) {\n cartLinesUpdate(cartId: $cartId, lines: $lines) {\n cart { ...CartFields }\n userErrors { message field code }\n }\n }\n`;\n\nexport const CART_LINES_REMOVE_MUTATION = `#graphql\n ${CART_FRAGMENT}\n mutation CartLinesRemove($cartId: ID!, $lineIds: [ID!]!) {\n cartLinesRemove(cartId: $cartId, lineIds: $lineIds) {\n cart { ...CartFields }\n userErrors { message field code }\n }\n }\n`;\n\nexport const CART_DISCOUNT_CODES_UPDATE_MUTATION = `#graphql\n ${CART_FRAGMENT}\n mutation CartDiscountCodesUpdate($cartId: ID!, $discountCodes: [String!]!) {\n cartDiscountCodesUpdate(cartId: $cartId, discountCodes: $discountCodes) {\n cart { ...CartFields }\n userErrors { message field code }\n }\n }\n`;\n\nexport const CART_BUYER_IDENTITY_UPDATE_MUTATION = `#graphql\n ${CART_FRAGMENT}\n mutation CartBuyerIdentityUpdate($cartId: ID!, $buyerIdentity: CartBuyerIdentityInput!) {\n cartBuyerIdentityUpdate(cartId: $cartId, buyerIdentity: $buyerIdentity) {\n cart { ...CartFields }\n userErrors { message field code }\n }\n }\n`;\n\nexport const CART_NOTE_UPDATE_MUTATION = `#graphql\n ${CART_FRAGMENT}\n mutation CartNoteUpdate($cartId: ID!, $note: String!) {\n cartNoteUpdate(cartId: $cartId, note: $note) {\n cart { ...CartFields }\n userErrors { message field code }\n }\n }\n`;\n","import type {StorefrontClient} from '../storefront/types.js';\nimport type {CartHandler, CartHandlerConfig} from './types.js';\nimport type {CartData} from '../types/cart.js';\nimport {CART_CREATE_MUTATION, CART_QUERY, CART_LINES_ADD_MUTATION, CART_LINES_UPDATE_MUTATION, CART_LINES_REMOVE_MUTATION, CART_DISCOUNT_CODES_UPDATE_MUTATION, CART_BUYER_IDENTITY_UPDATE_MUTATION, CART_NOTE_UPDATE_MUTATION} from './queries.js';\n\nexport function createCartHandler(\n storefront: StorefrontClient,\n config: CartHandlerConfig,\n): CartHandler {\n async function getOrCreateCartId(): Promise<string> {\n const existing = config.getCartId();\n if (existing) return existing;\n\n const data = await storefront.mutate<{cartCreate: {cart: CartData}}>(\n CART_CREATE_MUTATION,\n {variables: {input: {}}},\n );\n const cartId = data.cartCreate.cart.id;\n config.setCartId(cartId);\n return cartId;\n }\n\n return {\n async get() {\n const cartId = config.getCartId();\n if (!cartId) return emptyCart();\n\n const data = await storefront.query<{cart: CartData | null}>(\n CART_QUERY,\n {variables: {cartId}},\n );\n return data.cart ?? emptyCart();\n },\n\n async addLines(lines) {\n const cartId = await getOrCreateCartId();\n const data = await storefront.mutate<{cartLinesAdd: {cart: CartData}}>(\n CART_LINES_ADD_MUTATION,\n {variables: {cartId, lines}},\n );\n return data.cartLinesAdd.cart;\n },\n\n async updateLines(lines) {\n const cartId = config.getCartId();\n if (!cartId) return emptyCart();\n\n const data = await storefront.mutate<{cartLinesUpdate: {cart: CartData}}>(\n CART_LINES_UPDATE_MUTATION,\n {variables: {cartId, lines}},\n );\n return data.cartLinesUpdate.cart;\n },\n\n async removeLines(lineIds) {\n const cartId = config.getCartId();\n if (!cartId) return emptyCart();\n\n const data = await storefront.mutate<{cartLinesRemove: {cart: CartData}}>(\n CART_LINES_REMOVE_MUTATION,\n {variables: {cartId, lineIds}},\n );\n return data.cartLinesRemove.cart;\n },\n\n async updateDiscountCodes(codes) {\n const cartId = await getOrCreateCartId();\n const data = await storefront.mutate<{cartDiscountCodesUpdate: {cart: CartData}}>(\n CART_DISCOUNT_CODES_UPDATE_MUTATION,\n {variables: {cartId, discountCodes: codes}},\n );\n return data.cartDiscountCodesUpdate.cart;\n },\n\n async updateBuyerIdentity(buyerIdentity) {\n const cartId = await getOrCreateCartId();\n const data = await storefront.mutate<{cartBuyerIdentityUpdate: {cart: CartData}}>(\n CART_BUYER_IDENTITY_UPDATE_MUTATION,\n {variables: {cartId, buyerIdentity}},\n );\n return data.cartBuyerIdentityUpdate.cart;\n },\n\n async updateNote(note) {\n const cartId = await getOrCreateCartId();\n const data = await storefront.mutate<{cartNoteUpdate: {cart: CartData}}>(\n CART_NOTE_UPDATE_MUTATION,\n {variables: {cartId, note}},\n );\n return data.cartNoteUpdate.cart;\n },\n };\n}\n\nfunction emptyCart(): CartData {\n return {\n id: '',\n totalQuantity: 0,\n lines: {nodes: []},\n cost: {\n subtotalAmount: {amount: '0.00', currencyCode: 'USD'},\n totalAmount: {amount: '0.00', currencyCode: 'USD'},\n },\n discountCodes: [],\n };\n}\n","/**\n * Mock cart handler for development — in-memory cart.\n */\n\nimport type {CartHandler} from './types.js';\nimport type {CartData} from '../types/cart.js';\n\nexport function createMockCartHandler(): CartHandler {\n let cart: CartData = emptyCart();\n\n function recalculate() {\n let total = 0;\n for (const line of cart.lines.nodes) {\n total += parseFloat(line.cost.totalAmount.amount);\n }\n cart.cost.subtotalAmount.amount = total.toFixed(2);\n cart.cost.totalAmount.amount = total.toFixed(2);\n cart.totalQuantity = cart.lines.nodes.reduce((sum, l) => sum + l.quantity, 0);\n }\n\n return {\n async get() {\n return cart;\n },\n async addLines(lines) {\n for (const {merchandiseId, quantity} of lines) {\n const existing = cart.lines.nodes.find((l) => l.merchandise.id === merchandiseId);\n if (existing) {\n existing.quantity += quantity;\n existing.cost.totalAmount.amount = (parseFloat(existing.merchandise.price.amount) * existing.quantity).toFixed(2);\n } else {\n cart.lines.nodes.push({\n id: `line-${Date.now()}-${Math.random().toString(36).slice(2)}`,\n quantity,\n merchandise: {\n id: merchandiseId,\n title: 'Product Variant',\n product: {title: 'Product', handle: 'product'},\n price: {amount: '19.99', currencyCode: 'USD'},\n image: null,\n selectedOptions: [],\n },\n cost: {totalAmount: {amount: (19.99 * quantity).toFixed(2), currencyCode: 'USD'}},\n });\n }\n }\n recalculate();\n return cart;\n },\n async updateLines(lines) {\n for (const {id, quantity} of lines) {\n const line = cart.lines.nodes.find((l) => l.id === id);\n if (line) {\n line.quantity = quantity;\n line.cost.totalAmount.amount = (parseFloat(line.merchandise.price.amount) * quantity).toFixed(2);\n }\n }\n cart.lines.nodes = cart.lines.nodes.filter((l) => l.quantity > 0);\n recalculate();\n return cart;\n },\n async removeLines(lineIds) {\n cart.lines.nodes = cart.lines.nodes.filter((l) => !lineIds.includes(l.id));\n recalculate();\n return cart;\n },\n async updateDiscountCodes(codes) {\n cart.discountCodes = codes.map((code) => ({code}));\n return cart;\n },\n async updateBuyerIdentity() {\n return cart;\n },\n async updateNote() {\n return cart;\n },\n };\n}\n\nfunction emptyCart(): CartData {\n return {\n id: 'mock-cart',\n totalQuantity: 0,\n lines: {nodes: []},\n cost: {\n subtotalAmount: {amount: '0.00', currencyCode: 'USD'},\n totalAmount: {amount: '0.00', currencyCode: 'USD'},\n },\n discountCodes: [],\n };\n}\n"],"mappings":"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","names":["emptyCart"]}
@@ -0,0 +1,57 @@
1
+ // src/cache/strategies.ts
2
+ function CacheShort() {
3
+ return { mode: "public", maxAge: 1, staleWhileRevalidate: 9 };
4
+ }
5
+ function CacheLong() {
6
+ return { mode: "public", maxAge: 3600, staleWhileRevalidate: 82800 };
7
+ }
8
+ function CacheNone() {
9
+ return { mode: "no-store", maxAge: 0, staleWhileRevalidate: 0 };
10
+ }
11
+ function CacheCustom(options) {
12
+ return {
13
+ mode: "public",
14
+ maxAge: 0,
15
+ staleWhileRevalidate: 0,
16
+ ...options
17
+ };
18
+ }
19
+
20
+ // src/cache/in-memory.ts
21
+ var InMemoryCache = class {
22
+ #entries = /* @__PURE__ */ new Map();
23
+ async get(key) {
24
+ const entry = this.#entries.get(key);
25
+ if (!entry) return void 0;
26
+ const age = (Date.now() - entry.createdAt) / 1e3;
27
+ const totalTtl = entry.strategy.maxAge + entry.strategy.staleWhileRevalidate;
28
+ if (age > totalTtl) {
29
+ this.#entries.delete(key);
30
+ return void 0;
31
+ }
32
+ return entry.value;
33
+ }
34
+ async set(key, value, strategy) {
35
+ if (strategy.mode === "no-store") return;
36
+ this.#entries.set(key, {
37
+ value,
38
+ createdAt: Date.now(),
39
+ strategy
40
+ });
41
+ }
42
+ async delete(key) {
43
+ this.#entries.delete(key);
44
+ }
45
+ async clear() {
46
+ this.#entries.clear();
47
+ }
48
+ };
49
+
50
+ export {
51
+ CacheShort,
52
+ CacheLong,
53
+ CacheNone,
54
+ CacheCustom,
55
+ InMemoryCache
56
+ };
57
+ //# sourceMappingURL=chunk-JHZ4UBA2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cache/strategies.ts","../src/cache/in-memory.ts"],"sourcesContent":["import type {CacheStrategy} from './types.js';\n\n/**\n * Short-lived cache: 1 second fresh, 9 seconds stale-while-revalidate.\n * Use for frequently changing data (pricing, availability).\n */\nexport function CacheShort(): CacheStrategy {\n return {mode: 'public', maxAge: 1, staleWhileRevalidate: 9};\n}\n\n/**\n * Long-lived cache: 1 hour fresh, 23 hours stale-while-revalidate.\n * Use for stable data (product descriptions, collection titles).\n */\nexport function CacheLong(): CacheStrategy {\n return {mode: 'public', maxAge: 3600, staleWhileRevalidate: 82800};\n}\n\n/**\n * No caching. Use for customer-specific or sensitive data.\n */\nexport function CacheNone(): CacheStrategy {\n return {mode: 'no-store', maxAge: 0, staleWhileRevalidate: 0};\n}\n\n/**\n * Custom cache strategy with user-defined values.\n */\nexport function CacheCustom(options: Partial<CacheStrategy>): CacheStrategy {\n return {\n mode: 'public',\n maxAge: 0,\n staleWhileRevalidate: 0,\n ...options,\n };\n}\n","import type {CacheStrategy} from './types.js';\n\ninterface CacheEntry {\n value: unknown;\n createdAt: number;\n strategy: CacheStrategy;\n}\n\n/**\n * Simple in-memory cache for development.\n * In production, use the Web Cache API or a platform-specific cache.\n */\nexport class InMemoryCache {\n #entries = new Map<string, CacheEntry>();\n\n async get<T>(key: string): Promise<T | undefined> {\n const entry = this.#entries.get(key);\n if (!entry) return undefined;\n\n const age = (Date.now() - entry.createdAt) / 1000;\n const totalTtl = entry.strategy.maxAge + entry.strategy.staleWhileRevalidate;\n\n if (age > totalTtl) {\n this.#entries.delete(key);\n return undefined;\n }\n\n return entry.value as T;\n }\n\n async set(key: string, value: unknown, strategy: CacheStrategy): Promise<void> {\n if (strategy.mode === 'no-store') return;\n\n this.#entries.set(key, {\n value,\n createdAt: Date.now(),\n strategy,\n });\n }\n\n async delete(key: string): Promise<void> {\n this.#entries.delete(key);\n }\n\n async clear(): Promise<void> {\n this.#entries.clear();\n }\n}\n"],"mappings":";AAMO,SAAS,aAA4B;AAC1C,SAAO,EAAC,MAAM,UAAU,QAAQ,GAAG,sBAAsB,EAAC;AAC5D;AAMO,SAAS,YAA2B;AACzC,SAAO,EAAC,MAAM,UAAU,QAAQ,MAAM,sBAAsB,MAAK;AACnE;AAKO,SAAS,YAA2B;AACzC,SAAO,EAAC,MAAM,YAAY,QAAQ,GAAG,sBAAsB,EAAC;AAC9D;AAKO,SAAS,YAAY,SAAgD;AAC1E,SAAO;AAAA,IACL,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,sBAAsB;AAAA,IACtB,GAAG;AAAA,EACL;AACF;;;ACvBO,IAAM,gBAAN,MAAoB;AAAA,EACzB,WAAW,oBAAI,IAAwB;AAAA,EAEvC,MAAM,IAAO,KAAqC;AAChD,UAAM,QAAQ,KAAK,SAAS,IAAI,GAAG;AACnC,QAAI,CAAC,MAAO,QAAO;AAEnB,UAAM,OAAO,KAAK,IAAI,IAAI,MAAM,aAAa;AAC7C,UAAM,WAAW,MAAM,SAAS,SAAS,MAAM,SAAS;AAExD,QAAI,MAAM,UAAU;AAClB,WAAK,SAAS,OAAO,GAAG;AACxB,aAAO;AAAA,IACT;AAEA,WAAO,MAAM;AAAA,EACf;AAAA,EAEA,MAAM,IAAI,KAAa,OAAgB,UAAwC;AAC7E,QAAI,SAAS,SAAS,WAAY;AAElC,SAAK,SAAS,IAAI,KAAK;AAAA,MACrB;AAAA,MACA,WAAW,KAAK,IAAI;AAAA,MACpB;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,OAAO,KAA4B;AACvC,SAAK,SAAS,OAAO,GAAG;AAAA,EAC1B;AAAA,EAEA,MAAM,QAAuB;AAC3B,SAAK,SAAS,MAAM;AAAA,EACtB;AACF;","names":[]}
@@ -0,0 +1,65 @@
1
+ // src/analytics/provider.tsx
2
+ import { createContext, useContext, useMemo } from "react";
3
+
4
+ // src/analytics/hooks.ts
5
+ import { useCallback, useRef } from "react";
6
+ function useAnalytics() {
7
+ const subscribersRef = useRef([]);
8
+ const publish = useCallback((event, data) => {
9
+ const payload = {
10
+ event,
11
+ data,
12
+ timestamp: Date.now()
13
+ };
14
+ for (const subscriber of subscribersRef.current) {
15
+ try {
16
+ subscriber(payload);
17
+ } catch (e) {
18
+ console.error("[Analytics] Subscriber error:", e);
19
+ }
20
+ }
21
+ }, []);
22
+ const subscribe = useCallback((fn) => {
23
+ subscribersRef.current.push(fn);
24
+ return () => {
25
+ subscribersRef.current = subscribersRef.current.filter((s) => s !== fn);
26
+ };
27
+ }, []);
28
+ return { publish, subscribe };
29
+ }
30
+
31
+ // src/analytics/provider.tsx
32
+ import { jsx } from "react/jsx-runtime";
33
+ var AnalyticsContext = createContext(null);
34
+ function AnalyticsProvider({ children }) {
35
+ const analytics = useAnalytics();
36
+ const value = useMemo(() => analytics, [analytics]);
37
+ return /* @__PURE__ */ jsx(AnalyticsContext.Provider, { value, children });
38
+ }
39
+ function useAnalyticsContext() {
40
+ const ctx = useContext(AnalyticsContext);
41
+ if (!ctx) {
42
+ throw new Error("useAnalyticsContext must be used within <AnalyticsProvider>");
43
+ }
44
+ return ctx;
45
+ }
46
+
47
+ // src/analytics/events.ts
48
+ var AnalyticsEvent = {
49
+ PAGE_VIEW: "page_view",
50
+ PRODUCT_VIEW: "product_view",
51
+ COLLECTION_VIEW: "collection_view",
52
+ SEARCH_VIEW: "search_view",
53
+ CART_VIEW: "cart_view",
54
+ CART_UPDATED: "cart_updated",
55
+ ADD_TO_CART: "add_to_cart",
56
+ REMOVE_FROM_CART: "remove_from_cart"
57
+ };
58
+
59
+ export {
60
+ useAnalytics,
61
+ AnalyticsProvider,
62
+ useAnalyticsContext,
63
+ AnalyticsEvent
64
+ };
65
+ //# sourceMappingURL=chunk-LVUU4ZBR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/analytics/provider.tsx","../src/analytics/hooks.ts","../src/analytics/events.ts"],"sourcesContent":["import {createContext, useContext, useMemo, type ReactNode} from 'react';\nimport {useAnalytics, type AnalyticsSubscriber} from './hooks.js';\nimport type {AnalyticsEventName} from './events.js';\n\ninterface AnalyticsContextValue {\n publish: (event: AnalyticsEventName, data?: Record<string, unknown>) => void;\n subscribe: (fn: AnalyticsSubscriber) => () => void;\n}\n\nconst AnalyticsContext = createContext<AnalyticsContextValue | null>(null);\n\nexport function AnalyticsProvider({children}: {children: ReactNode}) {\n const analytics = useAnalytics();\n const value = useMemo(() => analytics, [analytics]);\n return <AnalyticsContext.Provider value={value}>{children}</AnalyticsContext.Provider>;\n}\n\nexport function useAnalyticsContext(): AnalyticsContextValue {\n const ctx = useContext(AnalyticsContext);\n if (!ctx) {\n throw new Error('useAnalyticsContext must be used within <AnalyticsProvider>');\n }\n return ctx;\n}\n","import {useCallback, useRef} from 'react';\nimport type {AnalyticsPayload, AnalyticsEventName} from './events.js';\n\nexport type AnalyticsSubscriber = (payload: AnalyticsPayload) => void;\n\n/**\n * Hook for publishing analytics events from components.\n */\nexport function useAnalytics() {\n const subscribersRef = useRef<AnalyticsSubscriber[]>([]);\n\n const publish = useCallback((event: AnalyticsEventName, data?: Record<string, unknown>) => {\n const payload: AnalyticsPayload = {\n event,\n data,\n timestamp: Date.now(),\n };\n\n for (const subscriber of subscribersRef.current) {\n try {\n subscriber(payload);\n } catch (e) {\n console.error('[Analytics] Subscriber error:', e);\n }\n }\n }, []);\n\n const subscribe = useCallback((fn: AnalyticsSubscriber) => {\n subscribersRef.current.push(fn);\n return () => {\n subscribersRef.current = subscribersRef.current.filter((s) => s !== fn);\n };\n }, []);\n\n return {publish, subscribe};\n}\n","export const AnalyticsEvent = {\n PAGE_VIEW: 'page_view',\n PRODUCT_VIEW: 'product_view',\n COLLECTION_VIEW: 'collection_view',\n SEARCH_VIEW: 'search_view',\n CART_VIEW: 'cart_view',\n CART_UPDATED: 'cart_updated',\n ADD_TO_CART: 'add_to_cart',\n REMOVE_FROM_CART: 'remove_from_cart',\n} as const;\n\nexport type AnalyticsEventName = (typeof AnalyticsEvent)[keyof typeof AnalyticsEvent];\n\nexport interface AnalyticsPayload {\n event: AnalyticsEventName;\n data?: Record<string, unknown>;\n timestamp?: number;\n}\n"],"mappings":";AAAA,SAAQ,eAAe,YAAY,eAA8B;;;ACAjE,SAAQ,aAAa,cAAa;AAQ3B,SAAS,eAAe;AAC7B,QAAM,iBAAiB,OAA8B,CAAC,CAAC;AAEvD,QAAM,UAAU,YAAY,CAAC,OAA2B,SAAmC;AACzF,UAAM,UAA4B;AAAA,MAChC;AAAA,MACA;AAAA,MACA,WAAW,KAAK,IAAI;AAAA,IACtB;AAEA,eAAW,cAAc,eAAe,SAAS;AAC/C,UAAI;AACF,mBAAW,OAAO;AAAA,MACpB,SAAS,GAAG;AACV,gBAAQ,MAAM,iCAAiC,CAAC;AAAA,MAClD;AAAA,IACF;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,YAAY,YAAY,CAAC,OAA4B;AACzD,mBAAe,QAAQ,KAAK,EAAE;AAC9B,WAAO,MAAM;AACX,qBAAe,UAAU,eAAe,QAAQ,OAAO,CAAC,MAAM,MAAM,EAAE;AAAA,IACxE;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,SAAO,EAAC,SAAS,UAAS;AAC5B;;;ADrBS;AALT,IAAM,mBAAmB,cAA4C,IAAI;AAElE,SAAS,kBAAkB,EAAC,SAAQ,GAA0B;AACnE,QAAM,YAAY,aAAa;AAC/B,QAAM,QAAQ,QAAQ,MAAM,WAAW,CAAC,SAAS,CAAC;AAClD,SAAO,oBAAC,iBAAiB,UAAjB,EAA0B,OAAe,UAAS;AAC5D;AAEO,SAAS,sBAA6C;AAC3D,QAAM,MAAM,WAAW,gBAAgB;AACvC,MAAI,CAAC,KAAK;AACR,UAAM,IAAI,MAAM,6DAA6D;AAAA,EAC/E;AACA,SAAO;AACT;;;AEvBO,IAAM,iBAAiB;AAAA,EAC5B,WAAW;AAAA,EACX,cAAc;AAAA,EACd,iBAAiB;AAAA,EACjB,aAAa;AAAA,EACb,WAAW;AAAA,EACX,cAAc;AAAA,EACd,aAAa;AAAA,EACb,kBAAkB;AACpB;","names":[]}
@@ -0,0 +1,94 @@
1
+ // src/seo/meta.ts
2
+ function getSeoMeta(...configs) {
3
+ const merged = {};
4
+ for (const config of configs) {
5
+ if (config) Object.assign(merged, config);
6
+ }
7
+ const meta = [];
8
+ if (merged.title) {
9
+ meta.push({ title: merged.title });
10
+ meta.push({ property: "og:title", content: merged.title });
11
+ meta.push({ name: "twitter:title", content: merged.title });
12
+ }
13
+ if (merged.description) {
14
+ meta.push({ name: "description", content: merged.description });
15
+ meta.push({ property: "og:description", content: merged.description });
16
+ meta.push({ name: "twitter:description", content: merged.description });
17
+ }
18
+ if (merged.url) {
19
+ meta.push({ rel: "canonical", href: merged.url });
20
+ meta.push({ property: "og:url", content: merged.url });
21
+ }
22
+ if (merged.type) {
23
+ meta.push({ property: "og:type", content: merged.type });
24
+ }
25
+ if (merged.image) {
26
+ meta.push({ property: "og:image", content: merged.image.url });
27
+ meta.push({ name: "twitter:card", content: "summary_large_image" });
28
+ meta.push({ name: "twitter:image", content: merged.image.url });
29
+ if (merged.image.width) {
30
+ meta.push({ property: "og:image:width", content: String(merged.image.width) });
31
+ }
32
+ if (merged.image.height) {
33
+ meta.push({ property: "og:image:height", content: String(merged.image.height) });
34
+ }
35
+ if (merged.image.altText) {
36
+ meta.push({ property: "og:image:alt", content: merged.image.altText });
37
+ }
38
+ }
39
+ if (merged.jsonLd) {
40
+ meta.push({ "script:ld+json": merged.jsonLd });
41
+ }
42
+ return meta;
43
+ }
44
+
45
+ // src/seo/sitemap.ts
46
+ function generateSitemap(entries) {
47
+ const urls = entries.map((entry) => {
48
+ let xml = ` <url>
49
+ <loc>${escapeXml(entry.url)}</loc>`;
50
+ if (entry.lastmod) xml += `
51
+ <lastmod>${entry.lastmod}</lastmod>`;
52
+ if (entry.changefreq) xml += `
53
+ <changefreq>${entry.changefreq}</changefreq>`;
54
+ if (entry.priority != null) xml += `
55
+ <priority>${entry.priority}</priority>`;
56
+ xml += "\n </url>";
57
+ return xml;
58
+ });
59
+ return [
60
+ '<?xml version="1.0" encoding="UTF-8"?>',
61
+ '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">',
62
+ ...urls,
63
+ "</urlset>"
64
+ ].join("\n");
65
+ }
66
+ function escapeXml(str) {
67
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
68
+ }
69
+
70
+ // src/seo/robots.ts
71
+ function generateRobots(config) {
72
+ const lines = [];
73
+ for (const rule of config.rules) {
74
+ lines.push(`User-agent: ${rule.userAgent}`);
75
+ if (rule.allow) {
76
+ for (const path of rule.allow) lines.push(`Allow: ${path}`);
77
+ }
78
+ if (rule.disallow) {
79
+ for (const path of rule.disallow) lines.push(`Disallow: ${path}`);
80
+ }
81
+ lines.push("");
82
+ }
83
+ if (config.sitemap) {
84
+ lines.push(`Sitemap: ${config.sitemap}`);
85
+ }
86
+ return lines.join("\n");
87
+ }
88
+
89
+ export {
90
+ getSeoMeta,
91
+ generateSitemap,
92
+ generateRobots
93
+ };
94
+ //# sourceMappingURL=chunk-RKT3VKU2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/seo/meta.ts","../src/seo/sitemap.ts","../src/seo/robots.ts"],"sourcesContent":["/**\n * getSeoMeta — generates React Router meta descriptors from SEO config.\n * Mirrors Hydrogen's getSeoMeta utility.\n */\n\nexport interface SeoConfig {\n title?: string;\n description?: string;\n url?: string;\n image?: {url: string; width?: number; height?: number; altText?: string};\n type?: 'website' | 'product' | 'article';\n jsonLd?: Record<string, unknown> | Record<string, unknown>[];\n}\n\nexport interface MetaDescriptor {\n title?: string;\n name?: string;\n property?: string;\n content?: string;\n rel?: string;\n href?: string;\n 'script:ld+json'?: Record<string, unknown> | Record<string, unknown>[];\n}\n\n/**\n * Generates an array of meta descriptors compatible with React Router's\n * `meta` export. Supports title, description, Open Graph, Twitter, and JSON-LD.\n *\n * Merge multiple configs by spreading: `getSeoMeta(parentSeo, childSeo)`\n */\nexport function getSeoMeta(...configs: (SeoConfig | undefined)[]): MetaDescriptor[] {\n const merged: SeoConfig = {};\n for (const config of configs) {\n if (config) Object.assign(merged, config);\n }\n\n const meta: MetaDescriptor[] = [];\n\n if (merged.title) {\n meta.push({title: merged.title});\n meta.push({property: 'og:title', content: merged.title});\n meta.push({name: 'twitter:title', content: merged.title});\n }\n\n if (merged.description) {\n meta.push({name: 'description', content: merged.description});\n meta.push({property: 'og:description', content: merged.description});\n meta.push({name: 'twitter:description', content: merged.description});\n }\n\n if (merged.url) {\n meta.push({rel: 'canonical', href: merged.url});\n meta.push({property: 'og:url', content: merged.url});\n }\n\n if (merged.type) {\n meta.push({property: 'og:type', content: merged.type});\n }\n\n if (merged.image) {\n meta.push({property: 'og:image', content: merged.image.url});\n meta.push({name: 'twitter:card', content: 'summary_large_image'});\n meta.push({name: 'twitter:image', content: merged.image.url});\n if (merged.image.width) {\n meta.push({property: 'og:image:width', content: String(merged.image.width)});\n }\n if (merged.image.height) {\n meta.push({property: 'og:image:height', content: String(merged.image.height)});\n }\n if (merged.image.altText) {\n meta.push({property: 'og:image:alt', content: merged.image.altText});\n }\n }\n\n if (merged.jsonLd) {\n meta.push({'script:ld+json': merged.jsonLd});\n }\n\n return meta;\n}\n","export interface SitemapEntry {\n url: string;\n lastmod?: string;\n changefreq?: 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never';\n priority?: number;\n}\n\n/**\n * Generates an XML sitemap string from an array of entries.\n */\nexport function generateSitemap(entries: SitemapEntry[]): string {\n const urls = entries.map((entry) => {\n let xml = ` <url>\\n <loc>${escapeXml(entry.url)}</loc>`;\n if (entry.lastmod) xml += `\\n <lastmod>${entry.lastmod}</lastmod>`;\n if (entry.changefreq) xml += `\\n <changefreq>${entry.changefreq}</changefreq>`;\n if (entry.priority != null) xml += `\\n <priority>${entry.priority}</priority>`;\n xml += '\\n </url>';\n return xml;\n });\n\n return [\n '<?xml version=\"1.0\" encoding=\"UTF-8\"?>',\n '<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">',\n ...urls,\n '</urlset>',\n ].join('\\n');\n}\n\nfunction escapeXml(str: string): string {\n return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n}\n","export interface RobotsConfig {\n rules: Array<{\n userAgent: string;\n allow?: string[];\n disallow?: string[];\n }>;\n sitemap?: string;\n}\n\n/**\n * Generates a robots.txt string from configuration.\n */\nexport function generateRobots(config: RobotsConfig): string {\n const lines: string[] = [];\n\n for (const rule of config.rules) {\n lines.push(`User-agent: ${rule.userAgent}`);\n if (rule.allow) {\n for (const path of rule.allow) lines.push(`Allow: ${path}`);\n }\n if (rule.disallow) {\n for (const path of rule.disallow) lines.push(`Disallow: ${path}`);\n }\n lines.push('');\n }\n\n if (config.sitemap) {\n lines.push(`Sitemap: ${config.sitemap}`);\n }\n\n return lines.join('\\n');\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,45 @@
1
+ // src/csp/policy.ts
2
+ import { randomBytes } from "crypto";
3
+ function createContentSecurityPolicy(options) {
4
+ const nonce = options?.nonce ?? randomBytes(16).toString("base64");
5
+ const custom = options?.directives ?? {};
6
+ const directives = {
7
+ defaultSrc: custom.defaultSrc ?? ["'self'"],
8
+ scriptSrc: custom.scriptSrc ?? ["'self'", `'nonce-${nonce}'`],
9
+ styleSrc: custom.styleSrc ?? ["'self'", "'unsafe-inline'"],
10
+ imgSrc: custom.imgSrc ?? ["'self'", "data:", "https:"],
11
+ connectSrc: custom.connectSrc ?? ["'self'", "https:"],
12
+ fontSrc: custom.fontSrc ?? ["'self'"],
13
+ objectSrc: custom.objectSrc ?? ["'none'"],
14
+ mediaSrc: custom.mediaSrc ?? ["'self'"],
15
+ frameSrc: custom.frameSrc ?? ["'none'"],
16
+ baseUri: custom.baseUri ?? ["'self'"]
17
+ };
18
+ const nonceStr = `'nonce-${nonce}'`;
19
+ if (!directives.scriptSrc.includes(nonceStr)) {
20
+ directives.scriptSrc.push(nonceStr);
21
+ }
22
+ const header = Object.entries(directives).map(([key, values]) => {
23
+ const directive = key.replace(/[A-Z]/g, (c) => "-" + c.toLowerCase());
24
+ return `${directive} ${values.join(" ")}`;
25
+ }).join("; ");
26
+ return { nonce, header };
27
+ }
28
+
29
+ // src/csp/nonce.tsx
30
+ import { createContext, useContext } from "react";
31
+ import { jsx } from "react/jsx-runtime";
32
+ var NonceContext = createContext("");
33
+ function NonceProvider({ value, children }) {
34
+ return /* @__PURE__ */ jsx(NonceContext.Provider, { value, children });
35
+ }
36
+ function useNonce() {
37
+ return useContext(NonceContext);
38
+ }
39
+
40
+ export {
41
+ createContentSecurityPolicy,
42
+ NonceProvider,
43
+ useNonce
44
+ };
45
+ //# sourceMappingURL=chunk-RTJ73SCX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/csp/policy.ts","../src/csp/nonce.tsx"],"sourcesContent":["import {randomBytes} from 'node:crypto';\n\nexport interface CspDirectives {\n defaultSrc?: string[];\n scriptSrc?: string[];\n styleSrc?: string[];\n imgSrc?: string[];\n connectSrc?: string[];\n fontSrc?: string[];\n objectSrc?: string[];\n mediaSrc?: string[];\n frameSrc?: string[];\n childSrc?: string[];\n workerSrc?: string[];\n frameAncestors?: string[];\n formAction?: string[];\n baseUri?: string[];\n}\n\n/**\n * Creates a Content Security Policy with a unique nonce per request.\n * Mirrors Hydrogen's createContentSecurityPolicy.\n */\nexport function createContentSecurityPolicy(options?: {\n nonce?: string;\n directives?: CspDirectives;\n}) {\n const nonce = options?.nonce ?? randomBytes(16).toString('base64');\n const custom = options?.directives ?? {};\n\n const directives: Record<string, string[]> = {\n defaultSrc: custom.defaultSrc ?? [\"'self'\"],\n scriptSrc: custom.scriptSrc ?? [\"'self'\", `'nonce-${nonce}'`],\n styleSrc: custom.styleSrc ?? [\"'self'\", \"'unsafe-inline'\"],\n imgSrc: custom.imgSrc ?? [\"'self'\", 'data:', 'https:'],\n connectSrc: custom.connectSrc ?? [\"'self'\", 'https:'],\n fontSrc: custom.fontSrc ?? [\"'self'\"],\n objectSrc: custom.objectSrc ?? [\"'none'\"],\n mediaSrc: custom.mediaSrc ?? [\"'self'\"],\n frameSrc: custom.frameSrc ?? [\"'none'\"],\n baseUri: custom.baseUri ?? [\"'self'\"],\n };\n\n // Ensure nonce is in scriptSrc\n const nonceStr = `'nonce-${nonce}'`;\n if (!directives.scriptSrc.includes(nonceStr)) {\n directives.scriptSrc.push(nonceStr);\n }\n\n const header = Object.entries(directives)\n .map(([key, values]) => {\n const directive = key.replace(/[A-Z]/g, (c) => '-' + c.toLowerCase());\n return `${directive} ${values.join(' ')}`;\n })\n .join('; ');\n\n return {nonce, header};\n}\n","import {createContext, useContext, type ReactNode} from 'react';\n\nconst NonceContext = createContext<string>('');\n\nexport function NonceProvider({value, children}: {value: string; children: ReactNode}) {\n return <NonceContext.Provider value={value}>{children}</NonceContext.Provider>;\n}\n\nexport function useNonce(): string {\n return useContext(NonceContext);\n}\n"],"mappings":";AAAA,SAAQ,mBAAkB;AAuBnB,SAAS,4BAA4B,SAGzC;AACD,QAAM,QAAQ,SAAS,SAAS,YAAY,EAAE,EAAE,SAAS,QAAQ;AACjE,QAAM,SAAS,SAAS,cAAc,CAAC;AAEvC,QAAM,aAAuC;AAAA,IAC3C,YAAY,OAAO,cAAc,CAAC,QAAQ;AAAA,IAC1C,WAAW,OAAO,aAAa,CAAC,UAAU,UAAU,KAAK,GAAG;AAAA,IAC5D,UAAU,OAAO,YAAY,CAAC,UAAU,iBAAiB;AAAA,IACzD,QAAQ,OAAO,UAAU,CAAC,UAAU,SAAS,QAAQ;AAAA,IACrD,YAAY,OAAO,cAAc,CAAC,UAAU,QAAQ;AAAA,IACpD,SAAS,OAAO,WAAW,CAAC,QAAQ;AAAA,IACpC,WAAW,OAAO,aAAa,CAAC,QAAQ;AAAA,IACxC,UAAU,OAAO,YAAY,CAAC,QAAQ;AAAA,IACtC,UAAU,OAAO,YAAY,CAAC,QAAQ;AAAA,IACtC,SAAS,OAAO,WAAW,CAAC,QAAQ;AAAA,EACtC;AAGA,QAAM,WAAW,UAAU,KAAK;AAChC,MAAI,CAAC,WAAW,UAAU,SAAS,QAAQ,GAAG;AAC5C,eAAW,UAAU,KAAK,QAAQ;AAAA,EACpC;AAEA,QAAM,SAAS,OAAO,QAAQ,UAAU,EACrC,IAAI,CAAC,CAAC,KAAK,MAAM,MAAM;AACtB,UAAM,YAAY,IAAI,QAAQ,UAAU,CAAC,MAAM,MAAM,EAAE,YAAY,CAAC;AACpE,WAAO,GAAG,SAAS,IAAI,OAAO,KAAK,GAAG,CAAC;AAAA,EACzC,CAAC,EACA,KAAK,IAAI;AAEZ,SAAO,EAAC,OAAO,OAAM;AACvB;;;ACzDA,SAAQ,eAAe,kBAAiC;AAK/C;AAHT,IAAM,eAAe,cAAsB,EAAE;AAEtC,SAAS,cAAc,EAAC,OAAO,SAAQ,GAAyC;AACrF,SAAO,oBAAC,aAAa,UAAb,EAAsB,OAAe,UAAS;AACxD;AAEO,SAAS,WAAmB;AACjC,SAAO,WAAW,YAAY;AAChC;","names":[]}
@@ -0,0 +1,47 @@
1
+ // src/session/session.ts
2
+ import { createCookieSessionStorage } from "react-router";
3
+ var AppSession = class _AppSession {
4
+ isPending = false;
5
+ #sessionStorage;
6
+ #session;
7
+ constructor(sessionStorage, session) {
8
+ this.#sessionStorage = sessionStorage;
9
+ this.#session = session;
10
+ }
11
+ static async init(request, secrets) {
12
+ const storage = createCookieSessionStorage({
13
+ cookie: { name: "session", httpOnly: true, path: "/", sameSite: "lax", secrets }
14
+ });
15
+ const session = await storage.getSession(request.headers.get("Cookie"));
16
+ return new _AppSession(storage, session);
17
+ }
18
+ get has() {
19
+ return this.#session.has;
20
+ }
21
+ get get() {
22
+ return this.#session.get;
23
+ }
24
+ get flash() {
25
+ return this.#session.flash;
26
+ }
27
+ set(key, value) {
28
+ this.#session.set(key, value);
29
+ this.isPending = true;
30
+ }
31
+ unset(key) {
32
+ this.#session.unset(key);
33
+ this.isPending = true;
34
+ }
35
+ async destroy() {
36
+ return this.#sessionStorage.destroySession(this.#session);
37
+ }
38
+ async commit() {
39
+ this.isPending = false;
40
+ return this.#sessionStorage.commitSession(this.#session);
41
+ }
42
+ };
43
+
44
+ export {
45
+ AppSession
46
+ };
47
+ //# sourceMappingURL=chunk-WJCN2EO3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/session/session.ts"],"sourcesContent":["import {createCookieSessionStorage, type SessionStorage, type Session} from 'react-router';\n\nexport class AppSession {\n public isPending = false;\n\n #sessionStorage: SessionStorage;\n #session: Session;\n\n constructor(sessionStorage: SessionStorage, session: Session) {\n this.#sessionStorage = sessionStorage;\n this.#session = session;\n }\n\n static async init(request: Request, secrets: string[]): Promise<AppSession> {\n const storage = createCookieSessionStorage({\n cookie: {name: 'session', httpOnly: true, path: '/', sameSite: 'lax', secrets},\n });\n const session = await storage.getSession(request.headers.get('Cookie'));\n return new AppSession(storage, session);\n }\n\n get has() { return this.#session.has; }\n get get() { return this.#session.get; }\n get flash() { return this.#session.flash; }\n\n set(key: string, value: unknown) {\n this.#session.set(key, value);\n this.isPending = true;\n }\n\n unset(key: string) {\n this.#session.unset(key);\n this.isPending = true;\n }\n\n async destroy() { return this.#sessionStorage.destroySession(this.#session); }\n\n async commit() {\n this.isPending = false;\n return this.#sessionStorage.commitSession(this.#session);\n }\n}\n"],"mappings":";AAAA,SAAQ,kCAAoE;AAErE,IAAM,aAAN,MAAM,YAAW;AAAA,EACf,YAAY;AAAA,EAEnB;AAAA,EACA;AAAA,EAEA,YAAY,gBAAgC,SAAkB;AAC5D,SAAK,kBAAkB;AACvB,SAAK,WAAW;AAAA,EAClB;AAAA,EAEA,aAAa,KAAK,SAAkB,SAAwC;AAC1E,UAAM,UAAU,2BAA2B;AAAA,MACzC,QAAQ,EAAC,MAAM,WAAW,UAAU,MAAM,MAAM,KAAK,UAAU,OAAO,QAAO;AAAA,IAC/E,CAAC;AACD,UAAM,UAAU,MAAM,QAAQ,WAAW,QAAQ,QAAQ,IAAI,QAAQ,CAAC;AACtE,WAAO,IAAI,YAAW,SAAS,OAAO;AAAA,EACxC;AAAA,EAEA,IAAI,MAAM;AAAE,WAAO,KAAK,SAAS;AAAA,EAAK;AAAA,EACtC,IAAI,MAAM;AAAE,WAAO,KAAK,SAAS;AAAA,EAAK;AAAA,EACtC,IAAI,QAAQ;AAAE,WAAO,KAAK,SAAS;AAAA,EAAO;AAAA,EAE1C,IAAI,KAAa,OAAgB;AAC/B,SAAK,SAAS,IAAI,KAAK,KAAK;AAC5B,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,MAAM,KAAa;AACjB,SAAK,SAAS,MAAM,GAAG;AACvB,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,MAAM,UAAU;AAAE,WAAO,KAAK,gBAAgB,eAAe,KAAK,QAAQ;AAAA,EAAG;AAAA,EAE7E,MAAM,SAAS;AACb,SAAK,YAAY;AACjB,WAAO,KAAK,gBAAgB,cAAc,KAAK,QAAQ;AAAA,EACzD;AACF;","names":[]}