@teincfood/core 0.1.1 → 0.1.3

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 (72) hide show
  1. package/dist/adapters/auth.js +2 -6
  2. package/dist/adapters/business.js +2 -6
  3. package/dist/adapters/connectivity.js +6 -14
  4. package/dist/adapters/devices.js +2 -6
  5. package/dist/adapters/http.js +4 -10
  6. package/dist/adapters/image-cache.d.ts +9 -3
  7. package/dist/adapters/image-cache.js +12 -8
  8. package/dist/adapters/kv-types.js +1 -2
  9. package/dist/adapters/kv.js +11 -24
  10. package/dist/adapters/local-node.js +3 -7
  11. package/dist/adapters/pending-orders.js +1 -4
  12. package/dist/adapters/query-keys.js +4 -7
  13. package/dist/adapters/services.js +9 -14
  14. package/dist/adapters/sqlite.js +3 -8
  15. package/dist/db/connection.js +15 -20
  16. package/dist/db/migrations.js +1 -4
  17. package/dist/db/operations.js +19 -36
  18. package/dist/index.d.ts +3 -0
  19. package/dist/index.js +43 -93
  20. package/dist/reference/api.js +7 -12
  21. package/dist/reference/events.js +25 -33
  22. package/dist/reference/operations.js +6 -14
  23. package/dist/reference/service.js +36 -39
  24. package/dist/reference/types.js +1 -2
  25. package/dist/repositories/order.repository.js +15 -18
  26. package/dist/repositories/pos.repository.js +6 -9
  27. package/dist/sync/command-builder.js +8 -12
  28. package/dist/sync/command-queue.service.js +35 -38
  29. package/dist/sync/engine.js +38 -42
  30. package/dist/sync/order-number.js +6 -11
  31. package/dist/sync/order-number.test.d.ts +1 -0
  32. package/dist/sync/order-number.test.js +30 -0
  33. package/dist/sync/order-snapshots.service.js +19 -22
  34. package/dist/sync/pending-orders.service.js +27 -33
  35. package/dist/sync/transport-local-node.js +16 -20
  36. package/dist/sync/transport-rest.js +18 -22
  37. package/dist/sync/types.js +1 -2
  38. package/dist/sync/use-sync-reconciliation.js +37 -40
  39. package/dist/types/api/auth.api.js +1 -2
  40. package/dist/types/api/business.api.js +1 -2
  41. package/dist/types/api/conversation.api.js +1 -2
  42. package/dist/types/api/error.api.js +1 -2
  43. package/dist/types/api/fleet.api.js +1 -2
  44. package/dist/types/api/index.js +11 -27
  45. package/dist/types/api/kiosk.api.js +1 -2
  46. package/dist/types/api/menu.api.js +1 -2
  47. package/dist/types/api/pagination.js +1 -2
  48. package/dist/types/api/rider.api.js +1 -2
  49. package/dist/types/api/seller.api.js +1 -2
  50. package/dist/types/api/wallet.api.js +1 -2
  51. package/dist/types/pos.types.js +1 -2
  52. package/dist/utils/constants.js +24 -27
  53. package/dist/utils/copyToClipBoard.js +2 -7
  54. package/dist/utils/currency.js +8 -16
  55. package/dist/utils/currency.test.d.ts +1 -0
  56. package/dist/utils/currency.test.js +17 -0
  57. package/dist/utils/error.js +4 -11
  58. package/dist/utils/formatCurrency.js +1 -4
  59. package/dist/utils/formatDate.js +2 -6
  60. package/dist/utils/phone.js +6 -13
  61. package/dist/utils/phone.test.d.ts +1 -0
  62. package/dist/utils/phone.test.js +19 -0
  63. package/dist/utils/responsive.js +1 -4
  64. package/dist/utils/storage.js +14 -28
  65. package/dist/utils/sync-logger.js +1 -4
  66. package/dist/utils/uuid.js +4 -10
  67. package/dist/utils/uuid.test.d.ts +1 -0
  68. package/dist/utils/uuid.test.js +13 -0
  69. package/dist/utils/validation.js +11 -24
  70. package/dist/utils/validation.test.d.ts +1 -0
  71. package/dist/utils/validation.test.js +23 -0
  72. package/package.json +16 -1
