@teleporthq/teleport-project-generator-next 0.43.52 → 0.43.53
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/__tests__/ecommerce-session-cookie-resolver.test.ts +58 -0
- package/__tests__/motion-stagger-repeater.test.ts +53 -0
- package/dist/cjs/ecommerce/cart-api-routes-generator.d.ts.map +1 -1
- package/dist/cjs/ecommerce/cart-api-routes-generator.js +2 -1
- package/dist/cjs/ecommerce/cart-api-routes-generator.js.map +1 -1
- package/dist/cjs/ecommerce/ecommerce-api-routes-generator.d.ts.map +1 -1
- package/dist/cjs/ecommerce/ecommerce-api-routes-generator.js +3 -2
- package/dist/cjs/ecommerce/ecommerce-api-routes-generator.js.map +1 -1
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/cjs/widgets/motion-component.d.ts +7 -5
- package/dist/cjs/widgets/motion-component.d.ts.map +1 -1
- package/dist/cjs/widgets/motion-component.js +8 -6
- package/dist/cjs/widgets/motion-component.js.map +1 -1
- package/dist/esm/ecommerce/cart-api-routes-generator.d.ts.map +1 -1
- package/dist/esm/ecommerce/cart-api-routes-generator.js +2 -1
- package/dist/esm/ecommerce/cart-api-routes-generator.js.map +1 -1
- package/dist/esm/ecommerce/ecommerce-api-routes-generator.d.ts.map +1 -1
- package/dist/esm/ecommerce/ecommerce-api-routes-generator.js +3 -2
- package/dist/esm/ecommerce/ecommerce-api-routes-generator.js.map +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/widgets/motion-component.d.ts +7 -5
- package/dist/esm/widgets/motion-component.d.ts.map +1 -1
- package/dist/esm/widgets/motion-component.js +8 -6
- package/dist/esm/widgets/motion-component.js.map +1 -1
- package/package.json +18 -18
- package/src/ecommerce/cart-api-routes-generator.ts +4 -4
- package/src/ecommerce/ecommerce-api-routes-generator.ts +3 -2
- package/src/widgets/motion-component.ts +42 -5
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { generateCartApiRoute } from '../src/ecommerce/cart-api-routes-generator'
|
|
2
|
+
import { generateCheckoutApiRoute } from '../src/ecommerce/ecommerce-api-routes-generator'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The cart and checkout routes read the session the same way the workflow
|
|
6
|
+
* routes do — and had the same defect.
|
|
7
|
+
*
|
|
8
|
+
* ## ⛔ THE REPORTED DEFECT
|
|
9
|
+
*
|
|
10
|
+
* next-auth v4 derives the session-cookie NAME from `process.env.NEXTAUTH_URL`,
|
|
11
|
+
* which a generated project ships as a localhost default and repairs from the
|
|
12
|
+
* request only inside `/api/auth/[...nextauth]` — process-locally. On a
|
|
13
|
+
* published https domain every other lambda therefore looked for the non-secure
|
|
14
|
+
* cookie and reported a signed-in caller as anonymous. Measured on a live
|
|
15
|
+
* deployment: 50 concurrent POSTs to one workflow route returned 33 × 401.
|
|
16
|
+
*
|
|
17
|
+
* Here the same miss is silent and worse than a 401: a logged-in buyer is
|
|
18
|
+
* treated as a GUEST, so their cart is keyed by an anonymous session id instead
|
|
19
|
+
* of their user id, and `mark-ordered` retires the wrong cart.
|
|
20
|
+
*
|
|
21
|
+
* Both routes now resolve the cookie from the REQUEST — see
|
|
22
|
+
* `session-cookie-resolver.ts` in @teleporthq/teleport-plugin-next-workflows.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
const PG_CFG = { connectionString: 'env:DATABASE_URL' }
|
|
26
|
+
|
|
27
|
+
// tslint:disable-next-line:no-var-requires
|
|
28
|
+
const { parse } = require('@babel/parser')
|
|
29
|
+
const parses = (code: string) =>
|
|
30
|
+
parse(code, { sourceType: 'unambiguous', allowReturnOutsideFunction: true })
|
|
31
|
+
|
|
32
|
+
describe('the ecommerce routes resolve the session cookie from the request', () => {
|
|
33
|
+
const cart = generateCartApiRoute('postgresql', PG_CFG) as string
|
|
34
|
+
const checkout = generateCheckoutApiRoute({ guestCheckout: true } as never, 'postgresql', PG_CFG)
|
|
35
|
+
|
|
36
|
+
it('the cart route never asks getToken for the env-derived default', () => {
|
|
37
|
+
expect(cart).toContain('__tqResolveSessionToken')
|
|
38
|
+
expect(cart).toContain('__tqSessionToken(req)')
|
|
39
|
+
expect(cart).not.toMatch(/getToken\(\{\s*req/)
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it('the checkout route never asks getToken for the env-derived default', () => {
|
|
43
|
+
expect(checkout).toContain('__tqResolveSessionToken')
|
|
44
|
+
expect(checkout).toContain('__tqSessionToken(req)')
|
|
45
|
+
expect(checkout).not.toMatch(/getToken\(\{\s*req/)
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it('both still parse — the snippet is inlined into an ESM handler here', () => {
|
|
49
|
+
expect(() => parses(cart)).not.toThrow()
|
|
50
|
+
expect(() => parses(checkout)).not.toThrow()
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('a checkout route with no database emits no resolver it cannot use', () => {
|
|
54
|
+
const noDb = generateCheckoutApiRoute({ guestCheckout: true } as never, null, null)
|
|
55
|
+
expect(noDb).not.toContain('__tqResolveSessionToken')
|
|
56
|
+
expect(() => parses(noDb)).not.toThrow()
|
|
57
|
+
})
|
|
58
|
+
})
|
|
@@ -90,6 +90,59 @@ describe('TqMotion stagger resolution (mapStaggerTargets)', () => {
|
|
|
90
90
|
const lone = element('img', { src: 'x' })
|
|
91
91
|
expect(mapStaggerTargets(lone, wrap, 0)).toBeNull()
|
|
92
92
|
})
|
|
93
|
+
|
|
94
|
+
// ⛔ THE REPORTED DEFECT (run 75860d32, "Start by Concern" on Routine Guide).
|
|
95
|
+
// The lone-child guard above only fires for a container whose ONLY child is the
|
|
96
|
+
// repeater. That grid held the mapper AND one static "text pivot" card, so the
|
|
97
|
+
// array was length 2, both branches were wrapped, and the mapper's four cards
|
|
98
|
+
// shipped stacked inside ONE cell of a repeat(3, 1fr) grid — one column of tall
|
|
99
|
+
// cards, the pivot beside it, and the third column empty down the whole band.
|
|
100
|
+
it('returns null when a <Repeater> sits BESIDE a static sibling in the same container', () => {
|
|
101
|
+
const grid = element('div', {
|
|
102
|
+
className: 'collage-grid',
|
|
103
|
+
children: [
|
|
104
|
+
element('Repeater', { items: [], renderItem: (): null => null }),
|
|
105
|
+
element('div', { className: 'collage-item text-pivot', children: 'Your Glow Pathway' }),
|
|
106
|
+
],
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
expect(mapStaggerTargets(grid, wrap, 0)).toBeNull()
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
it('returns null for a table-backed <DataProvider> beside a static sibling', () => {
|
|
113
|
+
// A table-backed array-mapper compiles to <DataProvider renderSuccess={…}> —
|
|
114
|
+
// opaque at build time for the same reason, so it collapses the same way.
|
|
115
|
+
const grid = element('div', {
|
|
116
|
+
className: 'people-grid',
|
|
117
|
+
children: [
|
|
118
|
+
element('DataProvider', { name: 'team_members', renderSuccess: (): null => null }),
|
|
119
|
+
element('div', { className: 'people-cta', children: 'Join us' }),
|
|
120
|
+
],
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
expect(mapStaggerTargets(grid, wrap, 0)).toBeNull()
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
it('still cascades when the repeater sits inside its own wrapper beside a sibling', () => {
|
|
127
|
+
// The wrapper is ALREADY the box between the grid and the items, so a
|
|
128
|
+
// motion.div around it changes no layout — only the repeater as a DIRECT
|
|
129
|
+
// child of the laying-out container is the collapse case.
|
|
130
|
+
const grid = element('div', {
|
|
131
|
+
className: 'cards',
|
|
132
|
+
children: [
|
|
133
|
+
element('div', {
|
|
134
|
+
className: 'card-rail',
|
|
135
|
+
children: element('Repeater', { items: [], renderItem: (): null => null }),
|
|
136
|
+
}),
|
|
137
|
+
element('div', { className: 'card', children: 'static' }),
|
|
138
|
+
],
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
const result = mapStaggerTargets(grid, wrap, 0) as StubElement
|
|
142
|
+
const wrapped = result.props.children as StubElement[]
|
|
143
|
+
expect(wrapped).toHaveLength(2)
|
|
144
|
+
expect(wrapped.every((w) => w.type === 'motion.div')).toBe(true)
|
|
145
|
+
})
|
|
93
146
|
})
|
|
94
147
|
|
|
95
148
|
describe('generated TqMotion source', () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cart-api-routes-generator.d.ts","sourceRoot":"","sources":["../../../src/ecommerce/cart-api-routes-generator.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cart-api-routes-generator.d.ts","sourceRoot":"","sources":["../../../src/ecommerce/cart-api-routes-generator.ts"],"names":[],"mappings":"AAWA,eAAO,MAAM,wBAAwB,mBAAoB,MAAM,GAAG,IAAI,KAAG,OACM,CAAA;AAE/E;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,oBAAoB,mBACf,MAAM,GAAG,IAAI,oBACX,OAAO,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,KAC/C,MAAM,GAAG,IAyNX,CAAA"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.generateCartApiRoute = exports.isPostgresCartDataSource = void 0;
|
|
4
|
+
var teleport_plugin_next_workflows_1 = require("@teleporthq/teleport-plugin-next-workflows");
|
|
4
5
|
var ecommerce_api_routes_generator_1 = require("./ecommerce-api-routes-generator");
|
|
5
6
|
// Datasource types whose `generateDbImport` emits a node-postgres `Pool`. The
|
|
6
7
|
// cart route is Postgres-specific (transactions via `db.connect()`, `$N`
|
|
@@ -38,7 +39,7 @@ var generateCartApiRoute = function (dataSourceType, dataSourceConfig) {
|
|
|
38
39
|
if (!dbImport) {
|
|
39
40
|
return null;
|
|
40
41
|
}
|
|
41
|
-
return "".concat(dbImport, "\
|
|
42
|
+
return "".concat(dbImport, "\n").concat((0, teleport_plugin_next_workflows_1.generateCommonJsSessionTokenResolverCode)(), "\nconst UUID_RE =/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i\n\nfunction generateUUID() {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) return crypto.randomUUID()\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {\n var r = (Math.random() * 16) | 0\n var v = c === 'x' ? r : (r & 0x3) | 0x8\n return v.toString(16)\n })\n}\n\n// Resolve who owns this cart. Logged-in users come from the NextAuth token\n// (a uuid id); guests supply a client-generated sessionId. anonymousUserId is\n// the guest-order attribution key (data-create-item maps guest orders onto\n// user_id via it) and is only used by mark-ordered.\nasync function resolveIdentity(req, body) {\n var userId = null\n try {\n var token = await __tqSessionToken(req)\n if (token) userId = token.id || token.sub || null\n } catch (e) {\n userId = null\n }\n if (userId != null && !UUID_RE.test(String(userId))) userId = null\n var sessionId = null\n if (body && typeof body.sessionId === 'string' && body.sessionId) {\n sessionId = body.sessionId.slice(0, 255)\n }\n var anonymousUserId = null\n if (body && typeof body.anonymousUserId === 'string' && UUID_RE.test(body.anonymousUserId)) {\n anonymousUserId = body.anonymousUserId\n }\n return { userId: userId, sessionId: sessionId, anonymousUserId: anonymousUserId }\n}\n\n// Owner WHERE fragment + bind params starting at $startIdx. Returns null when\n// there is no usable identity (caller treats that as \"match nothing\").\nfunction ownerClause(identity, startIdx) {\n if (identity.userId) {\n return { sql: 'user_id = $' + startIdx, params: [identity.userId] }\n }\n if (identity.sessionId) {\n return { sql: '(session_id = $' + startIdx + ' AND user_id IS NULL)', params: [identity.sessionId] }\n }\n return null\n}\n\nasync function handleLoad(req, res, body) {\n var identity = await resolveIdentity(req, body)\n var owner = ownerClause(identity, 1)\n if (!owner) return res.status(200).json({ ok: true, items: [] })\n try {\n var sql =\n 'SELECT product_id, variant_id, SUM(quantity) AS quantity FROM teleport_cart_items ' +\n \"WHERE cart_id = (SELECT id FROM teleport_cart WHERE status = 'active' AND \" +\n owner.sql +\n ' ORDER BY updated_at DESC LIMIT 1) GROUP BY product_id, variant_id'\n var result = await db.query(sql, owner.params)\n var items = (result.rows || [])\n .map(function (r) {\n return { productId: r.product_id, variantId: r.variant_id || null, quantity: Number(r.quantity) || 0 }\n })\n .filter(function (i) {\n return i.productId && i.quantity > 0\n })\n return res.status(200).json({ ok: true, items: items })\n } catch (e) {\n return res.status(200).json({ ok: false, items: [] })\n }\n}\n\nasync function handleSync(req, res, body) {\n if (req.method !== 'POST') return res.status(200).json({ ok: false })\n var identity = await resolveIdentity(req, body)\n if (!identity.userId && !identity.sessionId) return res.status(200).json({ ok: false })\n\n // Cart lines are keyed by (product, variant): two variants of the same\n // product stay distinct rows. Quantities are summed per line. Non-uuid\n // productIds are dropped before any DB call so a bad id can't 500 on the\n // product_id FK.\n var rawItems = body && Array.isArray(body.items) ? body.items : []\n var lineByKey = {}\n for (var i = 0; i < rawItems.length; i++) {\n var it = rawItems[i] || {}\n var pid = typeof it.productId === 'string' ? it.productId : ''\n if (!UUID_RE.test(pid)) continue\n var variantId = typeof it.variantId === 'string' && it.variantId ? it.variantId.slice(0, 255) : null\n var q = Math.floor(Number(it.quantity))\n if (!isFinite(q) || q <= 0) continue\n var key = pid + '|' + (variantId || '')\n if (lineByKey[key]) lineByKey[key].quantity += q\n else lineByKey[key] = { productId: pid, variantId: variantId, quantity: q }\n }\n var lines = Object.keys(lineByKey).map(function (k) {\n return lineByKey[k]\n })\n\n var client\n try {\n client = await db.connect()\n } catch (e) {\n return res.status(200).json({ ok: false })\n }\n try {\n await client.query('BEGIN')\n var owner = ownerClause(identity, 1)\n // Lock the active cart row so two tabs syncing at once serialize instead\n // of racing to create duplicate carts.\n var found = await client.query(\n \"SELECT id FROM teleport_cart WHERE status = 'active' AND \" +\n owner.sql +\n ' ORDER BY updated_at DESC LIMIT 1 FOR UPDATE',\n owner.params\n )\n var cartId\n if (found.rows && found.rows.length > 0) {\n cartId = found.rows[0].id\n } else if (lines.length === 0) {\n // Nothing to store and no existing cart \u2014 don't create an empty row.\n await client.query('COMMIT')\n return res.status(200).json({ ok: true })\n } else {\n cartId = generateUUID()\n await client.query(\n 'INSERT INTO teleport_cart (id, user_id, session_id, status, created_at, updated_at) ' +\n \"VALUES ($1, $2, $3, 'active', NOW(), NOW())\",\n [cartId, identity.userId || null, identity.userId ? null : identity.sessionId]\n )\n }\n await client.query('DELETE FROM teleport_cart_items WHERE cart_id = $1', [cartId])\n for (var p = 0; p < lines.length; p++) {\n var line = lines[p]\n // WHERE EXISTS guards the product FK: a stale product becomes a silent\n // no-op instead of a 23503 violation that would abort the transaction.\n await client.query(\n 'INSERT INTO teleport_cart_items (id, cart_id, product_id, variant_id, quantity, created_at, updated_at) ' +\n 'SELECT $1, $2, $3, $4, $5, NOW(), NOW() WHERE EXISTS (SELECT 1 FROM teleport_products WHERE id = $3)',\n [generateUUID(), cartId, line.productId, line.variantId, line.quantity]\n )\n }\n await client.query('UPDATE teleport_cart SET updated_at = NOW() WHERE id = $1', [cartId])\n await client.query('COMMIT')\n return res.status(200).json({ ok: true })\n } catch (e) {\n try {\n await client.query('ROLLBACK')\n } catch (e2) {}\n return res.status(200).json({ ok: false })\n } finally {\n try {\n client.release()\n } catch (e3) {}\n }\n}\n\nasync function handleMarkOrdered(req, res, body) {\n if (req.method !== 'POST') return res.status(200).json({ ok: false })\n var identity = await resolveIdentity(req, body)\n var clauses = []\n var params = []\n if (identity.userId) {\n params.push(identity.userId)\n clauses.push('user_id = $' + params.length)\n }\n if (identity.sessionId) {\n params.push(identity.sessionId)\n clauses.push('(session_id = $' + params.length + ' AND user_id IS NULL)')\n }\n if (identity.anonymousUserId) {\n params.push(identity.anonymousUserId)\n clauses.push('user_id = $' + params.length)\n }\n if (clauses.length === 0) return res.status(200).json({ ok: true, updated: 0 })\n try {\n var result = await db.query(\n \"UPDATE teleport_cart SET status = 'ordered', updated_at = NOW() \" +\n \"WHERE status = 'active' AND (\" +\n clauses.join(' OR ') +\n ')',\n params\n )\n return res.status(200).json({ ok: true, updated: result.rowCount || 0 })\n } catch (e) {\n return res.status(200).json({ ok: false })\n }\n}\n\nmodule.exports = async function handler(req, res) {\n var op = req.query.op\n var body = {}\n try {\n body = typeof req.body === 'string' ? JSON.parse(req.body) : req.body || {}\n } catch (e) {\n body = {}\n }\n try {\n if (op === 'load') return await handleLoad(req, res, body)\n if (op === 'sync') return await handleSync(req, res, body)\n if (op === 'mark-ordered') return await handleMarkOrdered(req, res, body)\n return res.status(200).json({ ok: false, error: 'unknown op' })\n } catch (e) {\n // The cart must never break the app: degrade to localStorage-only.\n return res.status(200).json({ ok: false })\n }\n}\n");
|
|
42
43
|
};
|
|
43
44
|
exports.generateCartApiRoute = generateCartApiRoute;
|
|
44
45
|
//# sourceMappingURL=cart-api-routes-generator.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cart-api-routes-generator.js","sourceRoot":"","sources":["../../../src/ecommerce/cart-api-routes-generator.ts"],"names":[],"mappings":";;;AAAA,mFAAmE;AAEnE,8EAA8E;AAC9E,yEAAyE;AACzE,6EAA6E;AAC7E,6EAA6E;AAC7E,2EAA2E;AAC3E,4CAA4C;AAC5C,IAAM,0BAA0B,GAAG,CAAC,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,iBAAiB,CAAC,CAAA;AAExF,IAAM,wBAAwB,GAAG,UAAC,cAA6B;IACpE,OAAA,CAAC,CAAC,cAAc,IAAI,0BAA0B,CAAC,OAAO,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;AAA7E,CAA6E,CAAA;AADlE,QAAA,wBAAwB,4BAC0C;AAE/E;;;;;;;;;;;;;;;;GAgBG;AACI,IAAM,oBAAoB,GAAG,UAClC,cAA6B,EAC7B,gBAAgD;IAEhD,IAAI,CAAC,IAAA,gCAAwB,EAAC,cAAc,CAAC,EAAE;QAC7C,OAAO,IAAI,CAAA;KACZ;IACD,IAAM,QAAQ,GAAG,IAAA,iDAAgB,EAAC,cAAc,EAAE,gBAAgB,CAAC,CAAA;IACnE,IAAI,CAAC,QAAQ,EAAE;QACb,OAAO,IAAI,CAAA;KACZ;IAED,OAAO,UAAG,QAAQ,
|
|
1
|
+
{"version":3,"file":"cart-api-routes-generator.js","sourceRoot":"","sources":["../../../src/ecommerce/cart-api-routes-generator.ts"],"names":[],"mappings":";;;AAAA,6FAAqG;AACrG,mFAAmE;AAEnE,8EAA8E;AAC9E,yEAAyE;AACzE,6EAA6E;AAC7E,6EAA6E;AAC7E,2EAA2E;AAC3E,4CAA4C;AAC5C,IAAM,0BAA0B,GAAG,CAAC,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,iBAAiB,CAAC,CAAA;AAExF,IAAM,wBAAwB,GAAG,UAAC,cAA6B;IACpE,OAAA,CAAC,CAAC,cAAc,IAAI,0BAA0B,CAAC,OAAO,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;AAA7E,CAA6E,CAAA;AADlE,QAAA,wBAAwB,4BAC0C;AAE/E;;;;;;;;;;;;;;;;GAgBG;AACI,IAAM,oBAAoB,GAAG,UAClC,cAA6B,EAC7B,gBAAgD;IAEhD,IAAI,CAAC,IAAA,gCAAwB,EAAC,cAAc,CAAC,EAAE;QAC7C,OAAO,IAAI,CAAA;KACZ;IACD,IAAM,QAAQ,GAAG,IAAA,iDAAgB,EAAC,cAAc,EAAE,gBAAgB,CAAC,CAAA;IACnE,IAAI,CAAC,QAAQ,EAAE;QACb,OAAO,IAAI,CAAA;KACZ;IAED,OAAO,UAAG,QAAQ,eAClB,IAAA,yEAAwC,GAAE,s4PA8M3C,CAAA;AACD,CAAC,CAAA;AA5NY,QAAA,oBAAoB,wBA4NhC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ecommerce-api-routes-generator.d.ts","sourceRoot":"","sources":["../../../src/ecommerce/ecommerce-api-routes-generator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAA;
|
|
1
|
+
{"version":3,"file":"ecommerce-api-routes-generator.d.ts","sourceRoot":"","sources":["../../../src/ecommerce/ecommerce-api-routes-generator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAA;AAWlE,eAAO,MAAM,qCAAqC,aACtC,qBAAqB,KAC9B,OAAO,MAAM,EAAE,OAAO,CAaxB,CAAA;AAED,eAAO,MAAM,iCAAiC,aAAc,qBAAqB,KAAG,MAUnF,CAAA;AAED,eAAO,MAAM,wBAAwB,aACzB,qBAAqB,kBACf,MAAM,GAAG,IAAI,oBACX,OAAO,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,KAC/C,MAmNF,CAAA;AAED,eAAO,MAAM,0BAA0B,aAC3B,qBAAqB,kBACf,MAAM,GAAG,IAAI,oBACX,OAAO,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,KAC/C,MA0EF,CAAA;AAQD,eAAO,MAAM,+BAA+B,mBAC1B,MAAM,GAAG,IAAI,oBACX,OAAO,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,KAC/C,MAwCF,CAAA;AAED,eAAO,MAAM,8BAA8B,mBACzB,MAAM,GAAG,IAAI,oBACX,OAAO,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,KAC/C,MAkCF,CAAA;AAaD,eAAO,MAAM,6BAA6B,QAAO,MA2IhD,CAAA;AAED,eAAO,MAAM,6BAA6B,aAAc,qBAAqB,KAAG,MA6C/E,CAAA;AAED,eAAO,MAAM,iCAAiC,aAAc,qBAAqB,KAAG,MAqNnF,CAAA;AAQD,eAAO,MAAM,6BAA6B,aAAc,qBAAqB,KAAG,MAwG/E,CAAA;AAED,wBAAgB,gBAAgB,CAC9B,cAAc,EAAE,MAAM,GAAG,IAAI,EAC7B,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAC/C,MAAM,GAAG,IAAI,CAkDf"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.generateDbImport = exports.generateLowStockAlertApiRoute = exports.generateOrderNotificationApiRoute = exports.generateDeliveryPriceApiRoute = exports.generatePaypalCaptureApiRoute = exports.generateStoreLocationsApiRoute = exports.generateProductVariantsApiRoute = exports.generateStockCheckApiRoute = exports.generateCheckoutApiRoute = exports.generateEcommerceSettingsApiRoute = exports.buildWorkflowEcommerceSettingsPayload = void 0;
|
|
4
|
+
var teleport_plugin_next_workflows_1 = require("@teleporthq/teleport-plugin-next-workflows");
|
|
4
5
|
// The settings payload every workflow-facing consumer shares: the
|
|
5
6
|
// /api/ecommerce/settings route bakes it as its response literal, and the
|
|
6
7
|
// generated ecommerce-context publishes the SAME object on
|
|
@@ -48,7 +49,7 @@ var generateCheckoutApiRoute = function (settings, dataSourceType, dataSourceCon
|
|
|
48
49
|
? "await db.query(\n 'UPDATE teleport_orders SET status = $1, payment_status = $2 WHERE id = $3',\n ['confirmed', 'pending', orderId]\n )"
|
|
49
50
|
: '', "\n return res.status(200).json({ success: true, orderId, paymentMethod: 'cash_on_delivery' })\n }")
|
|
50
51
|
: '';
|
|
51
|
-
return "".concat(dbImport ? dbImport +
|
|
52
|
+
return "".concat(dbImport ? dbImport + '\n' + (0, teleport_plugin_next_workflows_1.generateCommonJsSessionTokenResolverCode)() : '', "\nexport default async function handler(req, res) {\n if (req.method !== 'POST') {\n return res.status(405).json({ error: 'Method not allowed' })\n }\n\n try {\n const {\n cartItems,\n customer,\n deliveryAddress,\n fulfillmentMethod,\n paymentMethod,\n deliveryNotes,\n storeLocationId,\n currency,\n } = req.body\n\n if (!cartItems || !Array.isArray(cartItems) || cartItems.length === 0) {\n return res.status(400).json({ error: 'Cart is empty' })\n }\n").concat(((_a = settings.stockManagementConfig) === null || _a === void 0 ? void 0 : _a.maxQuantityPerProduct) != null
|
|
52
53
|
? "\n const maxQtyPerProduct = ".concat(settings.stockManagementConfig.maxQuantityPerProduct, "\n for (const item of cartItems) {\n if ((item.quantity || 1) > maxQtyPerProduct) {\n return res.status(400).json({\n error: 'Maximum ' + maxQtyPerProduct + ' units allowed per product',\n productId: item.productId,\n })\n }\n }\n")
|
|
53
54
|
: '', "\n if (!paymentMethod) {\n return res.status(400).json({ error: 'Payment method is required' })\n }\n").concat(settings.guestCheckout
|
|
54
55
|
? ''
|
|
@@ -61,7 +62,7 @@ var generateCheckoutApiRoute = function (settings, dataSourceType, dataSourceCon
|
|
|
61
62
|
? " if (subtotal >= ".concat(settings.deliveryConfig.freeDeliveryThreshold, ") {\n deliveryCost = 0\n }")
|
|
62
63
|
: '', "\n }\n")
|
|
63
64
|
: '', "\n const totalAmount = subtotal + deliveryCost\n\n let orderId = null\n").concat(dbImport
|
|
64
|
-
? " const orderResult = await db.query(\n `INSERT INTO teleport_orders (\n customer_email, customer_name, payment_method, fulfillment_method,\n subtotal, delivery_cost, total_amount, status, payment_status,\n shipping_street, shipping_city, shipping_state, shipping_zip, shipping_country,\n store_location_id, delivery_notes, created_at\n ) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,NOW())\n RETURNING id`,\n [\n customer?.email || null,\n customer?.name || null,\n paymentMethod,\n fulfillmentMethod || 'delivery',\n subtotal,\n deliveryCost,\n totalAmount,\n 'pending',\n 'pending',\n deliveryAddress?.street || null,\n deliveryAddress?.city || null,\n deliveryAddress?.state || null,\n deliveryAddress?.zip || null,\n deliveryAddress?.country || null,\n storeLocationId || null,\n deliveryNotes || null,\n ]\n )\n orderId = orderResult.rows[0].id\n\n for (const item of cartItems) {\n await db.query(\n `INSERT INTO teleport_order_items (order_id, product_id, variant_id, quantity, price, name)\n VALUES ($1,$2,$3,$4,$5,$6)`,\n [orderId, item.productId, item.variantId || null, item.quantity || 1, item.price || 0, item.name || '']\n )\n }\n\n // Best-effort: mark this buyer's active cart as ordered. Wrapped so a\n // failure never aborts the (already successful) order response.\n try {\n let __cartUserId = null\n try {\n const __tok = await
|
|
65
|
+
? " const orderResult = await db.query(\n `INSERT INTO teleport_orders (\n customer_email, customer_name, payment_method, fulfillment_method,\n subtotal, delivery_cost, total_amount, status, payment_status,\n shipping_street, shipping_city, shipping_state, shipping_zip, shipping_country,\n store_location_id, delivery_notes, created_at\n ) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,NOW())\n RETURNING id`,\n [\n customer?.email || null,\n customer?.name || null,\n paymentMethod,\n fulfillmentMethod || 'delivery',\n subtotal,\n deliveryCost,\n totalAmount,\n 'pending',\n 'pending',\n deliveryAddress?.street || null,\n deliveryAddress?.city || null,\n deliveryAddress?.state || null,\n deliveryAddress?.zip || null,\n deliveryAddress?.country || null,\n storeLocationId || null,\n deliveryNotes || null,\n ]\n )\n orderId = orderResult.rows[0].id\n\n for (const item of cartItems) {\n await db.query(\n `INSERT INTO teleport_order_items (order_id, product_id, variant_id, quantity, price, name)\n VALUES ($1,$2,$3,$4,$5,$6)`,\n [orderId, item.productId, item.variantId || null, item.quantity || 1, item.price || 0, item.name || '']\n )\n }\n\n // Best-effort: mark this buyer's active cart as ordered. Wrapped so a\n // failure never aborts the (already successful) order response.\n try {\n let __cartUserId = null\n try {\n const __tok = await __tqSessionToken(req)\n if (__tok) __cartUserId = __tok.id || __tok.sub || null\n } catch (e) { __cartUserId = null }\n const __cartSessionId = req.body && req.body.sessionId ? String(req.body.sessionId).slice(0, 255) : null\n const __clauses = []\n const __params = []\n if (__cartUserId) { __params.push(__cartUserId); __clauses.push('user_id = $' + __params.length) }\n if (__cartSessionId) { __params.push(__cartSessionId); __clauses.push('(session_id = $' + __params.length + ' AND user_id IS NULL)') }\n if (__clauses.length > 0) {\n await db.query(\n \"UPDATE teleport_cart SET status = 'ordered', updated_at = NOW() WHERE status = 'active' AND (\" + __clauses.join(' OR ') + ')',\n __params\n )\n }\n } catch (e) {}"
|
|
65
66
|
: " orderId = 'order_' + Date.now()", "\n").concat(stripeBlock).concat(paypalBlock).concat(codBlock, "\n\n return res.status(200).json({ success: true, orderId, totalAmount })\n } catch (error) {\n console.error('Checkout error:', error)\n return res.status(500).json({ error: 'Internal server error' })\n }\n}\n");
|
|
66
67
|
};
|
|
67
68
|
exports.generateCheckoutApiRoute = generateCheckoutApiRoute;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ecommerce-api-routes-generator.js","sourceRoot":"","sources":["../../../src/ecommerce/ecommerce-api-routes-generator.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"ecommerce-api-routes-generator.js","sourceRoot":"","sources":["../../../src/ecommerce/ecommerce-api-routes-generator.ts"],"names":[],"mappings":";;;AACA,6FAAqG;AAErG,kEAAkE;AAClE,0EAA0E;AAC1E,2DAA2D;AAC3D,uEAAuE;AACvE,8EAA8E;AAC9E,6EAA6E;AAC7E,sEAAsE;AACtE,mBAAmB;AACZ,IAAM,qCAAqC,GAAG,UACnD,QAA+B;;IAE/B,IAAM,WAAW,GAAG,QAAQ,CAAC,qBAAqB,CAAA;IAClD,OAAO;QACL,aAAa,EAAE,QAAQ,CAAC,aAAa;QACrC,eAAe,EAAE,QAAQ,CAAC,eAAe;QACzC,eAAe,EAAE,MAAA,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAE,eAAe,mCAAI,IAAI;QACrD,qBAAqB,EAAE,MAAA,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAE,qBAAqB,mCAAI,IAAI;QACjE,iBAAiB,EAAE,MAAA,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAE,iBAAiB,mCAAI,CAAC;QACtD,oBAAoB,EAAE,MAAA,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAE,oBAAoB,mCAAI,SAAS;QACpE,cAAc,EAAE,QAAQ,CAAC,cAAc;QACvC,eAAe,EAAE,QAAQ,CAAC,eAAe;QACzC,kBAAkB,EAAE,QAAQ,CAAC,kBAAkB;KAChD,CAAA;AACH,CAAC,CAAA;AAfY,QAAA,qCAAqC,yCAejD;AAEM,IAAM,iCAAiC,GAAG,UAAC,QAA+B;IAC/E,IAAM,eAAe,GAAG,IAAI,CAAC,SAAS,CAAC,IAAA,6CAAqC,EAAC,QAAQ,CAAC,CAAC,CAAA;IAEvF,OAAO,2LAIuB,eAAe,WAE9C,CAAA;AACD,CAAC,CAAA;AAVY,QAAA,iCAAiC,qCAU7C;AAEM,IAAM,wBAAwB,GAAG,UACtC,QAA+B,EAC/B,cAA6B,EAC7B,gBAAgD;;IAEhD,IAAM,QAAQ,GAAG,gBAAgB,CAAC,cAAc,EAAE,gBAAgB,CAAC,CAAA;IACnE,IAAM,gBAAgB,GAAG,QAAQ,CAAC,gBAAgB,IAAI,EAAE,CAAA;IACxD,IAAM,aAAa,GAAG,gBAAgB,CAAC,GAAG,CAAC,UAAC,CAAC,IAAK,OAAA,CAAC,CAAC,IAAI,EAAN,CAAM,CAAC,CAAA;IACzD,IAAM,SAAS,GAAG,aAAa,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;IAClD,IAAM,SAAS,GAAG,aAAa,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;IAElD,IAAM,WAAW,GAAG,SAAS;QAC3B,CAAC,CAAC,ohCAoBF;QACA,CAAC,CAAC,EAAE,CAAA;IAEN,IAAM,WAAW,GAAG,SAAS;QAC3B,CAAC,CAAC,kIAGF;QACA,CAAC,CAAC,EAAE,CAAA;IAEN,IAAM,QAAQ,GAAG,QAAQ,CAAC,cAAc;QACtC,CAAC,CAAC,+DAGA,QAAQ;YACN,CAAC,CAAC,qJAGJ;YACE,CAAC,CAAC,EAAE,0GAGR;QACA,CAAC,CAAC,EAAE,CAAA;IAEN,OAAO,UAAG,QAAQ,CAAC,CAAC,CAAC,QAAQ,GAAG,IAAI,GAAG,IAAA,yEAAwC,GAAE,CAAC,CAAC,CAAC,EAAE,mhBAsBtF,CAAA,MAAA,QAAQ,CAAC,qBAAqB,0CAAE,qBAAqB,KAAI,IAAI;QAC3D,CAAC,CAAC,yCACyB,QAAQ,CAAC,qBAAqB,CAAC,qBAAqB,2RASlF;QACG,CAAC,CAAC,EAAE,6HAMN,QAAQ,CAAC,aAAa;QACpB,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC,sIAIL,SAEG,QAAQ,CAAC,eAAe;QACtB,CAAC,CAAC,uLAIP;QACK,CAAC,CAAC,EAAE,SAEN,QAAQ,CAAC,kBAAkB;QACzB,CAAC,CAAC,uKAIP;QACK,CAAC,CAAC,EAAE,yKASR,QAAQ,CAAC,eAAe,IAAI,QAAQ,CAAC,cAAc;QACjD,CAAC,CAAC,4EACiB,QAAQ,CAAC,cAAc,CAAC,aAAa,eAE1D,QAAQ,CAAC,cAAc,CAAC,mBAAmB;YACzC,CAAC,CAAC,gCAAyB,QAAQ,CAAC,cAAc,CAAC,qBAAqB,2CAEpE;YACJ,CAAC,CAAC,EAAE,cAGP;QACG,CAAC,CAAC,EAAE,0FAMN,QAAQ;QACN,CAAC,CAAC,42EAwDa;QACf,CAAC,CAAC,qCAAqC,eAEzC,WAAW,SAAG,WAAW,SAAG,QAAQ,kOAQrC,CAAA;AACD,CAAC,CAAA;AAvNY,QAAA,wBAAwB,4BAuNpC;AAEM,IAAM,0BAA0B,GAAG,UACxC,QAA+B,EAC/B,cAA6B,EAC7B,gBAAgD;;IAEhD,IAAM,QAAQ,GAAG,gBAAgB,CAAC,cAAc,EAAE,gBAAgB,CAAC,CAAA;IACnE,IAAM,MAAM,GAAG,MAAA,MAAA,QAAQ,CAAC,qBAAqB,0CAAE,qBAAqB,mCAAI,IAAI,CAAA;IAC5E,IAAM,eAAe,GAAG,MAAA,MAAA,QAAQ,CAAC,qBAAqB,0CAAE,eAAe,mCAAI,KAAK,CAAA;IAEhF,IAAI,CAAC,QAAQ,EAAE;QACb,OAAO,yPAKP,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,aAGpC,CAAA;KACE;IAED,OAAO,UAAG,QAAQ,gmCA8BW,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,2CAClC,eAAe,qyBAyB5C,CAAA;AACD,CAAC,CAAA;AA9EY,QAAA,0BAA0B,8BA8EtC;AAED,0EAA0E;AAC1E,6EAA6E;AAC7E,gFAAgF;AAChF,gFAAgF;AAChF,4EAA4E;AAC5E,kEAAkE;AAC3D,IAAM,+BAA+B,GAAG,UAC7C,cAA6B,EAC7B,gBAAgD;IAEhD,IAAM,QAAQ,GAAG,gBAAgB,CAAC,cAAc,EAAE,gBAAgB,CAAC,CAAA;IACnE,IAAI,CAAC,QAAQ,EAAE;QACb,OAAO,+MAMV,CAAA;KACE;IAED,OAAO,UAAG,QAAQ,y6BA2BnB,CAAA;AACD,CAAC,CAAA;AA3CY,QAAA,+BAA+B,mCA2C3C;AAEM,IAAM,8BAA8B,GAAG,UAC5C,cAA6B,EAC7B,gBAAgD;IAEhD,IAAM,QAAQ,GAAG,gBAAgB,CAAC,cAAc,EAAE,gBAAgB,CAAC,CAAA;IACnE,IAAI,CAAC,QAAQ,EAAE;QACb,OAAO,gNAMV,CAAA;KACE;IAED,OAAO,UAAG,QAAQ,m3BAqBnB,CAAA;AACD,CAAC,CAAA;AArCY,QAAA,8BAA8B,kCAqC1C;AAED,0EAA0E;AAC1E,6EAA6E;AAC7E,2EAA2E;AAC3E,wEAAwE;AACxE,4EAA4E;AAC5E,6CAA6C;AAC7C,EAAE;AACF,0EAA0E;AAC1E,uEAAuE;AACvE,yEAAyE;AACzE,gEAAgE;AACzD,IAAM,6BAA6B,GAAG;IAC3C,OAAO,snKAyIR,CAAA;AACD,CAAC,CAAA;AA3IY,QAAA,6BAA6B,iCA2IzC;AAEM,IAAM,6BAA6B,GAAG,UAAC,QAA+B;IAC3E,IAAM,MAAM,GAAG,QAAQ,CAAC,cAAc,CAAA;IAEtC,IAAI,CAAC,MAAM,EAAE;QACX,OAAO,iOAMV,CAAA;KACE;IAED,OAAO,wOAOe,MAAM,CAAC,aAAa,+CACV,MAAM,CAAC,mBAAmB,iDACxB,MAAM,CAAC,qBAAqB,oUAcxD,MAAM,CAAC,qBAAqB,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAC,CAAC,MAAM,qKAQpF,CAAA;AACD,CAAC,CAAA;AA7CY,QAAA,6BAA6B,iCA6CzC;AAEM,IAAM,iCAAiC,GAAG,UAAC,QAA+B;IAC/E,IAAM,MAAM,GAAG,QAAQ,CAAC,uBAAuB,CAAA;IAC/C,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE;QAC/B,OAAO,uPAMV,CAAA;KACE;IAED,IAAM,eAAe,GAAG,MAAM,CAAC,OAAO,IAAI,2BAA2B,CAAA;IACrE,IAAM,YAAY,GAAG,MAAM,CAAC,IAAI,IAAI,2CAA2C,CAAA;IAC/E,IAAM,kBAAkB,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,kBAAkB,IAAI,EAAE,CAAC,CAAA;IAE1E,oEAAoE;IACpE,uDAAuD;IACvD,8DAA8D;IAC9D,mDAAmD;IACnD,OAAO,4gBAsBwB,kBAAkB,6rHAoFP,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,mEACpC,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,6gBASxC,IAAI,CAAC,SAAS,CACnC,YAAY,CACb,2vIA0EJ,CAAA;AACD,CAAC,CAAA;AArNY,QAAA,iCAAiC,qCAqN7C;AAED,6DAA6D;AAC7D,uDAAuD;AACvD,iEAAiE;AACjE,oEAAoE;AACpE,kEAAkE;AAClE,aAAa;AACN,IAAM,6BAA6B,GAAG,UAAC,QAA+B;;IAC3E,IAAM,WAAW,GAAG,QAAQ,CAAC,qBAAqB,CAAA;IAClD,IAAM,WAAW,GAAG,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAE,mBAAmB,CAAA;IACpD,+DAA+D;IAC/D,0DAA0D;IAC1D,+DAA+D;IAC/D,+DAA+D;IAC/D,6DAA6D;IAC7D,6DAA6D;IAC7D,qCAAqC;IACrC,IACE,CAAC,QAAQ,CAAC,eAAe;QACzB,CAAC,WAAW;QACZ,CAAC,WAAW,CAAC,cAAc;QAC3B,CAAC,WAAW;QACZ,CAAC,WAAW,CAAC,QAAQ,EACrB;QACA,OAAO,0PAMV,CAAA;KACE;IAED,IAAM,eAAe,GACnB,WAAW,CAAC,OAAO,IAAI,gEAAgE,CAAA;IACzF,IAAM,YAAY,GAChB,WAAW,CAAC,IAAI;QAChB,0GAA0G,CAAA;IAC5G,IAAM,kBAAkB,GAAG,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,kBAAkB,IAAI,EAAE,CAAC,CAAA;IAC/E,IAAM,SAAS,GAAG,MAAA,WAAW,CAAC,iBAAiB,mCAAI,CAAC,CAAA;IAEpD,OAAO,itDAyCwB,kBAAkB,2VAM6D,SAAS,4iBAY7E,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,qEAClC,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,+aASpE,CAAA;AACD,CAAC,CAAA;AAxGY,QAAA,6BAA6B,iCAwGzC;AAED,SAAgB,gBAAgB,CAC9B,cAA6B,EAC7B,gBAAgD;IAEhD,IAAI,CAAC,cAAc,IAAI,CAAC,gBAAgB,EAAE;QACxC,OAAO,IAAI,CAAA;KACZ;IAED,QAAQ,cAAc,EAAE;QACtB,KAAK,UAAU;YACb,oEAAoE;YACpE,8DAA8D;YAC9D,kEAAkE;YAClE,kEAAkE;YAClE,uDAAuD;YACvD,OAAO,uJAGV,CAAA;QACC,KAAK,YAAY,CAAC;QAClB,KAAK,aAAa,CAAC;QACnB,KAAK,iBAAiB;YACpB,OAAO,0GAGV,CAAA;QACC,KAAK,OAAO,CAAC;QACb,KAAK,SAAS,CAAC;QACf,KAAK,MAAM;YACT,OAAO,4GACqD,CAAA;QAC9D,KAAK,UAAU;YACb,OAAO,0WAQX,CAAA;QACE,KAAK,OAAO;YACV,OAAO,+UAOX,CAAA;QACE;YACE,OAAO,IAAI,CAAA;KACd;AACH,CAAC;AArDD,4CAqDC"}
|