qumra-pos-storage 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,163 @@
1
+ // src/conformance/assert.ts
2
+ function assert(cond, msg) {
3
+ if (!cond) throw new Error(`conformance: ${msg}`);
4
+ }
5
+ function assertEqual(actual, expected, msg) {
6
+ const a = JSON.stringify(actual);
7
+ const e = JSON.stringify(expected);
8
+ if (a !== e) {
9
+ throw new Error(`conformance: ${msg} \u2014 \u0627\u0644\u0645\u062A\u0648\u0642\u0639 ${e} \u0644\u0643\u0646 \u062C\u0647 ${a}`);
10
+ }
11
+ }
12
+ function bytesEqual(a, b) {
13
+ if (a.length !== b.length) return false;
14
+ for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
15
+ return true;
16
+ }
17
+ async function assertThrows(fn, msg) {
18
+ let threw = false;
19
+ try {
20
+ await fn();
21
+ } catch {
22
+ threw = true;
23
+ }
24
+ assert(threw, msg);
25
+ }
26
+
27
+ // src/conformance/cases.ts
28
+ async function withAdapter(make, body) {
29
+ const adapter = await make();
30
+ try {
31
+ await body(adapter);
32
+ } finally {
33
+ await adapter.close();
34
+ }
35
+ }
36
+ var CONFORMANCE_CASES = [
37
+ {
38
+ name: "execute + query: \u0625\u0646\u0634\u0627\u0621 \u0648\u0625\u062F\u0631\u0627\u062C \u0648\u0642\u0631\u0627\u0621\u0629",
39
+ run: (make) => withAdapter(make, async (a) => {
40
+ await a.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT)");
41
+ await a.execute("INSERT INTO t (id, name) VALUES (?, ?)", [1, "\u0642\u0647\u0648\u0629"]);
42
+ const rows = await a.query(
43
+ "SELECT id, name FROM t"
44
+ );
45
+ assertEqual(rows, [{ id: 1, name: "\u0642\u0647\u0648\u0629" }], "\u0635\u0641 \u0648\u0627\u062D\u062F \u0628\u0627\u0644\u0642\u064A\u0645 \u0627\u0644\u0635\u062D");
46
+ })
47
+ },
48
+ {
49
+ name: "\u0627\u0644\u0645\u0639\u0627\u0645\u0644\u0627\u062A: string / number / null / blob \u062A\u062A\u0631\u0628\u0637 \u0648\u062A\u0631\u062C\u0639 \u0635\u062D",
50
+ run: (make) => withAdapter(make, async (a) => {
51
+ await a.execute(
52
+ "CREATE TABLE t (id INTEGER, s TEXT, n REAL, z TEXT, b BLOB)"
53
+ );
54
+ const blob = new Uint8Array([0, 255, 16, 200]);
55
+ await a.execute("INSERT INTO t (id, s, n, z, b) VALUES (?, ?, ?, ?, ?)", [
56
+ 7,
57
+ "\u0646\u0635",
58
+ 12.5,
59
+ null,
60
+ blob
61
+ ]);
62
+ const rows = await a.query("SELECT id, s, n, z, b FROM t WHERE id = ?", [7]);
63
+ assert(rows.length === 1, "\u0635\u0641 \u0648\u0627\u062D\u062F");
64
+ const r = rows[0];
65
+ assertEqual(
66
+ { id: r.id, s: r.s, n: r.n, z: r.z },
67
+ { id: 7, s: "\u0646\u0635", n: 12.5, z: null },
68
+ "\u0627\u0644\u0642\u064A\u0645 \u063A\u064A\u0631 \u0627\u0644\u0640 blob"
69
+ );
70
+ assert(
71
+ bytesEqual(new Uint8Array(r.b), blob),
72
+ "\u0627\u0644\u0640 blob \u064A\u0631\u062C\u0639 \u0628\u0646\u0641\u0633 \u0627\u0644\u0628\u0627\u064A\u062A\u0627\u062A"
73
+ );
74
+ })
75
+ },
76
+ {
77
+ name: "execute \u0645\u0646 \u063A\u064A\u0631 params \u064A\u0646\u0641\u0651\u0630 \u0623\u0643\u062A\u0631 \u0645\u0646 statement (DDL/migrations)",
78
+ run: (make) => withAdapter(make, async (a) => {
79
+ await a.execute(`
80
+ CREATE TABLE a (id INTEGER);
81
+ CREATE TABLE b (id INTEGER);
82
+ INSERT INTO a (id) VALUES (1);
83
+ INSERT INTO b (id) VALUES (2);
84
+ `);
85
+ const ra = await a.query("SELECT id FROM a");
86
+ const rb = await a.query("SELECT id FROM b");
87
+ assertEqual([ra, rb], [[{ id: 1 }], [{ id: 2 }]], "\u0627\u0644\u062C\u062F\u0648\u0644\u064A\u0646 \u0627\u062A\u0639\u0645\u0644\u0648\u0627");
88
+ })
89
+ },
90
+ {
91
+ name: "transaction \u0628\u062A\u0639\u0645\u0644 commit \u0639\u0646\u062F \u0627\u0644\u0646\u062C\u0627\u062D",
92
+ run: (make) => withAdapter(make, async (a) => {
93
+ await a.execute("CREATE TABLE t (id INTEGER)");
94
+ await a.transaction(async (tx) => {
95
+ await tx.execute("INSERT INTO t (id) VALUES (1)");
96
+ await tx.execute("INSERT INTO t (id) VALUES (2)");
97
+ });
98
+ const rows = await a.query("SELECT id FROM t ORDER BY id");
99
+ assertEqual(rows, [{ id: 1 }, { id: 2 }], "\u0627\u0644\u0635\u0641\u064A\u0646 \u0627\u062A\u062B\u0628\u062A\u0648\u0627 \u0628\u0639\u062F \u0627\u0644\u0640 commit");
100
+ })
101
+ },
102
+ {
103
+ name: "transaction \u0628\u062A\u0639\u0645\u0644 rollback \u0639\u0646\u062F \u0623\u064A throw (atomic) \u0648\u062A\u0639\u064A\u062F \u0627\u0644\u0631\u0645\u064A",
104
+ run: (make) => withAdapter(make, async (a) => {
105
+ await a.execute("CREATE TABLE t (id INTEGER)");
106
+ await assertThrows(
107
+ () => a.transaction(async (tx) => {
108
+ await tx.execute("INSERT INTO t (id) VALUES (1)");
109
+ throw new Error("\u0641\u0634\u0644 \u0645\u0642\u0635\u0648\u062F \u0648\u0633\u0637 \u0627\u0644\u0640 transaction");
110
+ }),
111
+ "\u0627\u0644\u0640 transaction \u0628\u062A\u0631\u0645\u064A \u0627\u0644\u062E\u0637\u0623 \u0628\u0631\u0647"
112
+ );
113
+ const rows = await a.query("SELECT id FROM t");
114
+ assertEqual(rows, [], "\u0645\u0641\u064A\u0634 \u0623\u064A \u0635\u0641 \u0627\u062A\u0643\u062A\u0628 \u2014 \u0627\u062A\u0639\u0645\u0644 rollback \u0644\u0644\u0643\u0644");
115
+ })
116
+ },
117
+ {
118
+ name: "transaction \u0628\u062A\u0631\u062C\u0651\u0639 \u0642\u064A\u0645\u0629 \u0627\u0644\u0640 callback",
119
+ run: (make) => withAdapter(make, async (a) => {
120
+ await a.execute("CREATE TABLE t (id INTEGER)");
121
+ const value = await a.transaction(async (tx) => {
122
+ await tx.execute("INSERT INTO t (id) VALUES (42)");
123
+ const rows = await tx.query("SELECT id FROM t");
124
+ return rows[0].id;
125
+ });
126
+ assertEqual(value, 42, "\u0627\u0644\u0642\u064A\u0645\u0629 \u0627\u0644\u0631\u0627\u062C\u0639\u0629 \u0645\u0646 \u0627\u0644\u0640 callback");
127
+ })
128
+ },
129
+ {
130
+ name: "read-your-writes: \u0627\u0644\u0640 tx \u0628\u064A\u0634\u0648\u0641 \u0643\u062A\u0627\u0628\u0627\u062A\u0647 \u0642\u0628\u0644 \u0627\u0644\u0640 commit",
131
+ run: (make) => withAdapter(make, async (a) => {
132
+ await a.execute("CREATE TABLE t (id INTEGER)");
133
+ await a.transaction(async (tx) => {
134
+ await tx.execute("INSERT INTO t (id) VALUES (5)");
135
+ const rows = await tx.query("SELECT id FROM t");
136
+ assertEqual(rows, [{ id: 5 }], "\u0627\u0644\u0627\u0633\u062A\u0639\u0644\u0627\u0645 \u062C\u0648\u0647 \u0627\u0644\u0640 tx \u0628\u064A\u0634\u0648\u0641 \u0627\u0644\u0625\u062F\u0631\u0627\u062C");
137
+ });
138
+ })
139
+ },
140
+ {
141
+ name: "INSERT OR REPLACE: upsert (\u064A\u0633\u062A\u062E\u062F\u0645\u0647 \u0627\u0644\u0640 pull)",
142
+ run: (make) => withAdapter(make, async (a) => {
143
+ await a.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, v TEXT)");
144
+ await a.execute("INSERT OR REPLACE INTO t (id, v) VALUES (?, ?)", [1, "\u0623\u0648\u0644"]);
145
+ await a.execute("INSERT OR REPLACE INTO t (id, v) VALUES (?, ?)", [1, "\u062A\u0627\u0646\u064A"]);
146
+ const rows = await a.query("SELECT id, v FROM t");
147
+ assertEqual(rows, [{ id: 1, v: "\u062A\u0627\u0646\u064A" }], "\u0627\u0644\u0635\u0641 \u0627\u062A\u0633\u062A\u0628\u062F\u0644 \u0645\u0634 \u0627\u062A\u0643\u0631\u0651\u0631");
148
+ })
149
+ }
150
+ ];
151
+
152
+ // src/conformance/index.ts
153
+ function runAdapterConformance(label, make, harness) {
154
+ harness.describe(`StorageAdapter conformance \u2014 ${label}`, () => {
155
+ for (const c of CONFORMANCE_CASES) {
156
+ harness.it(c.name, () => c.run(make));
157
+ }
158
+ });
159
+ }
160
+
161
+ export { CONFORMANCE_CASES, assert, assertEqual, assertThrows, bytesEqual, runAdapterConformance };
162
+ //# sourceMappingURL=chunk-HXRUTEU7.js.map
163
+ //# sourceMappingURL=chunk-HXRUTEU7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/conformance/assert.ts","../src/conformance/cases.ts","../src/conformance/index.ts"],"names":[],"mappings":"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","file":"chunk-HXRUTEU7.js","sourcesContent":["/**\n * assertions صغيرة مستقلة عن أي test runner — علشان نفس الـ conformance cases\n * تشتغل تحت vitest (Node) و Playwright (المتصفح، لـ wa-sqlite) من غير تغيير.\n * الفشل = throw.\n */\nexport function assert(cond: unknown, msg: string): asserts cond {\n if (!cond) throw new Error(`conformance: ${msg}`);\n}\n\nexport function assertEqual(actual: unknown, expected: unknown, msg: string): void {\n const a = JSON.stringify(actual);\n const e = JSON.stringify(expected);\n if (a !== e) {\n throw new Error(`conformance: ${msg} — المتوقع ${e} لكن جه ${a}`);\n }\n}\n\nexport function bytesEqual(a: Uint8Array, b: Uint8Array): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;\n return true;\n}\n\n/** يتأكد إن fn رمت (للتحقق من الـ rollback / إعادة الرمي). */\nexport async function assertThrows(\n fn: () => Promise<unknown>,\n msg: string,\n): Promise<void> {\n let threw = false;\n try {\n await fn();\n } catch {\n threw = true;\n }\n assert(threw, msg);\n}\n","import type { StorageAdapter } from \"../ports/StorageAdapter\";\nimport { assert, assertEqual, assertThrows, bytesEqual } from \"./assert\";\n\n/** بيرجّع adapter جديد فاضي (مفضّل :memory:) لكل حالة. */\nexport type MakeAdapter = () => Promise<StorageAdapter> | StorageAdapter;\n\nexport interface ConformanceCase {\n name: string;\n run(make: MakeAdapter): Promise<void>;\n}\n\n/** helper: يفتح adapter، ينفّذ الجسم، ويقفل مهما حصل. */\nasync function withAdapter(\n make: MakeAdapter,\n body: (a: StorageAdapter) => Promise<void>,\n): Promise<void> {\n const adapter = await make();\n try {\n await body(adapter);\n } finally {\n await adapter.close();\n }\n}\n\n/**\n * بطارية العقد اللي أي StorageAdapter لازم يعدّيها قبل الدمج. بتختبر عقد الـ\n * adapter نفسه (execute/query/params/transaction/close) — مش الـ repositories.\n */\nexport const CONFORMANCE_CASES: ConformanceCase[] = [\n {\n name: \"execute + query: إنشاء وإدراج وقراءة\",\n run: (make) =>\n withAdapter(make, async (a) => {\n await a.execute(\"CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT)\");\n await a.execute(\"INSERT INTO t (id, name) VALUES (?, ?)\", [1, \"قهوة\"]);\n const rows = await a.query<{ id: number; name: string }>(\n \"SELECT id, name FROM t\",\n );\n assertEqual(rows, [{ id: 1, name: \"قهوة\" }], \"صف واحد بالقيم الصح\");\n }),\n },\n\n {\n name: \"المعاملات: string / number / null / blob تتربط وترجع صح\",\n run: (make) =>\n withAdapter(make, async (a) => {\n await a.execute(\n \"CREATE TABLE t (id INTEGER, s TEXT, n REAL, z TEXT, b BLOB)\",\n );\n const blob = new Uint8Array([0, 255, 16, 200]);\n await a.execute(\"INSERT INTO t (id, s, n, z, b) VALUES (?, ?, ?, ?, ?)\", [\n 7,\n \"نص\",\n 12.5,\n null,\n blob,\n ]);\n const rows = await a.query<{\n id: number;\n s: string;\n n: number;\n z: string | null;\n b: Uint8Array;\n }>(\"SELECT id, s, n, z, b FROM t WHERE id = ?\", [7]);\n assert(rows.length === 1, \"صف واحد\");\n const r = rows[0]!;\n assertEqual(\n { id: r.id, s: r.s, n: r.n, z: r.z },\n { id: 7, s: \"نص\", n: 12.5, z: null },\n \"القيم غير الـ blob\",\n );\n assert(\n bytesEqual(new Uint8Array(r.b), blob),\n \"الـ blob يرجع بنفس البايتات\",\n );\n }),\n },\n\n {\n name: \"execute من غير params ينفّذ أكتر من statement (DDL/migrations)\",\n run: (make) =>\n withAdapter(make, async (a) => {\n await a.execute(`\n CREATE TABLE a (id INTEGER);\n CREATE TABLE b (id INTEGER);\n INSERT INTO a (id) VALUES (1);\n INSERT INTO b (id) VALUES (2);\n `);\n const ra = await a.query<{ id: number }>(\"SELECT id FROM a\");\n const rb = await a.query<{ id: number }>(\"SELECT id FROM b\");\n assertEqual([ra, rb], [[{ id: 1 }], [{ id: 2 }]], \"الجدولين اتعملوا\");\n }),\n },\n\n {\n name: \"transaction بتعمل commit عند النجاح\",\n run: (make) =>\n withAdapter(make, async (a) => {\n await a.execute(\"CREATE TABLE t (id INTEGER)\");\n await a.transaction(async (tx) => {\n await tx.execute(\"INSERT INTO t (id) VALUES (1)\");\n await tx.execute(\"INSERT INTO t (id) VALUES (2)\");\n });\n const rows = await a.query<{ id: number }>(\"SELECT id FROM t ORDER BY id\");\n assertEqual(rows, [{ id: 1 }, { id: 2 }], \"الصفين اتثبتوا بعد الـ commit\");\n }),\n },\n\n {\n name: \"transaction بتعمل rollback عند أي throw (atomic) وتعيد الرمي\",\n run: (make) =>\n withAdapter(make, async (a) => {\n await a.execute(\"CREATE TABLE t (id INTEGER)\");\n await assertThrows(\n () =>\n a.transaction(async (tx) => {\n await tx.execute(\"INSERT INTO t (id) VALUES (1)\");\n throw new Error(\"فشل مقصود وسط الـ transaction\");\n }),\n \"الـ transaction بترمي الخطأ بره\",\n );\n const rows = await a.query<{ id: number }>(\"SELECT id FROM t\");\n assertEqual(rows, [], \"مفيش أي صف اتكتب — اتعمل rollback للكل\");\n }),\n },\n\n {\n name: \"transaction بترجّع قيمة الـ callback\",\n run: (make) =>\n withAdapter(make, async (a) => {\n await a.execute(\"CREATE TABLE t (id INTEGER)\");\n const value = await a.transaction(async (tx) => {\n await tx.execute(\"INSERT INTO t (id) VALUES (42)\");\n const rows = await tx.query<{ id: number }>(\"SELECT id FROM t\");\n return rows[0]!.id;\n });\n assertEqual(value, 42, \"القيمة الراجعة من الـ callback\");\n }),\n },\n\n {\n name: \"read-your-writes: الـ tx بيشوف كتاباته قبل الـ commit\",\n run: (make) =>\n withAdapter(make, async (a) => {\n await a.execute(\"CREATE TABLE t (id INTEGER)\");\n await a.transaction(async (tx) => {\n await tx.execute(\"INSERT INTO t (id) VALUES (5)\");\n const rows = await tx.query<{ id: number }>(\"SELECT id FROM t\");\n assertEqual(rows, [{ id: 5 }], \"الاستعلام جوه الـ tx بيشوف الإدراج\");\n });\n }),\n },\n\n {\n name: \"INSERT OR REPLACE: upsert (يستخدمه الـ pull)\",\n run: (make) =>\n withAdapter(make, async (a) => {\n await a.execute(\"CREATE TABLE t (id INTEGER PRIMARY KEY, v TEXT)\");\n await a.execute(\"INSERT OR REPLACE INTO t (id, v) VALUES (?, ?)\", [1, \"أول\"]);\n await a.execute(\"INSERT OR REPLACE INTO t (id, v) VALUES (?, ?)\", [1, \"تاني\"]);\n const rows = await a.query<{ id: number; v: string }>(\"SELECT id, v FROM t\");\n assertEqual(rows, [{ id: 1, v: \"تاني\" }], \"الصف اتستبدل مش اتكرّر\");\n }),\n },\n];\n","import { CONFORMANCE_CASES, type MakeAdapter } from \"./cases\";\n\nexport { CONFORMANCE_CASES } from \"./cases\";\nexport type { ConformanceCase, MakeAdapter } from \"./cases\";\nexport { assert, assertEqual, assertThrows, bytesEqual } from \"./assert\";\n\n/**\n * ربط الـ conformance battery بـ vitest. أي adapter جديد (op-sqlite / wa-sqlite)\n * لازم يعدّي دي كلها قبل الدمج:\n *\n * import { runAdapterConformance } from \"qumra-pos-storage/conformance\";\n * runAdapterConformance(\"better-sqlite3\", () => BetterSqlite3Adapter.open());\n *\n * الـ describe/it بتتحقن — علشان نفضل مستقلين عن الـ runner (Playwright يقدر\n * يمرّر test.describe/test بتوعه لتشغيل نفس الحالات في المتصفح لـ wa-sqlite).\n */\nexport interface TestHarness {\n describe(name: string, fn: () => void): void;\n it(name: string, fn: () => void | Promise<void>): void;\n}\n\nexport function runAdapterConformance(\n label: string,\n make: MakeAdapter,\n harness: TestHarness,\n): void {\n harness.describe(`StorageAdapter conformance — ${label}`, () => {\n for (const c of CONFORMANCE_CASES) {\n harness.it(c.name, () => c.run(make));\n }\n });\n}\n"]}
@@ -0,0 +1,170 @@
1
+ 'use strict';
2
+
3
+ // src/conformance/assert.ts
4
+ function assert(cond, msg) {
5
+ if (!cond) throw new Error(`conformance: ${msg}`);
6
+ }
7
+ function assertEqual(actual, expected, msg) {
8
+ const a = JSON.stringify(actual);
9
+ const e = JSON.stringify(expected);
10
+ if (a !== e) {
11
+ throw new Error(`conformance: ${msg} \u2014 \u0627\u0644\u0645\u062A\u0648\u0642\u0639 ${e} \u0644\u0643\u0646 \u062C\u0647 ${a}`);
12
+ }
13
+ }
14
+ function bytesEqual(a, b) {
15
+ if (a.length !== b.length) return false;
16
+ for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
17
+ return true;
18
+ }
19
+ async function assertThrows(fn, msg) {
20
+ let threw = false;
21
+ try {
22
+ await fn();
23
+ } catch {
24
+ threw = true;
25
+ }
26
+ assert(threw, msg);
27
+ }
28
+
29
+ // src/conformance/cases.ts
30
+ async function withAdapter(make, body) {
31
+ const adapter = await make();
32
+ try {
33
+ await body(adapter);
34
+ } finally {
35
+ await adapter.close();
36
+ }
37
+ }
38
+ var CONFORMANCE_CASES = [
39
+ {
40
+ name: "execute + query: \u0625\u0646\u0634\u0627\u0621 \u0648\u0625\u062F\u0631\u0627\u062C \u0648\u0642\u0631\u0627\u0621\u0629",
41
+ run: (make) => withAdapter(make, async (a) => {
42
+ await a.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT)");
43
+ await a.execute("INSERT INTO t (id, name) VALUES (?, ?)", [1, "\u0642\u0647\u0648\u0629"]);
44
+ const rows = await a.query(
45
+ "SELECT id, name FROM t"
46
+ );
47
+ assertEqual(rows, [{ id: 1, name: "\u0642\u0647\u0648\u0629" }], "\u0635\u0641 \u0648\u0627\u062D\u062F \u0628\u0627\u0644\u0642\u064A\u0645 \u0627\u0644\u0635\u062D");
48
+ })
49
+ },
50
+ {
51
+ name: "\u0627\u0644\u0645\u0639\u0627\u0645\u0644\u0627\u062A: string / number / null / blob \u062A\u062A\u0631\u0628\u0637 \u0648\u062A\u0631\u062C\u0639 \u0635\u062D",
52
+ run: (make) => withAdapter(make, async (a) => {
53
+ await a.execute(
54
+ "CREATE TABLE t (id INTEGER, s TEXT, n REAL, z TEXT, b BLOB)"
55
+ );
56
+ const blob = new Uint8Array([0, 255, 16, 200]);
57
+ await a.execute("INSERT INTO t (id, s, n, z, b) VALUES (?, ?, ?, ?, ?)", [
58
+ 7,
59
+ "\u0646\u0635",
60
+ 12.5,
61
+ null,
62
+ blob
63
+ ]);
64
+ const rows = await a.query("SELECT id, s, n, z, b FROM t WHERE id = ?", [7]);
65
+ assert(rows.length === 1, "\u0635\u0641 \u0648\u0627\u062D\u062F");
66
+ const r = rows[0];
67
+ assertEqual(
68
+ { id: r.id, s: r.s, n: r.n, z: r.z },
69
+ { id: 7, s: "\u0646\u0635", n: 12.5, z: null },
70
+ "\u0627\u0644\u0642\u064A\u0645 \u063A\u064A\u0631 \u0627\u0644\u0640 blob"
71
+ );
72
+ assert(
73
+ bytesEqual(new Uint8Array(r.b), blob),
74
+ "\u0627\u0644\u0640 blob \u064A\u0631\u062C\u0639 \u0628\u0646\u0641\u0633 \u0627\u0644\u0628\u0627\u064A\u062A\u0627\u062A"
75
+ );
76
+ })
77
+ },
78
+ {
79
+ name: "execute \u0645\u0646 \u063A\u064A\u0631 params \u064A\u0646\u0641\u0651\u0630 \u0623\u0643\u062A\u0631 \u0645\u0646 statement (DDL/migrations)",
80
+ run: (make) => withAdapter(make, async (a) => {
81
+ await a.execute(`
82
+ CREATE TABLE a (id INTEGER);
83
+ CREATE TABLE b (id INTEGER);
84
+ INSERT INTO a (id) VALUES (1);
85
+ INSERT INTO b (id) VALUES (2);
86
+ `);
87
+ const ra = await a.query("SELECT id FROM a");
88
+ const rb = await a.query("SELECT id FROM b");
89
+ assertEqual([ra, rb], [[{ id: 1 }], [{ id: 2 }]], "\u0627\u0644\u062C\u062F\u0648\u0644\u064A\u0646 \u0627\u062A\u0639\u0645\u0644\u0648\u0627");
90
+ })
91
+ },
92
+ {
93
+ name: "transaction \u0628\u062A\u0639\u0645\u0644 commit \u0639\u0646\u062F \u0627\u0644\u0646\u062C\u0627\u062D",
94
+ run: (make) => withAdapter(make, async (a) => {
95
+ await a.execute("CREATE TABLE t (id INTEGER)");
96
+ await a.transaction(async (tx) => {
97
+ await tx.execute("INSERT INTO t (id) VALUES (1)");
98
+ await tx.execute("INSERT INTO t (id) VALUES (2)");
99
+ });
100
+ const rows = await a.query("SELECT id FROM t ORDER BY id");
101
+ assertEqual(rows, [{ id: 1 }, { id: 2 }], "\u0627\u0644\u0635\u0641\u064A\u0646 \u0627\u062A\u062B\u0628\u062A\u0648\u0627 \u0628\u0639\u062F \u0627\u0644\u0640 commit");
102
+ })
103
+ },
104
+ {
105
+ name: "transaction \u0628\u062A\u0639\u0645\u0644 rollback \u0639\u0646\u062F \u0623\u064A throw (atomic) \u0648\u062A\u0639\u064A\u062F \u0627\u0644\u0631\u0645\u064A",
106
+ run: (make) => withAdapter(make, async (a) => {
107
+ await a.execute("CREATE TABLE t (id INTEGER)");
108
+ await assertThrows(
109
+ () => a.transaction(async (tx) => {
110
+ await tx.execute("INSERT INTO t (id) VALUES (1)");
111
+ throw new Error("\u0641\u0634\u0644 \u0645\u0642\u0635\u0648\u062F \u0648\u0633\u0637 \u0627\u0644\u0640 transaction");
112
+ }),
113
+ "\u0627\u0644\u0640 transaction \u0628\u062A\u0631\u0645\u064A \u0627\u0644\u062E\u0637\u0623 \u0628\u0631\u0647"
114
+ );
115
+ const rows = await a.query("SELECT id FROM t");
116
+ assertEqual(rows, [], "\u0645\u0641\u064A\u0634 \u0623\u064A \u0635\u0641 \u0627\u062A\u0643\u062A\u0628 \u2014 \u0627\u062A\u0639\u0645\u0644 rollback \u0644\u0644\u0643\u0644");
117
+ })
118
+ },
119
+ {
120
+ name: "transaction \u0628\u062A\u0631\u062C\u0651\u0639 \u0642\u064A\u0645\u0629 \u0627\u0644\u0640 callback",
121
+ run: (make) => withAdapter(make, async (a) => {
122
+ await a.execute("CREATE TABLE t (id INTEGER)");
123
+ const value = await a.transaction(async (tx) => {
124
+ await tx.execute("INSERT INTO t (id) VALUES (42)");
125
+ const rows = await tx.query("SELECT id FROM t");
126
+ return rows[0].id;
127
+ });
128
+ assertEqual(value, 42, "\u0627\u0644\u0642\u064A\u0645\u0629 \u0627\u0644\u0631\u0627\u062C\u0639\u0629 \u0645\u0646 \u0627\u0644\u0640 callback");
129
+ })
130
+ },
131
+ {
132
+ name: "read-your-writes: \u0627\u0644\u0640 tx \u0628\u064A\u0634\u0648\u0641 \u0643\u062A\u0627\u0628\u0627\u062A\u0647 \u0642\u0628\u0644 \u0627\u0644\u0640 commit",
133
+ run: (make) => withAdapter(make, async (a) => {
134
+ await a.execute("CREATE TABLE t (id INTEGER)");
135
+ await a.transaction(async (tx) => {
136
+ await tx.execute("INSERT INTO t (id) VALUES (5)");
137
+ const rows = await tx.query("SELECT id FROM t");
138
+ assertEqual(rows, [{ id: 5 }], "\u0627\u0644\u0627\u0633\u062A\u0639\u0644\u0627\u0645 \u062C\u0648\u0647 \u0627\u0644\u0640 tx \u0628\u064A\u0634\u0648\u0641 \u0627\u0644\u0625\u062F\u0631\u0627\u062C");
139
+ });
140
+ })
141
+ },
142
+ {
143
+ name: "INSERT OR REPLACE: upsert (\u064A\u0633\u062A\u062E\u062F\u0645\u0647 \u0627\u0644\u0640 pull)",
144
+ run: (make) => withAdapter(make, async (a) => {
145
+ await a.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, v TEXT)");
146
+ await a.execute("INSERT OR REPLACE INTO t (id, v) VALUES (?, ?)", [1, "\u0623\u0648\u0644"]);
147
+ await a.execute("INSERT OR REPLACE INTO t (id, v) VALUES (?, ?)", [1, "\u062A\u0627\u0646\u064A"]);
148
+ const rows = await a.query("SELECT id, v FROM t");
149
+ assertEqual(rows, [{ id: 1, v: "\u062A\u0627\u0646\u064A" }], "\u0627\u0644\u0635\u0641 \u0627\u062A\u0633\u062A\u0628\u062F\u0644 \u0645\u0634 \u0627\u062A\u0643\u0631\u0651\u0631");
150
+ })
151
+ }
152
+ ];
153
+
154
+ // src/conformance/index.ts
155
+ function runAdapterConformance(label, make, harness) {
156
+ harness.describe(`StorageAdapter conformance \u2014 ${label}`, () => {
157
+ for (const c of CONFORMANCE_CASES) {
158
+ harness.it(c.name, () => c.run(make));
159
+ }
160
+ });
161
+ }
162
+
163
+ exports.CONFORMANCE_CASES = CONFORMANCE_CASES;
164
+ exports.assert = assert;
165
+ exports.assertEqual = assertEqual;
166
+ exports.assertThrows = assertThrows;
167
+ exports.bytesEqual = bytesEqual;
168
+ exports.runAdapterConformance = runAdapterConformance;
169
+ //# sourceMappingURL=conformance.cjs.map
170
+ //# sourceMappingURL=conformance.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/conformance/assert.ts","../src/conformance/cases.ts","../src/conformance/index.ts"],"names":[],"mappings":"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","file":"conformance.cjs","sourcesContent":["/**\n * assertions صغيرة مستقلة عن أي test runner — علشان نفس الـ conformance cases\n * تشتغل تحت vitest (Node) و Playwright (المتصفح، لـ wa-sqlite) من غير تغيير.\n * الفشل = throw.\n */\nexport function assert(cond: unknown, msg: string): asserts cond {\n if (!cond) throw new Error(`conformance: ${msg}`);\n}\n\nexport function assertEqual(actual: unknown, expected: unknown, msg: string): void {\n const a = JSON.stringify(actual);\n const e = JSON.stringify(expected);\n if (a !== e) {\n throw new Error(`conformance: ${msg} — المتوقع ${e} لكن جه ${a}`);\n }\n}\n\nexport function bytesEqual(a: Uint8Array, b: Uint8Array): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;\n return true;\n}\n\n/** يتأكد إن fn رمت (للتحقق من الـ rollback / إعادة الرمي). */\nexport async function assertThrows(\n fn: () => Promise<unknown>,\n msg: string,\n): Promise<void> {\n let threw = false;\n try {\n await fn();\n } catch {\n threw = true;\n }\n assert(threw, msg);\n}\n","import type { StorageAdapter } from \"../ports/StorageAdapter\";\nimport { assert, assertEqual, assertThrows, bytesEqual } from \"./assert\";\n\n/** بيرجّع adapter جديد فاضي (مفضّل :memory:) لكل حالة. */\nexport type MakeAdapter = () => Promise<StorageAdapter> | StorageAdapter;\n\nexport interface ConformanceCase {\n name: string;\n run(make: MakeAdapter): Promise<void>;\n}\n\n/** helper: يفتح adapter، ينفّذ الجسم، ويقفل مهما حصل. */\nasync function withAdapter(\n make: MakeAdapter,\n body: (a: StorageAdapter) => Promise<void>,\n): Promise<void> {\n const adapter = await make();\n try {\n await body(adapter);\n } finally {\n await adapter.close();\n }\n}\n\n/**\n * بطارية العقد اللي أي StorageAdapter لازم يعدّيها قبل الدمج. بتختبر عقد الـ\n * adapter نفسه (execute/query/params/transaction/close) — مش الـ repositories.\n */\nexport const CONFORMANCE_CASES: ConformanceCase[] = [\n {\n name: \"execute + query: إنشاء وإدراج وقراءة\",\n run: (make) =>\n withAdapter(make, async (a) => {\n await a.execute(\"CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT)\");\n await a.execute(\"INSERT INTO t (id, name) VALUES (?, ?)\", [1, \"قهوة\"]);\n const rows = await a.query<{ id: number; name: string }>(\n \"SELECT id, name FROM t\",\n );\n assertEqual(rows, [{ id: 1, name: \"قهوة\" }], \"صف واحد بالقيم الصح\");\n }),\n },\n\n {\n name: \"المعاملات: string / number / null / blob تتربط وترجع صح\",\n run: (make) =>\n withAdapter(make, async (a) => {\n await a.execute(\n \"CREATE TABLE t (id INTEGER, s TEXT, n REAL, z TEXT, b BLOB)\",\n );\n const blob = new Uint8Array([0, 255, 16, 200]);\n await a.execute(\"INSERT INTO t (id, s, n, z, b) VALUES (?, ?, ?, ?, ?)\", [\n 7,\n \"نص\",\n 12.5,\n null,\n blob,\n ]);\n const rows = await a.query<{\n id: number;\n s: string;\n n: number;\n z: string | null;\n b: Uint8Array;\n }>(\"SELECT id, s, n, z, b FROM t WHERE id = ?\", [7]);\n assert(rows.length === 1, \"صف واحد\");\n const r = rows[0]!;\n assertEqual(\n { id: r.id, s: r.s, n: r.n, z: r.z },\n { id: 7, s: \"نص\", n: 12.5, z: null },\n \"القيم غير الـ blob\",\n );\n assert(\n bytesEqual(new Uint8Array(r.b), blob),\n \"الـ blob يرجع بنفس البايتات\",\n );\n }),\n },\n\n {\n name: \"execute من غير params ينفّذ أكتر من statement (DDL/migrations)\",\n run: (make) =>\n withAdapter(make, async (a) => {\n await a.execute(`\n CREATE TABLE a (id INTEGER);\n CREATE TABLE b (id INTEGER);\n INSERT INTO a (id) VALUES (1);\n INSERT INTO b (id) VALUES (2);\n `);\n const ra = await a.query<{ id: number }>(\"SELECT id FROM a\");\n const rb = await a.query<{ id: number }>(\"SELECT id FROM b\");\n assertEqual([ra, rb], [[{ id: 1 }], [{ id: 2 }]], \"الجدولين اتعملوا\");\n }),\n },\n\n {\n name: \"transaction بتعمل commit عند النجاح\",\n run: (make) =>\n withAdapter(make, async (a) => {\n await a.execute(\"CREATE TABLE t (id INTEGER)\");\n await a.transaction(async (tx) => {\n await tx.execute(\"INSERT INTO t (id) VALUES (1)\");\n await tx.execute(\"INSERT INTO t (id) VALUES (2)\");\n });\n const rows = await a.query<{ id: number }>(\"SELECT id FROM t ORDER BY id\");\n assertEqual(rows, [{ id: 1 }, { id: 2 }], \"الصفين اتثبتوا بعد الـ commit\");\n }),\n },\n\n {\n name: \"transaction بتعمل rollback عند أي throw (atomic) وتعيد الرمي\",\n run: (make) =>\n withAdapter(make, async (a) => {\n await a.execute(\"CREATE TABLE t (id INTEGER)\");\n await assertThrows(\n () =>\n a.transaction(async (tx) => {\n await tx.execute(\"INSERT INTO t (id) VALUES (1)\");\n throw new Error(\"فشل مقصود وسط الـ transaction\");\n }),\n \"الـ transaction بترمي الخطأ بره\",\n );\n const rows = await a.query<{ id: number }>(\"SELECT id FROM t\");\n assertEqual(rows, [], \"مفيش أي صف اتكتب — اتعمل rollback للكل\");\n }),\n },\n\n {\n name: \"transaction بترجّع قيمة الـ callback\",\n run: (make) =>\n withAdapter(make, async (a) => {\n await a.execute(\"CREATE TABLE t (id INTEGER)\");\n const value = await a.transaction(async (tx) => {\n await tx.execute(\"INSERT INTO t (id) VALUES (42)\");\n const rows = await tx.query<{ id: number }>(\"SELECT id FROM t\");\n return rows[0]!.id;\n });\n assertEqual(value, 42, \"القيمة الراجعة من الـ callback\");\n }),\n },\n\n {\n name: \"read-your-writes: الـ tx بيشوف كتاباته قبل الـ commit\",\n run: (make) =>\n withAdapter(make, async (a) => {\n await a.execute(\"CREATE TABLE t (id INTEGER)\");\n await a.transaction(async (tx) => {\n await tx.execute(\"INSERT INTO t (id) VALUES (5)\");\n const rows = await tx.query<{ id: number }>(\"SELECT id FROM t\");\n assertEqual(rows, [{ id: 5 }], \"الاستعلام جوه الـ tx بيشوف الإدراج\");\n });\n }),\n },\n\n {\n name: \"INSERT OR REPLACE: upsert (يستخدمه الـ pull)\",\n run: (make) =>\n withAdapter(make, async (a) => {\n await a.execute(\"CREATE TABLE t (id INTEGER PRIMARY KEY, v TEXT)\");\n await a.execute(\"INSERT OR REPLACE INTO t (id, v) VALUES (?, ?)\", [1, \"أول\"]);\n await a.execute(\"INSERT OR REPLACE INTO t (id, v) VALUES (?, ?)\", [1, \"تاني\"]);\n const rows = await a.query<{ id: number; v: string }>(\"SELECT id, v FROM t\");\n assertEqual(rows, [{ id: 1, v: \"تاني\" }], \"الصف اتستبدل مش اتكرّر\");\n }),\n },\n];\n","import { CONFORMANCE_CASES, type MakeAdapter } from \"./cases\";\n\nexport { CONFORMANCE_CASES } from \"./cases\";\nexport type { ConformanceCase, MakeAdapter } from \"./cases\";\nexport { assert, assertEqual, assertThrows, bytesEqual } from \"./assert\";\n\n/**\n * ربط الـ conformance battery بـ vitest. أي adapter جديد (op-sqlite / wa-sqlite)\n * لازم يعدّي دي كلها قبل الدمج:\n *\n * import { runAdapterConformance } from \"qumra-pos-storage/conformance\";\n * runAdapterConformance(\"better-sqlite3\", () => BetterSqlite3Adapter.open());\n *\n * الـ describe/it بتتحقن — علشان نفضل مستقلين عن الـ runner (Playwright يقدر\n * يمرّر test.describe/test بتوعه لتشغيل نفس الحالات في المتصفح لـ wa-sqlite).\n */\nexport interface TestHarness {\n describe(name: string, fn: () => void): void;\n it(name: string, fn: () => void | Promise<void>): void;\n}\n\nexport function runAdapterConformance(\n label: string,\n make: MakeAdapter,\n harness: TestHarness,\n): void {\n harness.describe(`StorageAdapter conformance — ${label}`, () => {\n for (const c of CONFORMANCE_CASES) {\n harness.it(c.name, () => c.run(make));\n }\n });\n}\n"]}
@@ -0,0 +1,42 @@
1
+ import { a as StorageAdapter } from './StorageAdapter-B2IgHcUF.cjs';
2
+
3
+ /** بيرجّع adapter جديد فاضي (مفضّل :memory:) لكل حالة. */
4
+ type MakeAdapter = () => Promise<StorageAdapter> | StorageAdapter;
5
+ interface ConformanceCase {
6
+ name: string;
7
+ run(make: MakeAdapter): Promise<void>;
8
+ }
9
+ /**
10
+ * بطارية العقد اللي أي StorageAdapter لازم يعدّيها قبل الدمج. بتختبر عقد الـ
11
+ * adapter نفسه (execute/query/params/transaction/close) — مش الـ repositories.
12
+ */
13
+ declare const CONFORMANCE_CASES: ConformanceCase[];
14
+
15
+ /**
16
+ * assertions صغيرة مستقلة عن أي test runner — علشان نفس الـ conformance cases
17
+ * تشتغل تحت vitest (Node) و Playwright (المتصفح، لـ wa-sqlite) من غير تغيير.
18
+ * الفشل = throw.
19
+ */
20
+ declare function assert(cond: unknown, msg: string): asserts cond;
21
+ declare function assertEqual(actual: unknown, expected: unknown, msg: string): void;
22
+ declare function bytesEqual(a: Uint8Array, b: Uint8Array): boolean;
23
+ /** يتأكد إن fn رمت (للتحقق من الـ rollback / إعادة الرمي). */
24
+ declare function assertThrows(fn: () => Promise<unknown>, msg: string): Promise<void>;
25
+
26
+ /**
27
+ * ربط الـ conformance battery بـ vitest. أي adapter جديد (op-sqlite / wa-sqlite)
28
+ * لازم يعدّي دي كلها قبل الدمج:
29
+ *
30
+ * import { runAdapterConformance } from "qumra-pos-storage/conformance";
31
+ * runAdapterConformance("better-sqlite3", () => BetterSqlite3Adapter.open());
32
+ *
33
+ * الـ describe/it بتتحقن — علشان نفضل مستقلين عن الـ runner (Playwright يقدر
34
+ * يمرّر test.describe/test بتوعه لتشغيل نفس الحالات في المتصفح لـ wa-sqlite).
35
+ */
36
+ interface TestHarness {
37
+ describe(name: string, fn: () => void): void;
38
+ it(name: string, fn: () => void | Promise<void>): void;
39
+ }
40
+ declare function runAdapterConformance(label: string, make: MakeAdapter, harness: TestHarness): void;
41
+
42
+ export { CONFORMANCE_CASES, type ConformanceCase, type MakeAdapter, type TestHarness, assert, assertEqual, assertThrows, bytesEqual, runAdapterConformance };
@@ -0,0 +1,42 @@
1
+ import { a as StorageAdapter } from './StorageAdapter-B2IgHcUF.js';
2
+
3
+ /** بيرجّع adapter جديد فاضي (مفضّل :memory:) لكل حالة. */
4
+ type MakeAdapter = () => Promise<StorageAdapter> | StorageAdapter;
5
+ interface ConformanceCase {
6
+ name: string;
7
+ run(make: MakeAdapter): Promise<void>;
8
+ }
9
+ /**
10
+ * بطارية العقد اللي أي StorageAdapter لازم يعدّيها قبل الدمج. بتختبر عقد الـ
11
+ * adapter نفسه (execute/query/params/transaction/close) — مش الـ repositories.
12
+ */
13
+ declare const CONFORMANCE_CASES: ConformanceCase[];
14
+
15
+ /**
16
+ * assertions صغيرة مستقلة عن أي test runner — علشان نفس الـ conformance cases
17
+ * تشتغل تحت vitest (Node) و Playwright (المتصفح، لـ wa-sqlite) من غير تغيير.
18
+ * الفشل = throw.
19
+ */
20
+ declare function assert(cond: unknown, msg: string): asserts cond;
21
+ declare function assertEqual(actual: unknown, expected: unknown, msg: string): void;
22
+ declare function bytesEqual(a: Uint8Array, b: Uint8Array): boolean;
23
+ /** يتأكد إن fn رمت (للتحقق من الـ rollback / إعادة الرمي). */
24
+ declare function assertThrows(fn: () => Promise<unknown>, msg: string): Promise<void>;
25
+
26
+ /**
27
+ * ربط الـ conformance battery بـ vitest. أي adapter جديد (op-sqlite / wa-sqlite)
28
+ * لازم يعدّي دي كلها قبل الدمج:
29
+ *
30
+ * import { runAdapterConformance } from "qumra-pos-storage/conformance";
31
+ * runAdapterConformance("better-sqlite3", () => BetterSqlite3Adapter.open());
32
+ *
33
+ * الـ describe/it بتتحقن — علشان نفضل مستقلين عن الـ runner (Playwright يقدر
34
+ * يمرّر test.describe/test بتوعه لتشغيل نفس الحالات في المتصفح لـ wa-sqlite).
35
+ */
36
+ interface TestHarness {
37
+ describe(name: string, fn: () => void): void;
38
+ it(name: string, fn: () => void | Promise<void>): void;
39
+ }
40
+ declare function runAdapterConformance(label: string, make: MakeAdapter, harness: TestHarness): void;
41
+
42
+ export { CONFORMANCE_CASES, type ConformanceCase, type MakeAdapter, type TestHarness, assert, assertEqual, assertThrows, bytesEqual, runAdapterConformance };
@@ -0,0 +1,3 @@
1
+ export { CONFORMANCE_CASES, assert, assertEqual, assertThrows, bytesEqual, runAdapterConformance } from './chunk-HXRUTEU7.js';
2
+ //# sourceMappingURL=conformance.js.map
3
+ //# sourceMappingURL=conformance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"conformance.js"}