@porulle/plugin-pos 0.8.0 → 0.10.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.
@@ -1 +1 @@
1
- {"version":3,"file":"checkout-pos.d.ts","sourceRoot":"","sources":["../../src/hooks/checkout-pos.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAEtC,UAAU,YAAY;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC1C,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAaD;;GAEG;AACH,wBAAgB,oBAAoB;;uBAGP,OAAO,EAAE;EAWrC;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,EAAE;;uBAG3B,OAAO,EAAE;EAsCrC"}
1
+ {"version":3,"file":"checkout-pos.d.ts","sourceRoot":"","sources":["../../src/hooks/checkout-pos.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAEtC,UAAU,YAAY;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC1C,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAaD;;GAEG;AACH,wBAAgB,oBAAoB;;uBAGP,OAAO,EAAE;EAWrC;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,EAAE;;uBAG3B,OAAO,EAAE;EA6CrC"}
@@ -4,7 +4,7 @@
4
4
  * checkout.beforePayment: Sets shippingTotal = 0 for POS transactions (no shipping).
5
5
  * checkout.afterCreate: Updates POS transaction with orderId, increments shift counters.
6
6
  */
7
- import { eq, sql } from "@porulle/core/drizzle";
7
+ import { eq, and, sql } from "@porulle/core/drizzle";
8
8
  import { posTransactions, posShifts } from "../schema.js";
9
9
  /**
10
10
  * Before payment hook: zero out shipping for POS transactions.
@@ -55,6 +55,13 @@ export function buildPOSFinalizationHook(getDb) {
55
55
  // Increment shift sales counters
56
56
  const posShiftId = metadata?.posShiftId;
57
57
  if (posShiftId) {
58
+ const txnRows = await db
59
+ .select({ organizationId: posTransactions.organizationId })
60
+ .from(posTransactions)
61
+ .where(eq(posTransactions.id, posTransactionId));
62
+ const orgId = txnRows[0]?.organizationId;
63
+ if (!orgId)
64
+ return;
58
65
  await db
59
66
  .update(posShifts)
60
67
  .set({
@@ -62,7 +69,7 @@ export function buildPOSFinalizationHook(getDb) {
62
69
  salesTotal: sql `${posShifts.salesTotal} + ${result.grandTotal ?? 0}`,
63
70
  updatedAt: new Date(),
64
71
  })
65
- .where(eq(posShifts.id, posShiftId));
72
+ .where(and(eq(posShifts.id, posShiftId), eq(posShifts.organizationId, orgId)));
66
73
  }
67
74
  },
68
75
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA6BA,OAAO,KAAK,EAAE,gBAAgB,EAAM,MAAM,YAAY,CAAC;AAGvD,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AACvE,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC/E,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAE/D;;;;;;;;;;;;;GAaG;AACH,wBAAgB,SAAS,CAAC,WAAW,GAAE,gBAAqB,0CAsG3D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAAE,gBAAgB,EAAM,MAAM,YAAY,CAAC;AAGvD,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AACvE,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC/E,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAE/D;;;;;;;;;;;;;GAaG;AACH,wBAAgB,SAAS,CAAC,WAAW,GAAE,gBAAqB,0CAwG3D"}
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { defineCommercePlugin } from "@porulle/core";
2
- import { posTerminals, posShifts, posCashEvents, posTransactions, posPayments, posReturnItems, posOperatorPins, } from "./schema.js";
2
+ import { posTerminals, posShifts, posCashEvents, posTransactions, posPayments, posReturnItems, posOperatorPins, posPinAttempts, } from "./schema.js";
3
3
  import { TerminalService } from "./services/terminal-service.js";
4
4
  import { ShiftService } from "./services/shift-service.js";
5
5
  import { TransactionService } from "./services/transaction-service.js";
@@ -76,6 +76,7 @@ export function posPlugin(userOptions = {}) {
76
76
  posPayments,
77
77
  posReturnItems,
78
78
  posOperatorPins,
79
+ posPinAttempts,
79
80
  }),
80
81
  // Scope used by PIN login to mint per-shift credentials (issue #51).
81
82
  apiKeyScopes: () => ({
@@ -83,6 +84,7 @@ export function posPlugin(userOptions = {}) {
83
84
  prefix: "pos_shift_",
84
85
  description: "Short-lived per-shift POS credentials minted by PIN login.",
85
86
  permissions: { pos: ["operate"] },
87
+ enableMetadata: true,
86
88
  // Allow credentials as short as 15 minutes (Better Auth default
87
89
  // minimum is 1 day — too long for per-shift keys).
88
90
  keyExpiration: { minExpiresIn: 1 / 96 },
@@ -1,4 +1,4 @@
1
- import type { PinService } from "../services/pin-service.js";
1
+ import { type PinService } from "../services/pin-service.js";
2
2
  import type { PluginRouteRegistration } from "@porulle/core";
3
3
  /**
4
4
  * PIN auth runtime (issue #51).
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/routes/auth.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAc,MAAM,4BAA4B,CAAC;AACzE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAE7D;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,UAAU,EACnB,GAAG,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,QAAQ,CAAC,EAAE;QAAE,EAAE,EAAE,OAAO,CAAA;KAAE,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,GACtF,uBAAuB,EAAE,CAiD3B"}
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/routes/auth.ts"],"names":[],"mappings":"AAEA,OAAO,EAAqB,KAAK,UAAU,EAAmB,MAAM,4BAA4B,CAAC;AACjG,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAO7D;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,UAAU,EACnB,GAAG,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,QAAQ,CAAC,EAAE;QAAE,EAAE,EAAE,OAAO,CAAA;KAAE,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,GACtF,uBAAuB,EAAE,CAiD3B"}
@@ -1,5 +1,11 @@
1
- import { router } from "@porulle/core";
1
+ import { router, CommerceValidationError } from "@porulle/core";
2
2
  import { z } from "@hono/zod-openapi";
3
+ import { PIN_LOCKOUT_ERROR } from "../services/pin-service.js";
4
+ function throwPinRouteError(message) {
5
+ if (message === PIN_LOCKOUT_ERROR)
6
+ throw new CommerceValidationError(message);
7
+ throw new Error(message);
8
+ }
3
9
  /**
4
10
  * PIN auth runtime (issue #51).
5
11
  *
@@ -23,7 +29,7 @@ export function buildPinAuthRoutes(service, ctx) {
23
29
  const body = input;
24
30
  const result = await service.setPin(orgId, body);
25
31
  if (!result.ok)
26
- throw new Error(result.error);
32
+ throwPinRouteError(result.error);
27
33
  return result.value;
28
34
  });
29
35
  r.post("/pin-login")
@@ -38,7 +44,7 @@ export function buildPinAuthRoutes(service, ctx) {
38
44
  const body = input;
39
45
  const result = await service.pinLogin(orgId, body, ctx.auth);
40
46
  if (!result.ok)
41
- throw new Error(result.error);
47
+ throwPinRouteError(result.error);
42
48
  return result.value;
43
49
  });
44
50
  r.post("/override")
@@ -53,7 +59,7 @@ export function buildPinAuthRoutes(service, ctx) {
53
59
  const body = input;
54
60
  const result = await service.override(orgId, body);
55
61
  if (!result.ok)
56
- throw new Error(result.error);
62
+ throwPinRouteError(result.error);
57
63
  return result.value;
58
64
  });
59
65
  return r.routes();
@@ -1 +1 @@
1
- {"version":3,"file":"returns.d.ts","sourceRoot":"","sources":["../../src/routes/returns.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AAC7E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAE7D,wBAAgB,iBAAiB,CAC/B,aAAa,EAAE,aAAa,EAC5B,kBAAkB,EAAE,kBAAkB,EACtC,cAAc,EAAE,cAAc,EAC9B,WAAW,EAAE;IAAE,MAAM,EAAE,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,EAAE,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE;YAAE,EAAE,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC,CAAA;CAAE,EAC/J,GAAG,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,QAAQ,CAAC,EAAE;QAAE,EAAE,EAAE,OAAO,CAAA;KAAE,CAAA;CAAE,GACtE,uBAAuB,EAAE,CAsG3B"}
1
+ {"version":3,"file":"returns.d.ts","sourceRoot":"","sources":["../../src/routes/returns.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AAC7E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAE7D,wBAAgB,iBAAiB,CAC/B,aAAa,EAAE,aAAa,EAC5B,kBAAkB,EAAE,kBAAkB,EACtC,cAAc,EAAE,cAAc,EAC9B,WAAW,EAAE;IAAE,MAAM,EAAE,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,EAAE,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE;YAAE,EAAE,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC,CAAA;CAAE,EAC/J,GAAG,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,QAAQ,CAAC,EAAE;QAAE,EAAE,EAAE,OAAO,CAAA;KAAE,CAAA;CAAE,GACtE,uBAAuB,EAAE,CAgJ3B"}
@@ -14,7 +14,8 @@ export function buildReturnRoutes(returnService, transactionService, paymentServ
14
14
  quantity: z.number().int().positive(),
15
15
  reason: z.enum(["defective", "wrong_item", "changed_mind", "other"]),
16
16
  restockingFee: z.number().int().min(0).optional(),
17
- refundAmount: z.number().int().positive(),
17
+ // refundAmount is NOT accepted from the client — it is computed
18
+ // server-side from the original order (SEC-08).
18
19
  })).min(1),
19
20
  }))
20
21
  .handler(async ({ input, actor, orgId }) => {
@@ -34,15 +35,35 @@ export function buildReturnRoutes(returnService, transactionService, paymentServ
34
35
  });
35
36
  if (!txnResult.ok)
36
37
  throw new Error(txnResult.error);
37
- // Record return items
38
+ // SEC-08: derive the refund server-side via orders.refundLines (the same
39
+ // primitive the exchange flow uses). It validates the order + line items
40
+ // belong to this org, enforces the per-line refundable quantity
41
+ // (quantity - refundedQuantity) and updates that ledger — so a fabricated
42
+ // or foreign order, an over-refund, or a repeat refund of the same units
43
+ // is rejected — and computes the amount from the recorded sale price. The
44
+ // client can no longer set the refund amount.
45
+ const orders = ctx.services?.orders;
46
+ if (!orders?.refundLines) {
47
+ throw new Error("Orders service unavailable for return refund");
48
+ }
49
+ const refundResult = await orders.refundLines(body.originalOrderId, {
50
+ lines: body.items.map((i) => ({ lineItemId: i.originalLineItemId, quantity: i.quantity })),
51
+ reason: "pos_return",
52
+ }, actor);
53
+ if (!refundResult.ok || !refundResult.value) {
54
+ throw new Error(refundResult.error?.message ??
55
+ "Return could not be validated against the original order.");
56
+ }
57
+ const serverAmountByLine = new Map(refundResult.value.refund.lines.map((l) => [l.lineItemId, l.amount]));
58
+ const refundTotal = refundResult.value.refund.amount;
59
+ // Record return items with the SERVER-computed refund amounts
38
60
  const itemsResult = await returnService.addReturnItems(txnResult.value.id, body.items.map((item) => ({
39
61
  ...item,
40
62
  originalOrderId: body.originalOrderId,
63
+ refundAmount: serverAmountByLine.get(item.originalLineItemId) ?? 0,
41
64
  })));
42
65
  if (!itemsResult.ok)
43
66
  throw new Error(itemsResult.error);
44
- // Update transaction totals
45
- const refundTotal = body.items.reduce((sum, i) => sum + i.refundAmount, 0);
46
67
  await transactionService.updateTotals(txnResult.value.id, {
47
68
  subtotal: refundTotal,
48
69
  taxTotal: 0,
package/dist/schema.d.ts CHANGED
@@ -1300,6 +1300,115 @@ export declare const posOperatorPins: import("drizzle-orm/pg-core/table").PgTabl
1300
1300
  };
1301
1301
  dialect: "pg";
1302
1302
  }>;
1303
+ export declare const posPinAttempts: import("drizzle-orm/pg-core/table").PgTableWithColumns<{
1304
+ name: "pos_pin_attempts";
1305
+ schema: undefined;
1306
+ columns: {
1307
+ id: import("@porulle/core/drizzle").PgColumn<{
1308
+ name: "id";
1309
+ tableName: "pos_pin_attempts";
1310
+ dataType: "string";
1311
+ columnType: "PgUUID";
1312
+ data: string;
1313
+ driverParam: string;
1314
+ notNull: true;
1315
+ hasDefault: true;
1316
+ isPrimaryKey: true;
1317
+ isAutoincrement: false;
1318
+ hasRuntimeDefault: false;
1319
+ enumValues: undefined;
1320
+ baseColumn: never;
1321
+ identity: undefined;
1322
+ generated: undefined;
1323
+ }, {}, {}>;
1324
+ organizationId: import("@porulle/core/drizzle").PgColumn<{
1325
+ name: "organization_id";
1326
+ tableName: "pos_pin_attempts";
1327
+ dataType: "string";
1328
+ columnType: "PgText";
1329
+ data: string;
1330
+ driverParam: string;
1331
+ notNull: true;
1332
+ hasDefault: false;
1333
+ isPrimaryKey: false;
1334
+ isAutoincrement: false;
1335
+ hasRuntimeDefault: false;
1336
+ enumValues: [string, ...string[]];
1337
+ baseColumn: never;
1338
+ identity: undefined;
1339
+ generated: undefined;
1340
+ }, {}, {}>;
1341
+ operatorId: import("@porulle/core/drizzle").PgColumn<{
1342
+ name: "operator_id";
1343
+ tableName: "pos_pin_attempts";
1344
+ dataType: "string";
1345
+ columnType: "PgText";
1346
+ data: string;
1347
+ driverParam: string;
1348
+ notNull: true;
1349
+ hasDefault: false;
1350
+ isPrimaryKey: false;
1351
+ isAutoincrement: false;
1352
+ hasRuntimeDefault: false;
1353
+ enumValues: [string, ...string[]];
1354
+ baseColumn: never;
1355
+ identity: undefined;
1356
+ generated: undefined;
1357
+ }, {}, {}>;
1358
+ failedCount: import("@porulle/core/drizzle").PgColumn<{
1359
+ name: "failed_count";
1360
+ tableName: "pos_pin_attempts";
1361
+ dataType: "number";
1362
+ columnType: "PgInteger";
1363
+ data: number;
1364
+ driverParam: string | number;
1365
+ notNull: true;
1366
+ hasDefault: true;
1367
+ isPrimaryKey: false;
1368
+ isAutoincrement: false;
1369
+ hasRuntimeDefault: false;
1370
+ enumValues: undefined;
1371
+ baseColumn: never;
1372
+ identity: undefined;
1373
+ generated: undefined;
1374
+ }, {}, {}>;
1375
+ lockedUntil: import("@porulle/core/drizzle").PgColumn<{
1376
+ name: "locked_until";
1377
+ tableName: "pos_pin_attempts";
1378
+ dataType: "date";
1379
+ columnType: "PgTimestamp";
1380
+ data: Date;
1381
+ driverParam: string;
1382
+ notNull: false;
1383
+ hasDefault: false;
1384
+ isPrimaryKey: false;
1385
+ isAutoincrement: false;
1386
+ hasRuntimeDefault: false;
1387
+ enumValues: undefined;
1388
+ baseColumn: never;
1389
+ identity: undefined;
1390
+ generated: undefined;
1391
+ }, {}, {}>;
1392
+ updatedAt: import("@porulle/core/drizzle").PgColumn<{
1393
+ name: "updated_at";
1394
+ tableName: "pos_pin_attempts";
1395
+ dataType: "date";
1396
+ columnType: "PgTimestamp";
1397
+ data: Date;
1398
+ driverParam: string;
1399
+ notNull: true;
1400
+ hasDefault: true;
1401
+ isPrimaryKey: false;
1402
+ isAutoincrement: false;
1403
+ hasRuntimeDefault: false;
1404
+ enumValues: undefined;
1405
+ baseColumn: never;
1406
+ identity: undefined;
1407
+ generated: undefined;
1408
+ }, {}, {}>;
1409
+ };
1410
+ dialect: "pg";
1411
+ }>;
1303
1412
  export declare const posReturnItems: import("drizzle-orm/pg-core/table").PgTableWithColumns<{
1304
1413
  name: "pos_return_items";
1305
1414
  schema: undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAMH,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAatB,CAAC;AAIJ,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsBnB,CAAC;AAIJ,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAWvB,CAAC;AAIJ,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA2BzB,CAAC;AAIJ,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAarB,CAAC;AAIJ,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAYzB,CAAC;AAIJ,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAaxB,CAAC"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAMH,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAatB,CAAC;AAIJ,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsBnB,CAAC;AAIJ,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAWvB,CAAC;AAIJ,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA2BzB,CAAC;AAIJ,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAarB,CAAC;AAIJ,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAYzB,CAAC;AAIJ,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EASxB,CAAC;AAIJ,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAaxB,CAAC"}
package/dist/schema.js CHANGED
@@ -120,6 +120,17 @@ export const posOperatorPins = pgTable("pos_operator_pins", {
120
120
  }, (table) => ({
121
121
  orgOperatorUnique: uniqueIndex("pos_operator_pins_org_operator_unique").on(table.organizationId, table.operatorId),
122
122
  }));
123
+ // ─── PIN attempt lockout (SEC-15) ───────────────────────────────────────
124
+ export const posPinAttempts = pgTable("pos_pin_attempts", {
125
+ id: uuid("id").defaultRandom().primaryKey(),
126
+ organizationId: text("organization_id").notNull(),
127
+ operatorId: text("operator_id").notNull(),
128
+ failedCount: integer("failed_count").notNull().default(0),
129
+ lockedUntil: timestamp("locked_until", { withTimezone: true }),
130
+ updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow().notNull(),
131
+ }, (table) => ({
132
+ orgOperatorUnique: uniqueIndex("pos_pin_attempts_org_operator_unique").on(table.organizationId, table.operatorId),
133
+ }));
123
134
  // ─── Return Items ───────────────────────────────────────────────────────
124
135
  export const posReturnItems = pgTable("pos_return_items", {
125
136
  id: uuid("id").defaultRandom().primaryKey(),
@@ -1 +1 @@
1
- {"version":3,"file":"exchange-service.d.ts","sourceRoot":"","sources":["../../src/services/exchange-service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,OAAO,KAAK,EAAE,EAAE,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAEnE;;;;;;;;;;GAUG;AAEH,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,WAAW,EAAE,KAAK,CAAC;QACjB,kBAAkB,EAAE,MAAM,CAAC;QAC3B,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,WAAW,GAAG,YAAY,GAAG,cAAc,GAAG,OAAO,CAAC;KAC/D,CAAC,CAAC;IACH,gBAAgB,EAAE,KAAK,CAAC;QACtB,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC/B,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QACzB,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;KAChC,CAAC,CAAC;CACJ;AAgBD,qBAAa,eAAe;IAExB,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,kBAAkB;IAC1B,OAAO,CAAC,aAAa;gBAJb,EAAE,EAAE,EAAE,EACN,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,WAAW,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,EAC1D,kBAAkB,EAAE,kBAAkB,EACtC,aAAa,EAAE,aAAa;IAGhC,QAAQ,CACZ,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,aAAa,EACpB,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAClD,OAAO,CAAC,YAAY,CAAC;QACtB,WAAW,EAAE,WAAW,CAAC;QACzB,QAAQ,EAAE,MAAM,CAAC;QACjB,WAAW,EAAE,MAAM,CAAC;QACpB,kBAAkB,EAAE,MAAM,CAAC;QAC3B,gBAAgB,EAAE,MAAM,CAAC;QACzB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC,CAAC;CAsJJ"}
1
+ {"version":3,"file":"exchange-service.d.ts","sourceRoot":"","sources":["../../src/services/exchange-service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,OAAO,KAAK,EAAE,EAAE,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAEnE;;;;;;;;;;GAUG;AAEH,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,WAAW,EAAE,KAAK,CAAC;QACjB,kBAAkB,EAAE,MAAM,CAAC;QAC3B,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,WAAW,GAAG,YAAY,GAAG,cAAc,GAAG,OAAO,CAAC;KAC/D,CAAC,CAAC;IACH,gBAAgB,EAAE,KAAK,CAAC;QACtB,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC/B,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QACzB,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;KAChC,CAAC,CAAC;CACJ;AA8BD,qBAAa,eAAe;IAExB,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,kBAAkB;IAC1B,OAAO,CAAC,aAAa;gBAJb,EAAE,EAAE,EAAE,EACN,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,WAAW,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,EAC1D,kBAAkB,EAAE,kBAAkB,EACtC,aAAa,EAAE,aAAa;IAGhC,QAAQ,CACZ,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,aAAa,EACpB,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAClD,OAAO,CAAC,YAAY,CAAC;QACtB,WAAW,EAAE,WAAW,CAAC;QACzB,QAAQ,EAAE,MAAM,CAAC;QACjB,WAAW,EAAE,MAAM,CAAC;QACpB,kBAAkB,EAAE,MAAM,CAAC;QAC3B,gBAAgB,EAAE,MAAM,CAAC;QACzB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC,CAAC;CAqLJ"}
@@ -20,13 +20,27 @@ export class ExchangeService {
20
20
  if (input.replacementItems.length === 0)
21
21
  return Err("At least one replacement item is required");
22
22
  const orders = this.services.orders;
23
+ const pricing = this.services.pricing;
23
24
  const currency = input.currency ?? "USD";
24
- const subtotal = input.replacementItems.reduce((sum, i) => sum + i.unitPrice * i.quantity, 0);
25
- const taxTotal = input.replacementItems.reduce((sum, i) => sum + (i.taxAmount ?? 0), 0);
26
- const replacementTotal = subtotal + taxTotal;
27
25
  // Atomic core half: refund the returned lines + create the replacement.
28
26
  const core = await this.transaction(async (tx) => {
29
27
  const txCtx = { tx, actor };
28
+ const pricedReplacements = [];
29
+ for (const item of input.replacementItems) {
30
+ const resolved = await pricing.resolve({
31
+ entityId: item.entityId,
32
+ currency,
33
+ quantity: item.quantity,
34
+ ...(item.variantId ? { variantId: item.variantId } : {}),
35
+ }, actor, txCtx);
36
+ if (!resolved.ok || !resolved.value) {
37
+ throw new Error(resolved.error?.message ?? `Cannot resolve price for replacement item ${item.entityId}`);
38
+ }
39
+ pricedReplacements.push({ ...item, unitPrice: resolved.value.finalAmount });
40
+ }
41
+ const subtotal = pricedReplacements.reduce((sum, i) => sum + i.unitPrice * i.quantity, 0);
42
+ const taxTotal = pricedReplacements.reduce((sum, i) => sum + (i.taxAmount ?? 0), 0);
43
+ const replacementTotal = subtotal + taxTotal;
30
44
  const refund = await orders.refundLines(input.originalOrderId, {
31
45
  lines: input.returnItems.map((item) => ({
32
46
  lineItemId: item.originalLineItemId,
@@ -50,7 +64,7 @@ export class ExchangeService {
50
64
  refundId: refund.value.refund.id,
51
65
  },
52
66
  },
53
- lineItems: input.replacementItems.map((item) => ({
67
+ lineItems: pricedReplacements.map((item) => ({
54
68
  entityId: item.entityId,
55
69
  entityType: "product",
56
70
  ...(item.variantId ? { variantId: item.variantId } : {}),
@@ -61,16 +75,25 @@ export class ExchangeService {
61
75
  totalPrice: item.unitPrice * item.quantity,
62
76
  taxAmount: item.taxAmount ?? 0,
63
77
  })),
64
- }, actor, txCtx);
78
+ }, actor, txCtx,
79
+ // Prices are server-resolved above (pricing.resolve); reserve stock for
80
+ // the replacement items so an exchange cannot oversell.
81
+ { trustedPricing: true, stockPolicy: "reserve" });
65
82
  if (!replacement.ok || !replacement.value) {
66
83
  throw new Error(replacement.error?.message ?? "Replacement order creation failed");
67
84
  }
68
- return { refund: refund.value.refund, replacementOrder: replacement.value };
85
+ return {
86
+ refund: refund.value.refund,
87
+ replacementOrder: replacement.value,
88
+ subtotal,
89
+ taxTotal,
90
+ replacementTotal,
91
+ };
69
92
  }).catch((error) => error);
70
93
  if (core instanceof Error)
71
94
  return Err(core.message);
72
95
  const returnTotal = core.refund.amount;
73
- const netDelta = replacementTotal - returnTotal;
96
+ const netDelta = core.replacementTotal - returnTotal;
74
97
  // POS bookkeeping (post-commit): cart + exchange transaction + return rows.
75
98
  const cart = this.services.cart;
76
99
  const cartResult = await cart.create({ currency, metadata: { posExchange: true, originalOrderId: input.originalOrderId } }, actor);
@@ -95,8 +118,8 @@ export class ExchangeService {
95
118
  refundAmount: core.refund.lines.find((l) => l.lineItemId === item.originalLineItemId)?.amount ?? 0,
96
119
  })));
97
120
  await this.transactionService.updateTotals(txn.id, {
98
- subtotal,
99
- taxTotal,
121
+ subtotal: core.subtotal,
122
+ taxTotal: core.taxTotal,
100
123
  total: netDelta,
101
124
  discountTotal: 0,
102
125
  });
@@ -111,7 +134,7 @@ export class ExchangeService {
111
134
  refundId: core.refund.id,
112
135
  replacementOrderId: core.replacementOrder.id,
113
136
  returnTotal,
114
- replacementTotal,
137
+ replacementTotal: core.replacementTotal,
115
138
  netDelta,
116
139
  },
117
140
  },
@@ -138,7 +161,7 @@ export class ExchangeService {
138
161
  refundId: core.refund.id,
139
162
  returnTotal,
140
163
  replacementOrderId: core.replacementOrder.id,
141
- replacementTotal,
164
+ replacementTotal: core.replacementTotal,
142
165
  netDelta,
143
166
  });
144
167
  }
@@ -1,5 +1,6 @@
1
1
  import type { PluginResult } from "@porulle/core";
2
2
  import type { Db } from "../types.js";
3
+ export declare const PIN_LOCKOUT_ERROR = "Too many failed PIN attempts; try again later";
3
4
  /** Narrow Better Auth surface the PIN runtime uses. */
4
5
  export interface PinAuthApi {
5
6
  api: {
@@ -18,7 +19,17 @@ export declare class PinService {
18
19
  apiKeyScope?: string | undefined;
19
20
  /** Shift-credential lifetime in seconds. Default: 12h. */
20
21
  credentialTtlSeconds?: number | undefined;
22
+ /** Failed PIN attempts before lockout (SEC-15). Default: 5. */
23
+ lockoutMaxAttempts?: number | undefined;
24
+ /** Failure window and lockout duration in minutes (SEC-15). Default: 15. */
25
+ lockoutWindowMinutes?: number | undefined;
21
26
  });
27
+ private lockoutMaxAttempts;
28
+ private lockoutWindowMs;
29
+ private getPinAttempt;
30
+ private isLockedOut;
31
+ private recordFailedPinAttempt;
32
+ private clearPinAttempts;
22
33
  setPin(orgId: string, input: {
23
34
  operatorId: string;
24
35
  pin: string;
@@ -1 +1 @@
1
- {"version":3,"file":"pin-service.d.ts","sourceRoot":"","sources":["../../src/services/pin-service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,OAAO,KAAK,EAAE,EAAE,EAAsB,MAAM,aAAa,CAAC;AAe1D,uDAAuD;AACvD,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE;QACH,YAAY,CAAC,KAAK,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;SAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;KAC1E,CAAC;CACH;AA+BD,wBAAsB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAI1D;AAED,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CASlF;AAED,qBAAa,UAAU;IAEnB,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,OAAO;gBADP,EAAE,EAAE,EAAE,EACN,OAAO,GAAE;QACf,yEAAyE;QACzE,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QACjC,0DAA0D;QAC1D,oBAAoB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;KACtC;IAGF,MAAM,CACV,KAAK,EAAE,MAAM,EACb,KAAK,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;KAAE,GAC5E,OAAO,CAAC,YAAY,CAAC;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;YAgCxD,iBAAiB;IAiB/B;;;;OAIG;IACG,QAAQ,CACZ,KAAK,EAAE,MAAM,EACb,KAAK,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,EACxE,IAAI,EAAE,UAAU,GAAG,SAAS,GAC3B,OAAO,CAAC,YAAY,CAAC;QACtB,UAAU,EAAE,MAAM,CAAC;QACnB,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC,CAAC;IAsDH;;;OAGG;IACG,QAAQ,CACZ,KAAK,EAAE,MAAM,EACb,KAAK,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GACzD,OAAO,CAAC,YAAY,CAAC;QACtB,QAAQ,EAAE,IAAI,CAAC;QACf,UAAU,EAAE,MAAM,CAAC;QACnB,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC,CAAC;CAWJ"}
1
+ {"version":3,"file":"pin-service.d.ts","sourceRoot":"","sources":["../../src/services/pin-service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAGlD,OAAO,KAAK,EAAE,EAAE,EAAkC,MAAM,aAAa,CAAC;AAgBtE,eAAO,MAAM,iBAAiB,kDAAkD,CAAC;AAOjF,uDAAuD;AACvD,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE;QACH,YAAY,CAAC,KAAK,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;SAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;KAC1E,CAAC;CACH;AA+BD,wBAAsB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAI1D;AAED,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CASlF;AAmBD,qBAAa,UAAU;IAEnB,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,OAAO;gBADP,EAAE,EAAE,EAAE,EACN,OAAO,GAAE;QACf,yEAAyE;QACzE,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QACjC,0DAA0D;QAC1D,oBAAoB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC1C,+DAA+D;QAC/D,kBAAkB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QACxC,4EAA4E;QAC5E,oBAAoB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;KACtC;IAGR,OAAO,CAAC,kBAAkB;IAI1B,OAAO,CAAC,eAAe;YAKT,aAAa;IAc3B,OAAO,CAAC,WAAW;YAIL,sBAAsB;YA+BtB,gBAAgB;IASxB,MAAM,CACV,KAAK,EAAE,MAAM,EACb,KAAK,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;KAAE,GAC5E,OAAO,CAAC,YAAY,CAAC;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;YAgCxD,iBAAiB;IA+B/B;;;;OAIG;IACG,QAAQ,CACZ,KAAK,EAAE,MAAM,EACb,KAAK,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,EACxE,IAAI,EAAE,UAAU,GAAG,SAAS,GAC3B,OAAO,CAAC,YAAY,CAAC;QACtB,UAAU,EAAE,MAAM,CAAC;QACnB,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC,CAAC;IAgEH;;;OAGG;IACG,QAAQ,CACZ,KAAK,EAAE,MAAM,EACb,KAAK,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GACzD,OAAO,CAAC,YAAY,CAAC;QACtB,QAAQ,EAAE,IAAI,CAAC;QACf,UAAU,EAAE,MAAM,CAAC;QACnB,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC,CAAC;CAYJ"}