@teincfood/core 0.1.2 → 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.
@@ -1,8 +1,14 @@
1
1
  /**
2
2
  * Image cache stub — no-op in core library.
3
- * Host apps with file-system access inject real impl.
3
+ * Host apps with file-system access inject real impl via imageCacheService.
4
4
  */
5
- export declare function cacheImageBatch(_urls: (string | null | undefined)[], _opts?: {
5
+ export declare const imageCacheService: {
6
+ cacheImageBatch(urls: (string | null | undefined)[], opts?: {
7
+ skipIfCached?: boolean;
8
+ }): Promise<string[]>;
9
+ pruneImageCache(urls: (string | null | undefined)[]): Promise<void>;
10
+ };
11
+ export declare function cacheImageBatch(urls: (string | null | undefined)[], opts?: {
6
12
  skipIfCached?: boolean;
7
13
  }): Promise<string[]>;
8
- export declare function pruneImageCache(_urls: (string | null | undefined)[]): Promise<void>;
14
+ export declare function pruneImageCache(urls: (string | null | undefined)[]): Promise<void>;
@@ -1,8 +1,16 @@
1
1
  /**
2
2
  * Image cache stub — no-op in core library.
3
- * Host apps with file-system access inject real impl.
3
+ * Host apps with file-system access inject real impl via imageCacheService.
4
4
  */
5
- export async function cacheImageBatch(_urls, _opts) {
6
- return [];
5
+ export const imageCacheService = {
6
+ async cacheImageBatch() {
7
+ return [];
8
+ },
9
+ async pruneImageCache() { },
10
+ };
11
+ export async function cacheImageBatch(urls, opts) {
12
+ return imageCacheService.cacheImageBatch(urls, opts);
13
+ }
14
+ export async function pruneImageCache(urls) {
15
+ return imageCacheService.pruneImageCache(urls);
7
16
  }
8
- export async function pruneImageCache(_urls) { }
package/dist/index.d.ts CHANGED
@@ -30,6 +30,9 @@ export * from "./adapters/kv";
30
30
  export * from "./adapters/connectivity";
31
31
  export * from "./adapters/http";
32
32
  export * from "./adapters/local-node";
33
+ export * from "./adapters/services";
34
+ export * from "./adapters/devices";
35
+ export * from "./adapters/image-cache";
33
36
  import { type SqliteDriver } from "./adapters/sqlite";
34
37
  import { type KVDriver } from "./adapters/kv";
35
38
  import { type HttpClient } from "./adapters/http";
package/dist/index.js CHANGED
@@ -37,6 +37,9 @@ export * from "./adapters/kv";
37
37
  export * from "./adapters/connectivity";
38
38
  export * from "./adapters/http";
39
39
  export * from "./adapters/local-node";
40
+ export * from "./adapters/services";
41
+ export * from "./adapters/devices";
42
+ export * from "./adapters/image-cache";
40
43
  // ── Init helper ──
41
44
  import { setSqliteDriver } from "./adapters/sqlite";
42
45
  import { setKVDriver } from "./adapters/kv";
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,30 @@
1
+ import { describe, it, expect, beforeEach } from "vitest";
2
+ import { generateLocalOrderNumber, generateLocalCalloutNumber, isLocalOrderNumber } from "./order-number";
3
+ import { removeItemSync } from "../adapters/kv";
4
+ // Use in-memory KV for tests (default driver is memory)
5
+ describe("order-number", () => {
6
+ beforeEach(() => {
7
+ removeItemSync("local_order_sequence");
8
+ removeItemSync("local_callout_sequence");
9
+ });
10
+ it("generates TFL order numbers with date and sequence", () => {
11
+ const d = new Date("2026-08-24T10:00:00.000Z");
12
+ const a = generateLocalOrderNumber(d);
13
+ const b = generateLocalOrderNumber(d);
14
+ expect(a).toBe("TFL-20260824-0001");
15
+ expect(b).toBe("TFL-20260824-0002");
16
+ expect(isLocalOrderNumber(a)).toBe(true);
17
+ expect(isLocalOrderNumber("KF-260701-0042")).toBe(false);
18
+ });
19
+ it("generates incremental callout numbers per day", () => {
20
+ const d = new Date("2026-08-24T10:00:00.000Z");
21
+ expect(generateLocalCalloutNumber(d)).toBe(1);
22
+ expect(generateLocalCalloutNumber(d)).toBe(2);
23
+ });
24
+ it("isolates sequences per day", () => {
25
+ const d1 = new Date("2026-08-24T10:00:00.000Z");
26
+ const d2 = new Date("2026-08-25T10:00:00.000Z");
27
+ expect(generateLocalOrderNumber(d1)).toBe("TFL-20260824-0001");
28
+ expect(generateLocalOrderNumber(d2)).toBe("TFL-20260825-0001");
29
+ });
30
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,17 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { formatPrice, formatPriceFromMinor, getCurrencySymbol, DEFAULT_CURRENCY } from "./currency";
3
+ describe("currency", () => {
4
+ it("returns symbol for known currencies", () => {
5
+ expect(getCurrencySymbol("GHS")).toBe("GH₵");
6
+ expect(getCurrencySymbol("USD")).toBe("$");
7
+ expect(getCurrencySymbol("UNKNOWN")).toBe("UNKNOWN");
8
+ });
9
+ it("formats price with default currency", () => {
10
+ expect(formatPrice(10.5)).toBe(`${getCurrencySymbol(DEFAULT_CURRENCY)}10.50`);
11
+ });
12
+ it("formats price from minor units", () => {
13
+ expect(formatPriceFromMinor(1050, "GHS")).toBe("GH₵10.50");
14
+ expect(formatPriceFromMinor(100, "USD")).toBe("$1.00");
15
+ expect(formatPriceFromMinor(0)).toBe(`${getCurrencySymbol(DEFAULT_CURRENCY)}0.00`);
16
+ });
17
+ });
@@ -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
+ });
@@ -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
+ });
@@ -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,6 +1,6 @@
1
1
  {
2
2
  "name": "@teincfood/core",
3
- "version": "0.1.2",
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
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -10,6 +10,10 @@
10
10
  "types": "./dist/index.d.ts",
11
11
  "import": "./dist/index.js"
12
12
  },
13
+ "./dist/*.js": {
14
+ "types": "./dist/*.d.ts",
15
+ "import": "./dist/*.js"
16
+ },
13
17
  "./dist/*": {
14
18
  "types": "./dist/*.d.ts",
15
19
  "import": "./dist/*.js"