@majikah/majik-message 0.3.6 → 0.3.7

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 (66) hide show
  1. package/README.md +3 -3
  2. package/dist/core/client-state-manager.d.ts +105 -0
  3. package/dist/core/client-state-manager.js +250 -0
  4. package/dist/core/contacts/majik-contact-directory.d.ts +0 -5
  5. package/dist/core/contacts/majik-contact-directory.js +0 -12
  6. package/dist/core/contacts/majik-contact-groups.d.ts +1 -0
  7. package/dist/core/contacts/majik-contact-groups.js +5 -0
  8. package/dist/core/contacts/majik-contact-manager.d.ts +92 -184
  9. package/dist/core/contacts/majik-contact-manager.js +368 -288
  10. package/dist/core/crypto/keystore-manager.d.ts +166 -0
  11. package/dist/core/crypto/keystore-manager.js +371 -0
  12. package/dist/core/storage/chats/_types.d.ts +8 -0
  13. package/dist/core/storage/chats/_types.js +1 -0
  14. package/dist/core/storage/chats/adapter-idb.d.ts +3 -0
  15. package/dist/core/storage/chats/adapter-idb.js +5 -0
  16. package/dist/core/storage/chats/adapter-memory.d.ts +23 -0
  17. package/dist/core/storage/chats/adapter-memory.js +44 -0
  18. package/dist/core/storage/chats/adapter-sql.d.ts +17 -0
  19. package/dist/core/storage/chats/adapter-sql.js +84 -0
  20. package/dist/core/storage/client-state/_types.d.ts +37 -0
  21. package/dist/core/storage/client-state/_types.js +16 -0
  22. package/dist/core/storage/client-state/adapter-idb.d.ts +17 -0
  23. package/dist/core/storage/client-state/adapter-idb.js +19 -0
  24. package/dist/core/storage/client-state/adapter-memory.d.ts +20 -0
  25. package/dist/core/storage/client-state/adapter-memory.js +44 -0
  26. package/dist/core/storage/client-state/adapter-sql.d.ts +41 -0
  27. package/dist/core/storage/client-state/adapter-sql.js +104 -0
  28. package/dist/core/storage/contact-directory/contacts/_types.d.ts +3 -0
  29. package/dist/core/storage/contact-directory/contacts/_types.js +1 -0
  30. package/dist/core/storage/contact-directory/contacts/adapter-idb.d.ts +3 -0
  31. package/dist/core/storage/contact-directory/contacts/adapter-idb.js +5 -0
  32. package/dist/core/storage/contact-directory/contacts/adapter-memory.d.ts +14 -0
  33. package/dist/core/storage/contact-directory/contacts/adapter-memory.js +32 -0
  34. package/dist/core/storage/contact-directory/contacts/adapter-sql.d.ts +16 -0
  35. package/dist/core/storage/contact-directory/contacts/adapter-sql.js +73 -0
  36. package/dist/core/storage/contact-directory/groups/_types.d.ts +3 -0
  37. package/dist/core/storage/contact-directory/groups/_types.js +1 -0
  38. package/dist/core/storage/contact-directory/groups/adapter-idb.d.ts +3 -0
  39. package/dist/core/storage/contact-directory/groups/adapter-idb.js +5 -0
  40. package/dist/core/storage/contact-directory/groups/adapter-memory.d.ts +14 -0
  41. package/dist/core/storage/contact-directory/groups/adapter-memory.js +32 -0
  42. package/dist/core/storage/contact-directory/groups/adapter-sql.d.ts +16 -0
  43. package/dist/core/storage/contact-directory/groups/adapter-sql.js +71 -0
  44. package/dist/core/storage/idb-adapter.d.ts +21 -0
  45. package/dist/core/storage/idb-adapter.js +107 -0
  46. package/dist/core/storage/index.d.ts +24 -0
  47. package/dist/core/storage/index.js +19 -0
  48. package/dist/core/storage/keystore/_types.d.ts +3 -0
  49. package/dist/core/storage/keystore/_types.js +1 -0
  50. package/dist/core/storage/keystore/adapter-idb.d.ts +3 -0
  51. package/dist/core/storage/keystore/adapter-idb.js +5 -0
  52. package/dist/core/storage/keystore/adapter-memory.d.ts +14 -0
  53. package/dist/core/storage/keystore/adapter-memory.js +32 -0
  54. package/dist/core/storage/keystore/adapter-sql.d.ts +16 -0
  55. package/dist/core/storage/keystore/adapter-sql.js +69 -0
  56. package/dist/core/storage/sql-db-manager.d.ts +13 -0
  57. package/dist/core/storage/sql-db-manager.js +59 -0
  58. package/dist/core/storage/sql-schema.d.ts +10 -0
  59. package/dist/core/storage/sql-schema.js +108 -0
  60. package/dist/core/storage/storage-adapter.d.ts +14 -0
  61. package/dist/core/storage/storage-adapter.js +1 -0
  62. package/dist/index.d.ts +2 -4
  63. package/dist/index.js +2 -4
  64. package/dist/majik-message.d.ts +109 -174
  65. package/dist/majik-message.js +428 -677
  66. package/package.json +4 -5