@@ -1,11 +1,4 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PHONE_COUNTRIES = void 0;
4
- exports.getDialCodeFromCountry = getDialCodeFromCountry;
5
- exports.getCountryOption = getCountryOption;
6
- exports.normalizePhoneForCountry = normalizePhoneForCountry;
7
- exports.isLikelyE164 = isLikelyE164;
8
- exports.PHONE_COUNTRIES = [
1
+ export const PHONE_COUNTRIES = [
9
2
  { iso: 'GH', name: 'Ghana', dialCode: '+233' },
10
3
  { iso: 'NG', name: 'Nigeria', dialCode: '+234' },
11
4
  { iso: 'KE', name: 'Kenya', dialCode: '+254' },
@@ -40,17 +33,17 @@ const COUNTRY_DIAL_CODE_BY_ISO = {
40
33
  function digitsOnly(value) {
41
34
  return value.replace(/\D/g, '');
42
35
  }
43
- function getDialCodeFromCountry(countryCode) {
36
+ export function getDialCodeFromCountry(countryCode) {
44
37
  if (!countryCode)
45
38
  return null;
46
39
  return COUNTRY_DIAL_CODE_BY_ISO[countryCode.trim().toUpperCase()] || null;
47
40
  }
48
- function getCountryOption(iso) {
41
+ export function getCountryOption(iso) {
49
42
  if (!iso)
50
43
  return null;
51
- return exports.PHONE_COUNTRIES.find((country) => country.iso === iso.trim().toUpperCase()) || null;
44
+ return PHONE_COUNTRIES.find((country) => country.iso === iso.trim().toUpperCase()) || null;
52
45
  }
53
- function normalizePhoneForCountry(rawPhone, countryCode) {
46
+ export function normalizePhoneForCountry(rawPhone, countryCode) {
54
47
  const input = rawPhone.trim();
55
48
  if (!input)
56
49
  return null;
@@ -66,6 +59,6 @@ function normalizePhoneForCountry(rawPhone, countryCode) {
66
59
  return null;
67
60
  return `${dialCode}${localDigits}`;
68
61
  }
69
- function isLikelyE164(phone) {
62
+ export function isLikelyE164(phone) {
70
63
  return /^\+[1-9]\d{7,14}$/.test(phone);
71
64
  }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,19 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { normalizePhoneForCountry, isLikelyE164, getDialCodeFromCountry } from "./phone";
3
+ describe("phone", () => {
4
+ it("normalizes GH numbers", () => {
5
+ expect(normalizePhoneForCountry("0501234567", "GH")).toBe("+233501234567");
6
+ expect(normalizePhoneForCountry("+233501234567", "GH")).toBe("+233501234567");
7
+ expect(normalizePhoneForCountry(" ", "GH")).toBeNull();
8
+ });
9
+ it("validates E.164", () => {
10
+ expect(isLikelyE164("+233501234567")).toBe(true);
11
+ expect(isLikelyE164("0501234567")).toBe(false);
12
+ expect(isLikelyE164("+1abc")).toBe(false);
13
+ });
14
+ it("resolves dial codes", () => {
15
+ expect(getDialCodeFromCountry("GH")).toBe("+233");
16
+ expect(getDialCodeFromCountry("US")).toBe("+1");
17
+ expect(getDialCodeFromCountry("XX")).toBeNull();
18
+ });
19
+ });
@@ -1,17 +1,14 @@
1
- "use strict";
2
1
  /**
3
2
  * Responsive helper — plain, no react-native.
4
3
  * Host apps with RN can override via adapter; core provides window-based fallback.
5
4
  */
6
- Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.useResponsive = useResponsive;
8
5
  function getWindowSize() {
9
6
  if (typeof window !== "undefined") {
10
7
  return { width: window.innerWidth, height: window.innerHeight };
11
8
  }
12
9
  return { width: 1024, height: 768 };
13
10
  }
14
- function useResponsive() {
11
+ export function useResponsive() {
15
12
  const { width, height } = getWindowSize();
16
13
  const isTablet = width >= 768;
17
14
  const isLandscape = width > height;
@@ -1,48 +1,34 @@
1
- "use strict";
2
1
  /**
3
2
  * Storage facade — re-exports KV adapter.
4
3
  * Provides async JSON helpers and a zustand-compatible storage shim.
5
4
  */
6
- Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.zustandStorage = exports.mmkvStorage = exports.getKVDriver = exports.setKVDriver = exports.kvRemoveItem = exports.kvSetItem = exports.kvGetItem = exports.removeItem = exports.setItem = exports.getItem = void 0;
8
- exports.getItemAsync = getItemAsync;
9
- exports.setItemAsync = setItemAsync;
10
- exports.removeItemAsync = removeItemAsync;
11
- var kv_1 = require("../adapters/kv");
12
- Object.defineProperty(exports, "getItem", { enumerable: true, get: function () { return kv_1.getItem; } });
13
- Object.defineProperty(exports, "setItem", { enumerable: true, get: function () { return kv_1.setItem; } });
14
- Object.defineProperty(exports, "removeItem", { enumerable: true, get: function () { return kv_1.removeItem; } });
15
- Object.defineProperty(exports, "kvGetItem", { enumerable: true, get: function () { return kv_1.kvGetItem; } });
16
- Object.defineProperty(exports, "kvSetItem", { enumerable: true, get: function () { return kv_1.kvSetItem; } });
17
- Object.defineProperty(exports, "kvRemoveItem", { enumerable: true, get: function () { return kv_1.kvRemoveItem; } });
18
- Object.defineProperty(exports, "setKVDriver", { enumerable: true, get: function () { return kv_1.setKVDriver; } });
19
- Object.defineProperty(exports, "getKVDriver", { enumerable: true, get: function () { return kv_1.getKVDriver; } });
20
- const kv_2 = require("../adapters/kv");
21
- const kv_3 = require("../adapters/kv");
5
+ export { getItem, setItem, removeItem, kvGetItem, kvSetItem, kvRemoveItem, setKVDriver, getKVDriver, } from "../adapters/kv";
6
+ import { kvGetItem, kvSetItem, kvRemoveItem } from "../adapters/kv";
7
+ import { getItemSync, setItemSync, removeItemSync } from "../adapters/kv";
22
8
  // Keep mmkvStorage export for backwards compat (no-op)
23
- exports.mmkvStorage = {
9
+ export const mmkvStorage = {
24
10
  getString: (_key) => null,
25
11
  set: (_key, _value) => { },
26
12
  delete: (_key) => { },
27
13
  };
28
- exports.zustandStorage = {
14
+ export const zustandStorage = {
29
15
  getItem: (name) => {
30
- const v = (0, kv_3.getItemSync)(name);
16
+ const v = getItemSync(name);
31
17
  return v ? JSON.stringify(v) : null;
32
18
  },
33
19
  setItem: (name, value) => {
34
20
  try {
35
- (0, kv_3.setItemSync)(name, JSON.parse(value));
21
+ setItemSync(name, JSON.parse(value));
36
22
  }
37
23
  catch { }
38
24
  },
39
25
  removeItem: (name) => {
40
- (0, kv_3.removeItemSync)(name);
26
+ removeItemSync(name);
41
27
  },
42
28
  };
43
29
  // Async helpers for code that expects promise-based storage (used by order-number, etc.)
44
- async function getItemAsync(key) {
45
- const raw = await (0, kv_2.kvGetItem)(key);
30
+ export async function getItemAsync(key) {
31
+ const raw = await kvGetItem(key);
46
32
  if (raw == null)
47
33
  return null;
48
34
  try {
@@ -52,9 +38,9 @@ async function getItemAsync(key) {
52
38
  return null;
53
39
  }
54
40
  }
55
- async function setItemAsync(key, value) {
56
- await (0, kv_2.kvSetItem)(key, JSON.stringify(value));
41
+ export async function setItemAsync(key, value) {
42
+ await kvSetItem(key, JSON.stringify(value));
57
43
  }
58
- async function removeItemAsync(key) {
59
- await (0, kv_2.kvRemoveItem)(key);
44
+ export async function removeItemAsync(key) {
45
+ await kvRemoveItem(key);
60
46
  }
@@ -1,12 +1,9 @@
1
- "use strict";
2
1
  /**
3
2
  * Structured logging for the Sync Engine, commands, events, and transports.
4
3
  *
5
4
  * Logs are sent to console.debug in debug builds. In production this can be
6
5
  * swapped for a crashlytics/analytics sink without changing call sites.
7
6
  */
8
- Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.syncLogger = void 0;
10
7
  function emit(entry) {
11
8
  const { level, namespace, message, data } = entry;
12
9
  const tag = `[${namespace}] ${message}`;
@@ -76,4 +73,4 @@ class SyncLogger {
76
73
  });
77
74
  }
78
75
  }
79
- exports.syncLogger = new SyncLogger();
76
+ export const syncLogger = new SyncLogger();
@@ -1,12 +1,6 @@
1
- "use strict";
2
1
  /**
3
2
  * Client-side UUID generation — plain JS, no expo-crypto.
4
3
  */
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.generateUUID = generateUUID;
7
- exports.generateCommandId = generateCommandId;
8
- exports.generateEventId = generateEventId;
9
- exports.generateEntityId = generateEntityId;
10
4
  function randomHex(bytes) {
11
5
  const arr = new Uint8Array(bytes);
12
6
  if (typeof crypto !== "undefined" && crypto.getRandomValues) {
@@ -33,15 +27,15 @@ function uuidV4() {
33
27
  "-" +
34
28
  hex.slice(20, 32));
35
29
  }
36
- function generateUUID() {
30
+ export function generateUUID() {
37
31
  return uuidV4();
38
32
  }
39
- function generateCommandId() {
33
+ export function generateCommandId() {
40
34
  return `cmd_${uuidV4()}`;
41
35
  }
42
- function generateEventId() {
36
+ export function generateEventId() {
43
37
  return `evt_${uuidV4()}`;
44
38
  }
45
- function generateEntityId() {
39
+ export function generateEntityId() {
46
40
  return uuidV4();
47
41
  }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,13 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { generateUUID, generateCommandId, generateEventId } from "./uuid";
3
+ describe("uuid", () => {
4
+ it("generates valid v4 UUIDs", () => {
5
+ const id = generateUUID();
6
+ expect(id).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/);
7
+ });
8
+ it("generates prefixed ids", () => {
9
+ expect(generateCommandId()).toMatch(/^cmd_/);
10
+ expect(generateEventId()).toMatch(/^evt_/);
11
+ expect(generateCommandId()).not.toBe(generateEventId());
12
+ });
13
+ });
@@ -1,21 +1,8 @@
1
- "use strict";
2
1
  /**
3
2
  * Validation Utilities
4
3
  * Provides schema validation for REST/WS/SSE/push payloads
5
4
  */
6
- Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.validation = void 0;
8
- exports.validateEmail = validateEmail;
9
- exports.validatePhone = validatePhone;
10
- exports.validateUsername = validateUsername;
11
- exports.validatePassword = validatePassword;
12
- exports.validateOrderPayload = validateOrderPayload;
13
- exports.validateChatMessage = validateChatMessage;
14
- exports.validateEventEnvelope = validateEventEnvelope;
15
- exports.validatePromoCode = validatePromoCode;
16
- exports.validateAddress = validateAddress;
17
- exports.validatePaymentMethod = validatePaymentMethod;
18
- function validateEmail(email) {
5
+ export function validateEmail(email) {
19
6
  const errors = [];
20
7
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
21
8
  if (!email) {
@@ -26,7 +13,7 @@ function validateEmail(email) {
26
13
  }
27
14
  return { valid: errors.length === 0, errors };
28
15
  }
29
- function validatePhone(phone) {
16
+ export function validatePhone(phone) {
30
17
  const errors = [];
31
18
  const phoneRegex = /^\+?[1-9]\d{1,14}$/;
32
19
  if (!phone) {
@@ -37,7 +24,7 @@ function validatePhone(phone) {
37
24
  }
38
25
  return { valid: errors.length === 0, errors };
39
26
  }
40
- function validateUsername(username) {
27
+ export function validateUsername(username) {
41
28
  const errors = [];
42
29
  const usernameRegex = /^[a-zA-Z0-9_]{3,20}$/;
43
30
  if (!username) {
@@ -48,7 +35,7 @@ function validateUsername(username) {
48
35
  }
49
36
  return { valid: errors.length === 0, errors };
50
37
  }
51
- function validatePassword(password) {
38
+ export function validatePassword(password) {
52
39
  const errors = [];
53
40
  if (!password) {
54
41
  errors.push('Password is required');
@@ -69,7 +56,7 @@ function validatePassword(password) {
69
56
  }
70
57
  return { valid: errors.length === 0, errors };
71
58
  }
72
- function validateOrderPayload(payload) {
59
+ export function validateOrderPayload(payload) {
73
60
  const errors = [];
74
61
  if (!payload || typeof payload !== 'object') {
75
62
  errors.push('Order payload must be an object');
@@ -87,7 +74,7 @@ function validateOrderPayload(payload) {
87
74
  }
88
75
  return { valid: errors.length === 0, errors };
89
76
  }
90
- function validateChatMessage(payload) {
77
+ export function validateChatMessage(payload) {
91
78
  const errors = [];
92
79
  if (!payload || typeof payload !== 'object') {
93
80
  errors.push('Chat message payload must be an object');
@@ -105,7 +92,7 @@ function validateChatMessage(payload) {
105
92
  }
106
93
  return { valid: errors.length === 0, errors };
107
94
  }
108
- function validateEventEnvelope(payload) {
95
+ export function validateEventEnvelope(payload) {
109
96
  const errors = [];
110
97
  if (!payload || typeof payload !== 'object') {
111
98
  errors.push('Event envelope must be an object');
@@ -126,7 +113,7 @@ function validateEventEnvelope(payload) {
126
113
  }
127
114
  return { valid: errors.length === 0, errors };
128
115
  }
129
- function validatePromoCode(code) {
116
+ export function validatePromoCode(code) {
130
117
  const errors = [];
131
118
  const promoRegex = /^[A-Z0-9]{3,20}$/;
132
119
  if (!code) {
@@ -137,7 +124,7 @@ function validatePromoCode(code) {
137
124
  }
138
125
  return { valid: errors.length === 0, errors };
139
126
  }
140
- function validateAddress(address) {
127
+ export function validateAddress(address) {
141
128
  const errors = [];
142
129
  if (!address.street || typeof address.street !== 'string') {
143
130
  errors.push('Street address is required');
@@ -150,7 +137,7 @@ function validateAddress(address) {
150
137
  }
151
138
  return { valid: errors.length === 0, errors };
152
139
  }
153
- function validatePaymentMethod(payment) {
140
+ export function validatePaymentMethod(payment) {
154
141
  const errors = [];
155
142
  if (!payment.type || !['card', 'mobile_money'].includes(payment.type)) {
156
143
  errors.push('Payment type must be card or mobile_money');
@@ -161,7 +148,7 @@ function validatePaymentMethod(payment) {
161
148
  return { valid: errors.length === 0, errors };
162
149
  }
163
150
  // ─── Convenience object for screen-level validation ───
164
- exports.validation = {
151
+ export const validation = {
165
152
  isNotEmpty: (value) => value.trim().length > 0,
166
153
  isValid: (value, validator) => validator(value).valid,
167
154
  email: Object.assign((v) => validateEmail(v), { message: "Invalid email format" }),
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,23 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { validateEmail, validatePhone, validatePassword, validatePromoCode } from "./validation";
3
+ describe("validation", () => {
4
+ it("validates email", () => {
5
+ expect(validateEmail("a@b.com").valid).toBe(true);
6
+ expect(validateEmail("bad").valid).toBe(false);
7
+ expect(validateEmail("").errors).toContain("Email is required");
8
+ });
9
+ it("validates phone", () => {
10
+ expect(validatePhone("+233501234567").valid).toBe(true);
11
+ expect(validatePhone("123").valid).toBe(true); // minimal valid per regex
12
+ expect(validatePhone("").valid).toBe(false);
13
+ });
14
+ it("validates password strength", () => {
15
+ expect(validatePassword("StrongPass1").valid).toBe(true);
16
+ expect(validatePassword("short").valid).toBe(false);
17
+ expect(validatePassword("nouppercase1").valid).toBe(false);
18
+ });
19
+ it("validates promo code", () => {
20
+ expect(validatePromoCode("SAVE10").valid).toBe(true);
21
+ expect(validatePromoCode("ab").valid).toBe(false);
22
+ });
23
+ });
package/package.json CHANGED
@@ -1,9 +1,24 @@
1
1
  {
2
2
  "name": "@teincfood/core",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "TeincFood shared offline-first core — types, sync engine, local DB, reference data, and repositories for mobile + desktop",
5
+ "type": "module",
5
6
  "main": "dist/index.js",
6
7
  "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ },
13
+ "./dist/*.js": {
14
+ "types": "./dist/*.d.ts",
15
+ "import": "./dist/*.js"
16
+ },
17
+ "./dist/*": {
18
+ "types": "./dist/*.d.ts",
19
+ "import": "./dist/*.js"
20
+ }
21
+ },
7
22
  "files": [
8
23
  "dist"
9
24
  ],