@@ -0,0 +1,84 @@
1
+ export class SQLiteInvoiceAdapter {
2
+ db;
3
+ constructor(db) {
4
+ this.db = db;
5
+ }
6
+ async save(message, source = "local") {
7
+ const resolvedSource = source ?? "local";
8
+ await this.db.run(`INSERT OR REPLACE INTO majik_message_chats
9
+ (id, json, created_at, source)
10
+ VALUES (?, ?, ?, ?)`, [message.id, JSON.stringify(message), message.timestamp, resolvedSource]);
11
+ }
12
+ async getById(id, source) {
13
+ const row = source
14
+ ? await this.db.get("SELECT json FROM majik_messages WHERE id = ? AND source = ?", [id, source])
15
+ : await this.db.get("SELECT json FROM majik_messages WHERE id = ?", [id]);
16
+ return row ? JSON.parse(row.json) : null;
17
+ }
18
+ async list(source) {
19
+ const rows = source
20
+ ? await this.db.all("SELECT json FROM majik_messages WHERE source = ?", [source])
21
+ : await this.db.all("SELECT json FROM majik_messages");
22
+ return rows.map((r) => JSON.parse(r.json));
23
+ }
24
+ async remove(id, source) {
25
+ const exists = await this.exists(id, source);
26
+ if (!exists)
27
+ return false;
28
+ if (source) {
29
+ await this.db.run("DELETE FROM majik_messages WHERE id = ? AND source = ?", [id, source]);
30
+ }
31
+ else {
32
+ await this.db.run("DELETE FROM majik_messages WHERE id = ?", [id]);
33
+ }
34
+ return true;
35
+ }
36
+ async clear(source) {
37
+ if (source) {
38
+ await this.db.run("DELETE FROM majik_messages WHERE source = ?", [
39
+ source,
40
+ ]);
41
+ }
42
+ else {
43
+ await this.db.run("DELETE FROM majik_messages");
44
+ }
45
+ }
46
+ async count(source) {
47
+ const row = source
48
+ ? await this.db.get("SELECT COUNT(*) as n FROM majik_messages WHERE source = ?", [source])
49
+ : await this.db.get("SELECT COUNT(*) as n FROM majik_messages");
50
+ return row?.n ?? 0;
51
+ }
52
+ async exists(id, source) {
53
+ const row = source
54
+ ? await this.db.get("SELECT 1 FROM majik_messages WHERE id = ? AND source = ?", [id, source])
55
+ : await this.db.get("SELECT 1 FROM majik_messages WHERE id = ?", [id]);
56
+ return !!row;
57
+ }
58
+ async bulkSave(messages, source = "local") {
59
+ if (messages.length === 0)
60
+ return;
61
+ const resolvedSource = source ?? "local";
62
+ await this.db.transaction(async (tx) => {
63
+ for (const msg of messages) {
64
+ await tx.run(`INSERT OR REPLACE INTO majik_message_chats
65
+ (id, json, created_at, source)
66
+ VALUES (?, ?, ?, ?, ?, ?)`, [msg.id, JSON.stringify(msg), msg.timestamp, resolvedSource]);
67
+ }
68
+ });
69
+ }
70
+ async bulkRemove(ids, source) {
71
+ if (ids.length === 0)
72
+ return;
73
+ await this.db.transaction(async (tx) => {
74
+ for (const id of ids) {
75
+ if (source) {
76
+ await tx.run("DELETE FROM majik_messages WHERE id = ? AND source = ?", [id, source]);
77
+ }
78
+ else {
79
+ await tx.run("DELETE FROM majik_messages WHERE id = ?", [id]);
80
+ }
81
+ }
82
+ });
83
+ }
84
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * @file _types.ts
3
+ * @description Shared types for the ClientState storage layer.
4
+ *
5
+ * The adapter is intentionally minimal — it is a generic key/value store
6
+ * where each entry carries a plain JSON-serialisable `value`. The
7
+ * ClientStateManager owns all serialisation / deserialisation logic; the
8
+ * adapter only moves bytes.
9
+ */
10
+ import { MajikStorageAdapter } from "../storage-adapter";
11
+ export interface ClientStateEntry {
12
+ /** Stable string key that identifies this piece of state. */
13
+ id: string;
14
+ /** JSON-serialised value. Always a string on the wire / in storage. */
15
+ value: string;
16
+ /** ISO 8601 datetime — set by the adapter on every write where supported. */
17
+ updatedAt?: string;
18
+ }
19
+ export declare const CLIENT_STATE_KEYS: {
20
+ readonly ACCOUNT_ORDER: "user_account_order";
21
+ };
22
+ export type ClientStateKey = (typeof CLIENT_STATE_KEYS)[keyof typeof CLIENT_STATE_KEYS];
23
+ /**
24
+ * Ordered list of own account IDs. The head of the array is the active
25
+ * account. Stored as a JSON array: `["id1", "id2", ...]`.
26
+ */
27
+ export type AccountOrderValue = string[];
28
+ /**
29
+ * Pluggable persistence backend for client-level state.
30
+ *
31
+ * Implementations must provide IDB, SQLite, and in-memory variants.
32
+ * All methods are async for uniformity — in-memory may resolve immediately.
33
+ *
34
+ * The store is deliberately flat: every piece of state is a `ClientStateEntry`
35
+ * keyed by a stable string ID. There is no relational structure.
36
+ */
37
+ export type ClientStateStorageAdapter = MajikStorageAdapter<ClientStateEntry>;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @file _types.ts
3
+ * @description Shared types for the ClientState storage layer.
4
+ *
5
+ * The adapter is intentionally minimal — it is a generic key/value store
6
+ * where each entry carries a plain JSON-serialisable `value`. The
7
+ * ClientStateManager owns all serialisation / deserialisation logic; the
8
+ * adapter only moves bytes.
9
+ */
10
+ // ---------------------------------------------------------------------------
11
+ // Well-known state keys
12
+ // ---------------------------------------------------------------------------
13
+ export const CLIENT_STATE_KEYS = {
14
+ ACCOUNT_ORDER: "user_account_order",
15
+ // INVOICE_DEFAULTS: "invoice_defaults",
16
+ };
@@ -0,0 +1,17 @@
1
+ /**
2
+ * @file adapter-idb.ts
3
+ * @description IndexedDB-backed ClientStateStorageAdapter.
4
+ *
5
+ * Uses IDBGenericAdapter<ClientStateEntry> under the hood so the full
6
+ * IDB transaction / error-handling logic lives in one place.
7
+ *
8
+ * Database : "majik-client-state"
9
+ * Store : "client-state"
10
+ * Version : 1
11
+ *
12
+ * Each entry is stored with `id` as the keyPath — identical to the invoice
13
+ * and contact adapters.
14
+ */
15
+ import type { ClientStateEntry } from "./_types";
16
+ import { IDBGenericAdapter } from "../idb-adapter";
17
+ export declare const IDB_ADAPTER_CLIENT_STATE: IDBGenericAdapter<ClientStateEntry>;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @file adapter-idb.ts
3
+ * @description IndexedDB-backed ClientStateStorageAdapter.
4
+ *
5
+ * Uses IDBGenericAdapter<ClientStateEntry> under the hood so the full
6
+ * IDB transaction / error-handling logic lives in one place.
7
+ *
8
+ * Database : "majik-client-state"
9
+ * Store : "client-state"
10
+ * Version : 1
11
+ *
12
+ * Each entry is stored with `id` as the keyPath — identical to the invoice
13
+ * and contact adapters.
14
+ */
15
+ import { IDBGenericAdapter } from "../idb-adapter";
16
+ const IDB_DB_NAME = "majik-client-state";
17
+ const IDB_STORE_NAME = "client-state";
18
+ const IDB_VERSION = 1;
19
+ export const IDB_ADAPTER_CLIENT_STATE = new IDBGenericAdapter(IDB_DB_NAME, IDB_STORE_NAME, IDB_VERSION);
@@ -0,0 +1,20 @@
1
+ /**
2
+ * @file adapter-memory.ts
3
+ * @description In-memory ClientStateStorageAdapter.
4
+ *
5
+ * Default adapter — zero-config, non-persistent. State is lost on page reload
6
+ * or process restart. Useful for tests, SSR, and headless environments.
7
+ */
8
+ import type { ClientStateEntry, ClientStateStorageAdapter } from "./_types";
9
+ export declare class InMemoryClientStateAdapter implements ClientStateStorageAdapter {
10
+ private _store;
11
+ save(entry: ClientStateEntry): Promise<void>;
12
+ getById(id: string): Promise<ClientStateEntry | null>;
13
+ list(): Promise<ClientStateEntry[]>;
14
+ remove(id: string): Promise<boolean>;
15
+ clear(): Promise<void>;
16
+ count(): Promise<number>;
17
+ exists(id: string): Promise<boolean>;
18
+ bulkSave(entries: ClientStateEntry[]): Promise<void>;
19
+ bulkRemove(ids: string[]): Promise<void>;
20
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * @file adapter-memory.ts
3
+ * @description In-memory ClientStateStorageAdapter.
4
+ *
5
+ * Default adapter — zero-config, non-persistent. State is lost on page reload
6
+ * or process restart. Useful for tests, SSR, and headless environments.
7
+ */
8
+ export class InMemoryClientStateAdapter {
9
+ _store = new Map();
10
+ async save(entry) {
11
+ this._store.set(entry.id, {
12
+ ...entry,
13
+ updatedAt: new Date().toISOString(),
14
+ });
15
+ }
16
+ async getById(id) {
17
+ return this._store.get(id) ?? null;
18
+ }
19
+ async list() {
20
+ return Array.from(this._store.values());
21
+ }
22
+ async remove(id) {
23
+ return this._store.delete(id);
24
+ }
25
+ async clear() {
26
+ this._store.clear();
27
+ }
28
+ async count() {
29
+ return this._store.size;
30
+ }
31
+ async exists(id) {
32
+ return this._store.has(id);
33
+ }
34
+ async bulkSave(entries) {
35
+ const now = new Date().toISOString();
36
+ for (const entry of entries) {
37
+ this._store.set(entry.id, { ...entry, updatedAt: now });
38
+ }
39
+ }
40
+ async bulkRemove(ids) {
41
+ for (const id of ids)
42
+ this._store.delete(id);
43
+ }
44
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * @file adapter-sqlite.ts
3
+ * @description SQLite-backed ClientStateStorageAdapter.
4
+ *
5
+ * Schema (must exist before construction — call createSchema() or include in
6
+ * your db migration runner):
7
+ *
8
+ * ```sql
9
+ * CREATE TABLE IF NOT EXISTS majik_client_state (
10
+ * key TEXT PRIMARY KEY,
11
+ * value TEXT NOT NULL,
12
+ * updated_at TEXT DEFAULT (datetime('now'))
13
+ * );
14
+ * ```
15
+ *
16
+ * The column is named `key` in SQL (reserved-word-safe via quoting where
17
+ * needed) and mapped to the `id` field of ClientStateEntry in TypeScript so
18
+ * the adapter contract is identical to the IDB and memory variants.
19
+ */
20
+ import type { ClientStateEntry, ClientStateStorageAdapter } from "./_types";
21
+ import type { SQLiteDatabase } from "../sql-db-manager";
22
+ /** DDL — pass to your migration runner or call createSchema() directly. */
23
+ export declare const MAJIK_CLIENT_STATE_SCHEMA: string;
24
+ export declare class SQLiteClientStateAdapter implements ClientStateStorageAdapter {
25
+ private db;
26
+ constructor(db: SQLiteDatabase);
27
+ /**
28
+ * Ensure the table exists. Call this once during app initialisation if your
29
+ * migration runner does not already execute MAJIK_CLIENT_STATE_SCHEMA.
30
+ */
31
+ save(entry: ClientStateEntry): Promise<void>;
32
+ bulkSave(entries: ClientStateEntry[]): Promise<void>;
33
+ remove(id: string): Promise<boolean>;
34
+ bulkRemove(ids: string[]): Promise<void>;
35
+ clear(): Promise<void>;
36
+ getById(id: string): Promise<ClientStateEntry | null>;
37
+ list(): Promise<ClientStateEntry[]>;
38
+ count(): Promise<number>;
39
+ exists(id: string): Promise<boolean>;
40
+ private _rowToEntry;
41
+ }
@@ -0,0 +1,104 @@
1
+ /**
2
+ * @file adapter-sqlite.ts
3
+ * @description SQLite-backed ClientStateStorageAdapter.
4
+ *
5
+ * Schema (must exist before construction — call createSchema() or include in
6
+ * your db migration runner):
7
+ *
8
+ * ```sql
9
+ * CREATE TABLE IF NOT EXISTS majik_client_state (
10
+ * key TEXT PRIMARY KEY,
11
+ * value TEXT NOT NULL,
12
+ * updated_at TEXT DEFAULT (datetime('now'))
13
+ * );
14
+ * ```
15
+ *
16
+ * The column is named `key` in SQL (reserved-word-safe via quoting where
17
+ * needed) and mapped to the `id` field of ClientStateEntry in TypeScript so
18
+ * the adapter contract is identical to the IDB and memory variants.
19
+ */
20
+ const TABLE = "majik_client_state";
21
+ /** DDL — pass to your migration runner or call createSchema() directly. */
22
+ export const MAJIK_CLIENT_STATE_SCHEMA = `
23
+ CREATE TABLE IF NOT EXISTS ${TABLE} (
24
+ key TEXT PRIMARY KEY,
25
+ value TEXT NOT NULL,
26
+ updated_at TEXT DEFAULT (datetime('now'))
27
+ );
28
+ `.trim();
29
+ // ---------------------------------------------------------------------------
30
+ // Adapter
31
+ // ---------------------------------------------------------------------------
32
+ export class SQLiteClientStateAdapter {
33
+ db;
34
+ constructor(db) {
35
+ this.db = db;
36
+ }
37
+ // ── Schema helper ──────────────────────────────────────────────────────────
38
+ /**
39
+ * Ensure the table exists. Call this once during app initialisation if your
40
+ * migration runner does not already execute MAJIK_CLIENT_STATE_SCHEMA.
41
+ */
42
+ // async createSchema(): Promise<void> {
43
+ // await this.db.run(MAJIK_CLIENT_STATE_SCHEMA);
44
+ // }
45
+ // ── Write ──────────────────────────────────────────────────────────────────
46
+ async save(entry) {
47
+ await this.db.run(`INSERT OR REPLACE INTO ${TABLE} (key, value, updated_at)
48
+ VALUES (?, ?, datetime('now'))`, [entry.id, entry.value]);
49
+ }
50
+ async bulkSave(entries) {
51
+ if (entries.length === 0)
52
+ return;
53
+ await this.db.transaction(async (tx) => {
54
+ for (const entry of entries) {
55
+ await tx.run(`INSERT OR REPLACE INTO ${TABLE} (key, value, updated_at)
56
+ VALUES (?, ?, datetime('now'))`, [entry.id, entry.value]);
57
+ }
58
+ });
59
+ }
60
+ async remove(id) {
61
+ const exists = await this.exists(id);
62
+ if (!exists)
63
+ return false;
64
+ await this.db.run(`DELETE FROM ${TABLE} WHERE key = ?`, [id]);
65
+ return true;
66
+ }
67
+ async bulkRemove(ids) {
68
+ if (ids.length === 0)
69
+ return;
70
+ await this.db.transaction(async (tx) => {
71
+ for (const id of ids) {
72
+ await tx.run(`DELETE FROM ${TABLE} WHERE key = ?`, [id]);
73
+ }
74
+ });
75
+ }
76
+ async clear() {
77
+ await this.db.run(`DELETE FROM ${TABLE}`);
78
+ }
79
+ // ── Read ───────────────────────────────────────────────────────────────────
80
+ async getById(id) {
81
+ const row = await this.db.get(`SELECT key, value, updated_at FROM ${TABLE} WHERE key = ?`, [id]);
82
+ return row ? this._rowToEntry(row) : null;
83
+ }
84
+ async list() {
85
+ const rows = await this.db.all(`SELECT key, value, updated_at FROM ${TABLE}`);
86
+ return rows.map((r) => this._rowToEntry(r));
87
+ }
88
+ async count() {
89
+ const row = await this.db.get(`SELECT COUNT(*) as n FROM ${TABLE}`);
90
+ return row?.n ?? 0;
91
+ }
92
+ async exists(id) {
93
+ const row = await this.db.get(`SELECT 1 FROM ${TABLE} WHERE key = ?`, [id]);
94
+ return !!row;
95
+ }
96
+ // ── Private helpers ────────────────────────────────────────────────────────
97
+ _rowToEntry(row) {
98
+ return {
99
+ id: row.key,
100
+ value: row.value,
101
+ updatedAt: row.updated_at,
102
+ };
103
+ }
104
+ }
@@ -0,0 +1,3 @@
1
+ import { SerializedMajikContact } from "@majikah/majik-contact";
2
+ import { MajikStorageAdapter } from "../../storage-adapter";
3
+ export type MajikContactStorageAdapter = MajikStorageAdapter<SerializedMajikContact>;
@@ -0,0 +1,3 @@
1
+ import { SerializedMajikContact } from "@majikah/majik-contact";
2
+ import { IDBGenericAdapter } from "../../idb-adapter";
3
+ export declare const IDB_ADAPTER_CONTACT: IDBGenericAdapter<SerializedMajikContact>;
@@ -0,0 +1,5 @@
1
+ import { IDBGenericAdapter } from "../../idb-adapter";
2
+ const IDB_DB_NAME = "majik-contacts";
3
+ const IDB_STORE_NAME = "contacts";
4
+ const IDB_VERSION = 1;
5
+ export const IDB_ADAPTER_CONTACT = new IDBGenericAdapter(IDB_DB_NAME, IDB_STORE_NAME, IDB_VERSION);
@@ -0,0 +1,14 @@
1
+ import { SerializedMajikContact } from "@majikah/majik-contact";
2
+ import { MajikContactStorageAdapter } from "./_types";
3
+ export declare class InMemoryContactAdapter implements MajikContactStorageAdapter {
4
+ private _store;
5
+ save(contact: SerializedMajikContact): Promise<void>;
6
+ getById(id: string): Promise<SerializedMajikContact | null>;
7
+ list(): Promise<SerializedMajikContact[]>;
8
+ remove(id: string): Promise<boolean>;
9
+ clear(): Promise<void>;
10
+ count(): Promise<number>;
11
+ exists(id: string): Promise<boolean>;
12
+ bulkSave(contacts: SerializedMajikContact[]): Promise<void>;
13
+ bulkRemove(ids: string[]): Promise<void>;
14
+ }
@@ -0,0 +1,32 @@
1
+ export class InMemoryContactAdapter {
2
+ _store = new Map();
3
+ async save(contact) {
4
+ this._store.set(contact.id, contact);
5
+ }
6
+ async getById(id) {
7
+ return this._store.get(id) ?? null;
8
+ }
9
+ async list() {
10
+ return Array.from(this._store.values());
11
+ }
12
+ async remove(id) {
13
+ return this._store.delete(id);
14
+ }
15
+ async clear() {
16
+ this._store.clear();
17
+ }
18
+ async count() {
19
+ return this._store.size;
20
+ }
21
+ async exists(id) {
22
+ return this._store.has(id);
23
+ }
24
+ async bulkSave(contacts) {
25
+ for (const inv of contacts)
26
+ this._store.set(inv.id, inv);
27
+ }
28
+ async bulkRemove(ids) {
29
+ for (const id of ids)
30
+ this._store.delete(id);
31
+ }
32
+ }
@@ -0,0 +1,16 @@
1
+ import { SerializedMajikContact } from "@majikah/majik-contact";
2
+ import { MajikContactStorageAdapter } from "./_types";
3
+ import { SQLiteDatabase } from "../../sql-db-manager";
4
+ export declare class SQLiteContactAdapter implements MajikContactStorageAdapter {
5
+ private db;
6
+ constructor(db: SQLiteDatabase);
7
+ save(contact: SerializedMajikContact): Promise<void>;
8
+ getById(id: string): Promise<SerializedMajikContact | null>;
9
+ list(): Promise<SerializedMajikContact[]>;
10
+ remove(id: string): Promise<boolean>;
11
+ clear(): Promise<void>;
12
+ count(): Promise<number>;
13
+ exists(id: string): Promise<boolean>;
14
+ bulkSave(contacts: SerializedMajikContact[]): Promise<void>;
15
+ bulkRemove(ids: string[]): Promise<void>;
16
+ }
@@ -0,0 +1,73 @@
1
+ export class SQLiteContactAdapter {
2
+ db;
3
+ constructor(db) {
4
+ this.db = db;
5
+ }
6
+ async save(contact) {
7
+ await this.db.run(`INSERT OR REPLACE INTO majik_contacts
8
+ (id, json, fingerprint, label, created_at, updated_at)
9
+ VALUES (?, ?, ?, ?, ?, ?)`, [
10
+ contact.id,
11
+ JSON.stringify(contact),
12
+ contact.fingerprint ?? null,
13
+ contact.meta?.label ?? null,
14
+ contact.meta?.createdAt ?? null,
15
+ contact.meta?.updatedAt ?? null,
16
+ ]);
17
+ }
18
+ async getById(id) {
19
+ const row = await this.db.get("SELECT json FROM majik_contacts WHERE id = ?", [id]);
20
+ return row ? JSON.parse(row.json) : null;
21
+ }
22
+ async list() {
23
+ const rows = await this.db.all("SELECT json FROM majik_contacts");
24
+ return rows.map((r) => JSON.parse(r.json));
25
+ }
26
+ async remove(id) {
27
+ const exists = await this.exists(id);
28
+ if (!exists)
29
+ return false;
30
+ await this.db.run("DELETE FROM majik_contacts WHERE id = ?", [id]);
31
+ return true;
32
+ }
33
+ async clear() {
34
+ await this.db.run("DELETE FROM majik_contacts");
35
+ }
36
+ async count() {
37
+ const row = await this.db.get("SELECT COUNT(*) as n FROM majik_contacts");
38
+ return row?.n ?? 0;
39
+ }
40
+ async exists(id) {
41
+ const row = await this.db.get("SELECT 1 FROM majik_contacts WHERE id = ?", [
42
+ id,
43
+ ]);
44
+ return !!row;
45
+ }
46
+ async bulkSave(contacts) {
47
+ if (contacts.length === 0)
48
+ return;
49
+ await this.db.transaction(async (tx) => {
50
+ for (const c of contacts) {
51
+ await tx.run(`INSERT OR REPLACE INTO majik_contacts
52
+ (id, json, fingerprint, label, created_at, updated_at)
53
+ VALUES (?, ?, ?, ?, ?, ?)`, [
54
+ c.id,
55
+ JSON.stringify(c),
56
+ c.fingerprint ?? null,
57
+ c.meta?.label ?? null,
58
+ c.meta?.createdAt ?? null,
59
+ c.meta?.updatedAt ?? null,
60
+ ]);
61
+ }
62
+ });
63
+ }
64
+ async bulkRemove(ids) {
65
+ if (ids.length === 0)
66
+ return;
67
+ await this.db.transaction(async (tx) => {
68
+ for (const id of ids) {
69
+ await tx.run("DELETE FROM majik_contacts WHERE id = ?", [id]);
70
+ }
71
+ });
72
+ }
73
+ }
@@ -0,0 +1,3 @@
1
+ import { SerializedMajikContactGroup } from "@majikah/majik-contact";
2
+ import { MajikStorageAdapter } from "../../storage-adapter";
3
+ export type MajikContactGroupStorageAdapter = MajikStorageAdapter<SerializedMajikContactGroup>;
@@ -0,0 +1,3 @@
1
+ import { SerializedMajikContactGroup } from "@majikah/majik-contact";
2
+ import { IDBGenericAdapter } from "../../idb-adapter";
3
+ export declare const IDB_ADAPTER_CONTACT_GROUP: IDBGenericAdapter<SerializedMajikContactGroup>;
@@ -0,0 +1,5 @@
1
+ import { IDBGenericAdapter } from "../../idb-adapter";
2
+ const IDB_DB_NAME = "majik-contact-groups";
3
+ const IDB_STORE_NAME = "groups";
4
+ const IDB_VERSION = 1;
5
+ export const IDB_ADAPTER_CONTACT_GROUP = new IDBGenericAdapter(IDB_DB_NAME, IDB_STORE_NAME, IDB_VERSION);
@@ -0,0 +1,14 @@
1
+ import { SerializedMajikContactGroup } from "@majikah/majik-contact";
2
+ import { MajikContactGroupStorageAdapter } from "./_types";
3
+ export declare class InMemoryContactGroupAdapter implements MajikContactGroupStorageAdapter {
4
+ private _store;
5
+ save(invoice: SerializedMajikContactGroup): Promise<void>;
6
+ getById(id: string): Promise<SerializedMajikContactGroup | null>;
7
+ list(): Promise<SerializedMajikContactGroup[]>;
8
+ remove(id: string): Promise<boolean>;
9
+ clear(): Promise<void>;
10
+ count(): Promise<number>;
11
+ exists(id: string): Promise<boolean>;
12
+ bulkSave(invoices: SerializedMajikContactGroup[]): Promise<void>;
13
+ bulkRemove(ids: string[]): Promise<void>;
14
+ }
@@ -0,0 +1,32 @@
1
+ export class InMemoryContactGroupAdapter {
2
+ _store = new Map();
3
+ async save(invoice) {
4
+ this._store.set(invoice.id, invoice);
5
+ }
6
+ async getById(id) {
7
+ return this._store.get(id) ?? null;
8
+ }
9
+ async list() {
10
+ return Array.from(this._store.values());
11
+ }
12
+ async remove(id) {
13
+ return this._store.delete(id);
14
+ }
15
+ async clear() {
16
+ this._store.clear();
17
+ }
18
+ async count() {
19
+ return this._store.size;
20
+ }
21
+ async exists(id) {
22
+ return this._store.has(id);
23
+ }
24
+ async bulkSave(invoices) {
25
+ for (const inv of invoices)
26
+ this._store.set(inv.id, inv);
27
+ }
28
+ async bulkRemove(ids) {
29
+ for (const id of ids)
30
+ this._store.delete(id);
31
+ }
32
+ }
@@ -0,0 +1,16 @@
1
+ import { SerializedMajikContactGroup } from "@majikah/majik-contact";
2
+ import { MajikContactGroupStorageAdapter } from "./_types";
3
+ import { SQLiteDatabase } from "../../sql-db-manager";
4
+ export declare class SQLiteContactGroupAdapter implements MajikContactGroupStorageAdapter {
5
+ private db;
6
+ constructor(db: SQLiteDatabase);
7
+ save(group: SerializedMajikContactGroup): Promise<void>;
8
+ getById(id: string): Promise<SerializedMajikContactGroup | null>;
9
+ list(): Promise<SerializedMajikContactGroup[]>;
10
+ remove(id: string): Promise<boolean>;
11
+ clear(): Promise<void>;
12
+ count(): Promise<number>;
13
+ exists(id: string): Promise<boolean>;
14
+ bulkSave(groups: SerializedMajikContactGroup[]): Promise<void>;
15
+ bulkRemove(ids: string[]): Promise<void>;
16
+ }