ofplatform-electron 0.1.0-alpha.0 → 0.1.0-alpha.2

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.
package/README.md CHANGED
@@ -12,6 +12,8 @@ Electron platform adapter package for `of*` ecosystem.
12
12
  - `ElectronPlatformAdapter` (implements `PlatformAdapter`)
13
13
  - `src/adapters/storage`:
14
14
  - `ElectronStorageAdapter` (file-backed `StorageAdapter`)
15
+ - `src/auth`:
16
+ - `ElectronServerAuthoritativeAuthHostSupport` (shared auth bridge surface parity with `ofplatform-web`; runtime implementation intentionally scaffolded)
15
17
  - `src/adapters/desktop`:
16
18
  - `ElectronNotificationAdapter`
17
19
  - `ElectronExportAdapter`
@@ -21,6 +23,7 @@ Electron platform adapter package for `of*` ecosystem.
21
23
  Planned adapters:
22
24
  - Electron SQLCipher hardening profile (follow-up from current SQLite adapter)
23
25
  - Optional IPC-backed `HttpAdapter`/`SocketAdapter`
26
+ - Runtime-backed Electron auth host support that fulfills the scaffolded `src/auth` surface once host adoption starts
24
27
 
25
28
  Design rule:
26
29
  - Keep platform-specific implementation in platform packages, not in `ofcore`.
@@ -1,12 +1,18 @@
1
1
  import type { ColumnDefinition, DbAdapter, LiteralValue, QueryOptions, TableSchema } from 'ofcore';
2
2
  export interface SQLiteAdapterOptions {
3
3
  dbPath: string;
4
+ tableNamePrefix?: string;
5
+ tableNameMapper?: (tableName: string) => string;
4
6
  }
5
7
  export declare class SQLiteAdapter implements DbAdapter {
6
8
  private readonly executor;
7
9
  private savepointDepth;
8
10
  private readonly schemaMap;
11
+ private readonly tableNamePrefix;
12
+ private tableNameResolver?;
9
13
  constructor(options: SQLiteAdapterOptions);
14
+ setTableNameResolver(resolver: (logicalTableName: string) => string): void;
15
+ resolveTableName(tableName: string): string;
10
16
  getSchemaVersion(): Promise<number>;
11
17
  setSchemaVersion(version: number): Promise<void>;
12
18
  addTable(table: TableSchema): Promise<void>;
@@ -11,7 +11,6 @@ export declare class SQLiteExecutor {
11
11
  escapeIdentifier(id: string): string;
12
12
  mapType(type: string): string;
13
13
  parseRow<T>(row: any, columnMap: Record<string, ColumnDefinition>): T;
14
- denormalizeRow<T = Record<string, unknown>>(row: any, mainAlias?: string): T;
15
14
  buildFilter(expr: FilterExpression, mainAlias?: string): import("./SQLiteQueryBuilder").BuiltQuery;
16
15
  buildGroupBy(groupBy?: GroupBySpec[], mainAlias?: string): string;
17
16
  buildSort(sorts?: SortSpec[], mainAlias?: string): string;
@@ -2,6 +2,11 @@ import type { FilterExpression, GroupBySpec, LiteralValue, QueryOptions, SortSpe
2
2
  export interface BuiltQuery {
3
3
  sql: string;
4
4
  params: any[];
5
+ projectionMap?: Array<{
6
+ key: string;
7
+ alias: string;
8
+ field: string;
9
+ }>;
5
10
  }
6
11
  export declare class SQLiteQueryBuilder {
7
12
  private static escapeIdentifier;
@@ -0,0 +1,32 @@
1
+ import type { HttpAdapter, HttpRequest, HttpResponse } from 'ofcore';
2
+ export type ElectronFetchImplementation = (input: string, init?: RequestInit) => Promise<Response>;
3
+ export interface ElectronHttpAdapterOptions {
4
+ /**
5
+ * Override the fetch implementation.
6
+ * Defaults to `globalThis.fetch`, which is available in:
7
+ * - Electron renderer process (Chromium)
8
+ * - Electron main process (Node.js 18+)
9
+ *
10
+ * Useful for unit testing or when a custom agent (e.g. proxy, mTLS) is needed.
11
+ */
12
+ fetch?: ElectronFetchImplementation;
13
+ }
14
+ /**
15
+ * Electron HTTP adapter backed by the Fetch API.
16
+ *
17
+ * Works in both the renderer and main process of an Electron application.
18
+ * In the renderer the Chromium `fetch` is used; in the main process Node.js
19
+ * 18+ exposes `globalThis.fetch` using the same API surface.
20
+ *
21
+ * Features:
22
+ * - Query parameter serialisation (null/undefined values are omitted)
23
+ * - Automatic `content-type: application/json` for requests with a body
24
+ * - Timeout support via `AbortController`
25
+ * - Response body parsed as JSON when `content-type` indicates JSON,
26
+ * otherwise returns raw text
27
+ */
28
+ export declare class ElectronHttpAdapter implements HttpAdapter {
29
+ private readonly fetchImpl;
30
+ constructor(options?: ElectronHttpAdapterOptions);
31
+ request<T = unknown>(request: HttpRequest): Promise<HttpResponse<T>>;
32
+ }
@@ -1 +1,2 @@
1
- export declare const OFPLATFORM_ELECTRON_HTTP_ADAPTERS_READY = false;
1
+ export * from './ElectronHttpAdapter';
2
+ export declare const OFPLATFORM_ELECTRON_HTTP_ADAPTERS_READY = true;
@@ -0,0 +1,79 @@
1
+ import type { SocketAdapter, SocketConnectOptions, SocketEventHandler } from 'ofcore';
2
+ export interface ElectronWebSocketMessage {
3
+ event: string;
4
+ payload: unknown;
5
+ }
6
+ export interface ElectronSocketAdapterOptions {
7
+ /**
8
+ * Enable automatic reconnection on unexpected connection loss.
9
+ * @default true
10
+ */
11
+ reconnect?: boolean;
12
+ /**
13
+ * Maximum number of reconnection attempts before giving up.
14
+ * @default 5
15
+ */
16
+ maxReconnectAttempts?: number;
17
+ /**
18
+ * Initial delay between reconnection attempts in milliseconds.
19
+ * Grows exponentially on each subsequent attempt.
20
+ * @default 500
21
+ */
22
+ reconnectDelayMs?: number;
23
+ /**
24
+ * Maximum delay cap for exponential backoff in milliseconds.
25
+ * @default 30000
26
+ */
27
+ maxReconnectDelayMs?: number;
28
+ /**
29
+ * Override the WebSocket constructor. Defaults to `globalThis.WebSocket`.
30
+ *
31
+ * In the Electron renderer this is the Chromium WebSocket.
32
+ * In the Electron main process this requires Node.js 21+ or a polyfill
33
+ * (e.g. the `ws` package). Pass the constructor explicitly when running
34
+ * in the main process on older runtimes.
35
+ */
36
+ webSocket?: typeof WebSocket;
37
+ }
38
+ /**
39
+ * Electron WebSocket adapter implementing the `SocketAdapter` contract.
40
+ *
41
+ * ## Message protocol
42
+ * All messages on the wire are JSON-encoded `ElectronWebSocketMessage` objects:
43
+ * ```json
44
+ * { "event": "<event-name>", "payload": <any> }
45
+ * ```
46
+ *
47
+ * ## Headers limitation
48
+ * The WebSocket handshake does not support arbitrary HTTP headers in browser
49
+ * environments. Authentication tokens should be passed via
50
+ * `SocketConnectOptions.query` (e.g. `?token=...`) or via an HTTP-only cookie.
51
+ *
52
+ * ## Reconnection
53
+ * Unexpected disconnections trigger exponential-backoff reconnection unless
54
+ * `options.reconnect` is `false` or the maximum attempt count is reached.
55
+ * All existing subscriptions survive reconnection automatically.
56
+ */
57
+ export declare class ElectronSocketAdapter implements SocketAdapter {
58
+ private ws;
59
+ private connectionOptions;
60
+ private readonly listeners;
61
+ private reconnectAttempts;
62
+ private reconnectTimeout;
63
+ private intentionalClose;
64
+ private readonly reconnect;
65
+ private readonly maxReconnectAttempts;
66
+ private readonly reconnectDelayMs;
67
+ private readonly maxReconnectDelayMs;
68
+ private readonly WebSocketCtor;
69
+ constructor(options?: ElectronSocketAdapterOptions);
70
+ connect(options: SocketConnectOptions): Promise<void>;
71
+ disconnect(): Promise<void>;
72
+ subscribe(eventName: string, handler: SocketEventHandler): () => void;
73
+ publish(eventName: string, payload: unknown): Promise<void>;
74
+ isConnected(): boolean;
75
+ private openConnection;
76
+ private handleMessage;
77
+ private scheduleReconnect;
78
+ private clearReconnectTimeout;
79
+ }
@@ -1 +1,2 @@
1
- export declare const OFPLATFORM_ELECTRON_SOCKET_ADAPTERS_READY = false;
1
+ export * from './ElectronSocketAdapter';
2
+ export declare const OFPLATFORM_ELECTRON_SOCKET_ADAPTERS_READY = true;
@@ -0,0 +1,66 @@
1
+ import { type AuthoritySessionEnvelope, type CachedIdentityEntry } from 'ofauth-shared-core';
2
+ import type { StorageAdapter } from 'ofcore';
3
+ export type PlatformAuthoritySessionEnvelope = AuthoritySessionEnvelope;
4
+ type CacheRecord = {
5
+ identity?: CachedIdentityEntry;
6
+ envelope?: PlatformAuthoritySessionEnvelope;
7
+ };
8
+ export type ElectronServerAuthoritativeAuthHostSupportOptions = {
9
+ serverBaseUrl?: string;
10
+ cacheKey: string;
11
+ targetTenantId?: string;
12
+ targetBranchId?: string;
13
+ targetDomainId?: string;
14
+ storage?: StorageAdapter;
15
+ fetchImpl?: typeof fetch;
16
+ isOnline?: () => boolean;
17
+ verifyOfflineSecret?: (params: {
18
+ principal: string;
19
+ secret: string;
20
+ }) => Promise<boolean>;
21
+ };
22
+ export type ElectronServerAuthoritativeLoginResult = {
23
+ status: 'authenticated';
24
+ mode?: 'online' | 'offline';
25
+ } | {
26
+ status: 'rejected';
27
+ reasonCode?: string;
28
+ };
29
+ export type ElectronRestoredAuthEnvelope = {
30
+ principal: string;
31
+ envelope: PlatformAuthoritySessionEnvelope;
32
+ };
33
+ export type PlatformAuthSupportCapability = {
34
+ platform: 'electron';
35
+ implemented: boolean;
36
+ notes: string;
37
+ };
38
+ export declare const OFPLATFORM_ELECTRON_AUTH_STATUS: PlatformAuthSupportCapability;
39
+ export declare class ElectronServerAuthoritativeAuthHostSupport {
40
+ private readonly options;
41
+ private readonly serverBaseUrl;
42
+ private readonly cacheKey;
43
+ private readonly targetTenantId;
44
+ private readonly targetBranchId;
45
+ private readonly targetDomainId;
46
+ private readonly storage?;
47
+ private readonly fetchImpl;
48
+ private readonly isOnlineResolver;
49
+ private readonly serverBridge;
50
+ constructor(options: ElectronServerAuthoritativeAuthHostSupportOptions);
51
+ get isServerConfigured(): boolean;
52
+ get capability(): PlatformAuthSupportCapability;
53
+ get cacheNamespace(): string;
54
+ private loadCacheMap;
55
+ private saveCacheMap;
56
+ readCacheRecord(principal: string): Promise<CacheRecord | null>;
57
+ writeCacheRecord(principal: string, record: CacheRecord): Promise<void>;
58
+ clearCachedSessionEnvelope(principal: string): Promise<void>;
59
+ clearAllCachedSessionEnvelopes(): Promise<void>;
60
+ login(principal: string, secret: string): Promise<ElectronServerAuthoritativeLoginResult>;
61
+ logout(principal: string): Promise<void>;
62
+ restoreCachedEnvelope(): Promise<ElectronRestoredAuthEnvelope | null>;
63
+ getStoredAccessToken(principal: string): Promise<string | null>;
64
+ getStoredEnvelope(principal: string): Promise<PlatformAuthoritySessionEnvelope | null>;
65
+ }
66
+ export {};
@@ -0,0 +1 @@
1
+ export * from './ElectronServerAuthoritativeAuthHostSupport';
package/dist/index.d.ts CHANGED
@@ -4,6 +4,7 @@ export * from './adapters/desktop';
4
4
  export * from './adapters/http';
5
5
  export * from './adapters/socket';
6
6
  export * from './adapters/storage';
7
+ export * from './auth';
7
8
  export interface ElectronPlatformModuleStatus {
8
9
  platform: 'electron';
9
10
  ready: boolean;
package/dist/index.esm.js CHANGED
@@ -1,4 +1,4 @@
1
- import A from"better-sqlite3";var c=class{static escapeIdentifier(e){if(!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(e))throw new Error(`Invalid identifier: "${e}"`);return`"${e}"`}static normalizeValue(e){return e==null?null:typeof e=="boolean"?e?1:0:e instanceof Date?e.toISOString():Array.isArray(e)||typeof e=="object"&&e!==null?JSON.stringify(e):e}static buildValueExpr(e,t){if(e.type==="column"){let r=e.tableAlias||t;return{sql:`${this.escapeIdentifier(r)}.${this.escapeIdentifier(e.field)}`,params:[]}}return{sql:"?",params:[this.normalizeValue(e.value)]}}static buildFieldCondition(e,t){if("field"in e){let i=e.alias||t,s=`${this.escapeIdentifier(i)}.${this.escapeIdentifier(e.field)}`,o=e.operator||"=";if(o==="BETWEEN"){if(!Array.isArray(e.value)||e.value.length!==2)throw new Error("BETWEEN requires value tuple [min,max]");return{sql:`${s} BETWEEN ? AND ?`,params:[this.normalizeValue(e.value[0]),this.normalizeValue(e.value[1])]}}if(o==="IN"||o==="NOT IN"){if(!Array.isArray(e.value))throw new Error(`${o} requires array value`);return e.value.length===0?{sql:o==="IN"?"FALSE":"TRUE",params:[]}:{sql:`${s} ${o} (${e.value.map(()=>"?").join(", ")})`,params:e.value.map(a=>this.normalizeValue(a))}}if(o==="IS NULL"||o==="IS NOT NULL")return{sql:`${s} ${o}`,params:[]};if(o==="LIKE"){let l=("likeMode"in e?e.likeMode:"default")==="case-sensitive"?" COLLATE BINARY":"";return{sql:`${s} LIKE${l} ?`,params:[this.normalizeValue(e.value)]}}return{sql:`${s} ${o} ?`,params:[this.normalizeValue(e.value)]}}let r=this.buildValueExpr(e.left,t);if(e.operator==="IS NULL"||e.operator==="IS NOT NULL")return{sql:`${r.sql} ${e.operator}`,params:r.params};let n=this.buildValueExpr(e.right,t);if(e.operator==="IN"||e.operator==="NOT IN"){if(e.right.type!=="literal"||!Array.isArray(e.right.value))throw new Error(`${e.operator} requires literal array on right side`);return e.right.value.length===0?{sql:e.operator==="IN"?"FALSE":"TRUE",params:[]}:{sql:`${r.sql} ${e.operator} (${e.right.value.map(()=>"?").join(", ")})`,params:[...r.params,...e.right.value.map(i=>this.normalizeValue(i))]}}if(e.operator==="BETWEEN"){if(e.right.type!=="literal"||!Array.isArray(e.right.value)||e.right.value.length!==2)throw new Error("BETWEEN requires literal tuple [min,max] on right side");return{sql:`${r.sql} BETWEEN ? AND ?`,params:[...r.params,this.normalizeValue(e.right.value[0]),this.normalizeValue(e.right.value[1])]}}if(e.operator==="LIKE"){let i=e.likeMode==="case-sensitive"?" COLLATE BINARY":"";return{sql:`${r.sql} LIKE${i} ${n.sql}`,params:[...r.params,...n.params]}}return{sql:`${r.sql} ${e.operator} ${n.sql}`,params:[...r.params,...n.params]}}static buildFilter(e,t="t0"){let r=e.and;if(Array.isArray(r)){let i=r.map(s=>this.buildFilter(s,t));return{sql:i.map(s=>`(${s.sql})`).join(" AND "),params:i.flatMap(s=>s.params)}}let n=e.or;if(Array.isArray(n)){let i=n.map(s=>this.buildFilter(s,t));return{sql:i.map(s=>`(${s.sql})`).join(" OR "),params:i.flatMap(s=>s.params)}}if(e!==null&&typeof e=="object"&&!Array.isArray(e)){if("left"in e||"field"in e)return this.buildFieldCondition(e,t);let i=Object.entries(e);if(i.length===0)return{sql:"TRUE",params:[]};let s=i.map(([o,a])=>({sql:`${`${this.escapeIdentifier(t)}.${this.escapeIdentifier(o)}`} = ?`,params:[this.normalizeValue(a)]}));return{sql:s.map(o=>`(${o.sql})`).join(" AND "),params:s.flatMap(o=>o.params)}}throw new Error(`Invalid filter expression: ${JSON.stringify(e)}`)}static buildGroupBy(e,t="t0"){return!e||!e.length?"":` GROUP BY ${e.map(n=>{let i=typeof n.field=="string"?{tableAlias:t,field:n.field}:n.field;return`${this.escapeIdentifier(i.tableAlias)}.${this.escapeIdentifier(i.field)}`}).join(", ")}`}static buildSort(e,t="t0"){return!e||!e.length?"":e.map(r=>{let n=typeof r.field=="string"?{tableAlias:t,field:r.field}:r.field;return`${this.escapeIdentifier(n.tableAlias)}.${this.escapeIdentifier(n.field)} ${r.direction.toUpperCase()}`}).join(", ")}static buildSelect(e,t){let r=(t==null?void 0:t.mainAlias)||"t0",n=(t==null?void 0:t.joins)||[],i=[],s=[];if(t!=null&&t.aggregates&&t.aggregates.length>0)t.groupBy&&t.groupBy.forEach(a=>{let l=typeof a.field=="string"?{tableAlias:r,field:a.field}:a.field;s.push(`${this.escapeIdentifier(l.tableAlias)}.${this.escapeIdentifier(l.field)} AS ${this.escapeIdentifier(`${l.tableAlias}_${l.field}`)}`)}),t.aggregates.forEach(a=>{let l=typeof a.field=="string"?{tableAlias:r,field:a.field}:a.field,u=`${this.escapeIdentifier(l.tableAlias)}.${this.escapeIdentifier(l.field)}`;s.push(`${a.function}(${u}) AS ${this.escapeIdentifier(a.as)}`)});else if(t!=null&&t.fields)for(let[a,l]of Object.entries(t.fields))for(let u of l)s.push(`${this.escapeIdentifier(a)}.${this.escapeIdentifier(u)} AS ${this.escapeIdentifier(`${a}_${u}`)}`);else s.push(`${this.escapeIdentifier(r)}.*`);let o=`SELECT ${s.join(", ")} FROM ${this.escapeIdentifier(e)} AS ${this.escapeIdentifier(r)}`;if(n.forEach(a=>{let l=(a.type||"inner").toUpperCase();if(l==="RIGHT"||l==="FULL")throw new Error(`SQLite adapter does not support ${l} JOIN`);let u=[];a.conditions.forEach(p=>{let h=this.buildValueExpr(p.left,r),g=this.buildValueExpr(p.right,r);u.push(`${h.sql} ${p.operator} ${g.sql}`),i.push(...h.params,...g.params)}),o+=` ${l} JOIN ${this.escapeIdentifier(a.table)} AS ${this.escapeIdentifier(a.alias)} ON ${u.join(" AND ")}`}),t!=null&&t.filters){let a=this.buildFilter(t.filters,r);a.sql&&a.sql!=="TRUE"&&(o+=` WHERE ${a.sql}`,i.push(...a.params))}if(t!=null&&t.aggregates&&t.aggregates.length>0){if(o+=this.buildGroupBy(t.groupBy,r),t.having){let a=this.buildFilter(t.having,r);a.sql&&a.sql!=="TRUE"&&(o+=` HAVING ${a.sql}`,i.push(...a.params))}return{sql:o,params:i}}if(o+=this.buildGroupBy(t==null?void 0:t.groupBy,r),t!=null&&t.having){let a=this.buildFilter(t.having,r);a.sql&&a.sql!=="TRUE"&&(o+=` HAVING ${a.sql}`,i.push(...a.params))}if(t!=null&&t.sort&&t.sort.length){let a=this.buildSort(t.sort,r);a&&(o+=` ORDER BY ${a}`)}return(t==null?void 0:t.limit)!==void 0&&(o+=" LIMIT ?",i.push(t.limit)),(t==null?void 0:t.offset)!==void 0&&(o+=" OFFSET ?",i.push(t.offset)),{sql:o,params:i}}static buildInsert(e,t){let r=Object.keys(t),n=r.map(a=>this.escapeIdentifier(a)).join(", "),i=r.map(()=>"?").join(", "),s=r.map(a=>this.normalizeValue(t[a]));return{sql:`INSERT INTO ${this.escapeIdentifier(e)} (${n}) VALUES (${i})`,params:s}}static buildUpdate(e,t,r){let n=Object.keys(r);if(!n.length)throw new Error("Cannot build UPDATE query with empty updates payload");let i=n.map(a=>`${this.escapeIdentifier(a)} = ?`).join(", "),s=n.map(a=>this.normalizeValue(r[a]));return{sql:`UPDATE ${this.escapeIdentifier(e)} SET ${i} WHERE id = ?`,params:[...s,t]}}static buildDelete(e,t){return{sql:`DELETE FROM ${this.escapeIdentifier(e)} WHERE id = ?`,params:[t]}}};var d=class{constructor(e){this.db=new A(e)}initMetaTable(){this.db.exec(`
1
+ import N from"better-sqlite3";var y=class{static escapeIdentifier(e){if(!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(e))throw new Error(`Invalid identifier: "${e}"`);return`"${e}"`}static normalizeValue(e){return e==null?null:typeof e=="boolean"?e?1:0:e instanceof Date?e.toISOString():Array.isArray(e)||typeof e=="object"&&e!==null?JSON.stringify(e):e}static buildValueExpr(e,t){if(e.type==="column"){let r=e.tableAlias||t;return{sql:`${this.escapeIdentifier(r)}.${this.escapeIdentifier(e.field)}`,params:[]}}return{sql:"?",params:[this.normalizeValue(e.value)]}}static buildFieldCondition(e,t){if("field"in e){let n=e.alias||t,s=`${this.escapeIdentifier(n)}.${this.escapeIdentifier(e.field)}`,a=e.operator||"=";if(a==="BETWEEN"){if(!Array.isArray(e.value)||e.value.length!==2)throw new Error("BETWEEN requires value tuple [min,max]");return{sql:`${s} BETWEEN ? AND ?`,params:[this.normalizeValue(e.value[0]),this.normalizeValue(e.value[1])]}}if(a==="IN"||a==="NOT IN"){if(!Array.isArray(e.value))throw new Error(`${a} requires array value`);return e.value.length===0?{sql:a==="IN"?"FALSE":"TRUE",params:[]}:{sql:`${s} ${a} (${e.value.map(()=>"?").join(", ")})`,params:e.value.map(c=>this.normalizeValue(c))}}if(a==="IS NULL"||a==="IS NOT NULL")return{sql:`${s} ${a}`,params:[]};if(a==="LIKE"){let l=("likeMode"in e?e.likeMode:"default")==="case-sensitive"?" COLLATE BINARY":"";return{sql:`${s} LIKE${l} ?`,params:[this.normalizeValue(e.value)]}}return{sql:`${s} ${a} ?`,params:[this.normalizeValue(e.value)]}}let r=this.buildValueExpr(e.left,t);if(e.operator==="IS NULL"||e.operator==="IS NOT NULL")return{sql:`${r.sql} ${e.operator}`,params:r.params};let i=this.buildValueExpr(e.right,t);if(e.operator==="IN"||e.operator==="NOT IN"){if(e.right.type!=="literal"||!Array.isArray(e.right.value))throw new Error(`${e.operator} requires literal array on right side`);return e.right.value.length===0?{sql:e.operator==="IN"?"FALSE":"TRUE",params:[]}:{sql:`${r.sql} ${e.operator} (${e.right.value.map(()=>"?").join(", ")})`,params:[...r.params,...e.right.value.map(n=>this.normalizeValue(n))]}}if(e.operator==="BETWEEN"){if(e.right.type!=="literal"||!Array.isArray(e.right.value)||e.right.value.length!==2)throw new Error("BETWEEN requires literal tuple [min,max] on right side");return{sql:`${r.sql} BETWEEN ? AND ?`,params:[...r.params,this.normalizeValue(e.right.value[0]),this.normalizeValue(e.right.value[1])]}}if(e.operator==="LIKE"){let n=e.likeMode==="case-sensitive"?" COLLATE BINARY":"";return{sql:`${r.sql} LIKE${n} ${i.sql}`,params:[...r.params,...i.params]}}return{sql:`${r.sql} ${e.operator} ${i.sql}`,params:[...r.params,...i.params]}}static buildFilter(e,t="t0"){let r=e.and;if(Array.isArray(r)){let n=r.map(s=>this.buildFilter(s,t));return{sql:n.map(s=>`(${s.sql})`).join(" AND "),params:n.flatMap(s=>s.params)}}let i=e.or;if(Array.isArray(i)){let n=i.map(s=>this.buildFilter(s,t));return{sql:n.map(s=>`(${s.sql})`).join(" OR "),params:n.flatMap(s=>s.params)}}if(e!==null&&typeof e=="object"&&!Array.isArray(e)){if("left"in e||"field"in e)return this.buildFieldCondition(e,t);let n=Object.entries(e);if(n.length===0)return{sql:"TRUE",params:[]};let s=n.map(([a,c])=>({sql:`${`${this.escapeIdentifier(t)}.${this.escapeIdentifier(a)}`} = ?`,params:[this.normalizeValue(c)]}));return{sql:s.map(a=>`(${a.sql})`).join(" AND "),params:s.flatMap(a=>a.params)}}throw new Error(`Invalid filter expression: ${JSON.stringify(e)}`)}static buildGroupBy(e,t="t0"){return!e||!e.length?"":` GROUP BY ${e.map(i=>{let n=typeof i.field=="string"?{tableAlias:t,field:i.field}:i.field;return`${this.escapeIdentifier(n.tableAlias)}.${this.escapeIdentifier(n.field)}`}).join(", ")}`}static buildSort(e,t="t0"){return!e||!e.length?"":e.map(r=>{let i=typeof r.field=="string"?{tableAlias:t,field:r.field}:r.field;return`${this.escapeIdentifier(i.tableAlias)}.${this.escapeIdentifier(i.field)} ${r.direction.toUpperCase()}`}).join(", ")}static buildSelect(e,t){var u;let r=t,i=(t==null?void 0:t.mainAlias)||"t0",n=(t==null?void 0:t.joins)||[],s=[],a=[],c=[];if(t!=null&&t.aggregates&&t.aggregates.length>0)t.groupBy&&t.groupBy.forEach(o=>{let p=typeof o.field=="string"?{tableAlias:i,field:o.field}:o.field;c.push(`${this.escapeIdentifier(p.tableAlias)}.${this.escapeIdentifier(p.field)} AS ${this.escapeIdentifier(`${p.tableAlias}_${p.field}`)}`)}),t.aggregates.forEach(o=>{let p=typeof o.field=="string"?{tableAlias:i,field:o.field}:o.field,f=`${this.escapeIdentifier(p.tableAlias)}.${this.escapeIdentifier(p.field)}`;c.push(`${o.function}(${f}) AS ${this.escapeIdentifier(o.as)}`)});else if(t!=null&&t.fields){let o=0;for(let[p,f]of Object.entries(t.fields))for(let d of f){let m=((u=r==null?void 0:r.resultShape)!=null?u:"flat")==="nested"?`__c${o++}`:`${p}_${d}`;a.push({key:m,alias:p,field:d}),c.push(`${this.escapeIdentifier(p)}.${this.escapeIdentifier(d)} AS ${this.escapeIdentifier(m)}`)}}else c.push(`${this.escapeIdentifier(i)}.*`);let l=`SELECT ${c.join(", ")} FROM ${this.escapeIdentifier(e)} AS ${this.escapeIdentifier(i)}`;if(n.forEach(o=>{let p=(o.type||"inner").toUpperCase();if(p==="RIGHT"||p==="FULL")throw new Error(`SQLite adapter does not support ${p} JOIN`);let f=[];o.conditions.forEach(d=>{let g=this.buildValueExpr(d.left,i),m=this.buildValueExpr(d.right,i);f.push(`${g.sql} ${d.operator} ${m.sql}`),s.push(...g.params,...m.params)}),l+=` ${p} JOIN ${this.escapeIdentifier(o.table)} AS ${this.escapeIdentifier(o.alias)} ON ${f.join(" AND ")}`}),t!=null&&t.filters){let o=this.buildFilter(t.filters,i);o.sql&&o.sql!=="TRUE"&&(l+=` WHERE ${o.sql}`,s.push(...o.params))}if(t!=null&&t.aggregates&&t.aggregates.length>0){if(l+=this.buildGroupBy(t.groupBy,i),t.having){let o=this.buildFilter(t.having,i);o.sql&&o.sql!=="TRUE"&&(l+=` HAVING ${o.sql}`,s.push(...o.params))}return{sql:l,params:s,projectionMap:a.length?a:void 0}}if(l+=this.buildGroupBy(t==null?void 0:t.groupBy,i),t!=null&&t.having){let o=this.buildFilter(t.having,i);o.sql&&o.sql!=="TRUE"&&(l+=` HAVING ${o.sql}`,s.push(...o.params))}if(t!=null&&t.sort&&t.sort.length){let o=this.buildSort(t.sort,i);o&&(l+=` ORDER BY ${o}`)}return(t==null?void 0:t.limit)!==void 0&&(l+=" LIMIT ?",s.push(t.limit)),(t==null?void 0:t.offset)!==void 0&&(l+=" OFFSET ?",s.push(t.offset)),{sql:l,params:s,projectionMap:a.length?a:void 0}}static buildInsert(e,t){let r=Object.keys(t),i=r.map(c=>this.escapeIdentifier(c)).join(", "),n=r.map(()=>"?").join(", "),s=r.map(c=>this.normalizeValue(t[c]));return{sql:`INSERT INTO ${this.escapeIdentifier(e)} (${i}) VALUES (${n})`,params:s}}static buildUpdate(e,t,r){let i=Object.keys(r);if(!i.length)throw new Error("Cannot build UPDATE query with empty updates payload");let n=i.map(c=>`${this.escapeIdentifier(c)} = ?`).join(", "),s=i.map(c=>this.normalizeValue(r[c]));return{sql:`UPDATE ${this.escapeIdentifier(e)} SET ${n} WHERE id = ?`,params:[...s,t]}}static buildDelete(e,t){return{sql:`DELETE FROM ${this.escapeIdentifier(e)} WHERE id = ?`,params:[t]}}};var T=class{constructor(e){this.db=new N(e)}initMetaTable(){this.db.exec(`
2
2
  CREATE TABLE IF NOT EXISTS _meta (
3
3
  key TEXT PRIMARY KEY,
4
4
  value TEXT
@@ -6,4 +6,4 @@ import A from"better-sqlite3";var c=class{static escapeIdentifier(e){if(!/^[a-zA
6
6
  CREATE TABLE IF NOT EXISTS schema_version (
7
7
  version INTEGER NOT NULL
8
8
  );
9
- `)}run(e,t=[]){return this.db.prepare(e).run(...t)}get(e,t=[]){return this.db.prepare(e).get(...t)}all(e,t=[]){return this.db.prepare(e).all(...t)}transaction(e){return this.db.transaction(e)()}escapeIdentifier(e){if(!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(e))throw new Error(`Invalid identifier: "${e}"`);return`"${e}"`}mapType(e){switch(e){case"string":return"TEXT";case"string[]":return"TEXT";case"number":return"REAL";case"boolean":return"INTEGER";case"json":return"TEXT";case"date":return"TEXT";default:return"TEXT"}}parseRow(e,t){let r={};for(let[n,i]of Object.entries(e!=null?e:{})){let s=t[n],o=i;if(i!=null&&s){if(s.type==="boolean")o=!!i;else if(s.type==="date"&&typeof i=="string"){let a=new Date(i);Number.isNaN(a.getTime())||(o=a)}else if((s.type==="json"||s.type==="string[]")&&typeof i=="string")try{o=JSON.parse(i)}catch(a){}}r[n]=o}return r}denormalizeRow(e,t="t0"){let r={},n={};for(let[i,s]of Object.entries(e!=null?e:{})){if(!i.includes("_")){r[i]=s;continue}let[o,a]=i.split("_",2);if(!o||!a){r[i]=s;continue}o===t?r[a]=s:(n[o]||(n[o]={}),n[o][a]=s)}return{...r,...n}}buildFilter(e,t="t0"){return c.buildFilter(e,t)}buildGroupBy(e,t="t0"){return c.buildGroupBy(e,t)}buildSort(e,t="t0"){return c.buildSort(e,t)}buildSelect(e,t){return c.buildSelect(e,t)}buildInsert(e,t){return c.buildInsert(e,t)}buildUpdate(e,t,r){return c.buildUpdate(e,t,r)}buildDelete(e,t){return c.buildDelete(e,t)}};var E=class{constructor(e){this.savepointDepth=0;this.schemaMap=new Map;this.executor=new d(e.dbPath),this.executor.initMetaTable()}async getSchemaVersion(){let e=this.executor.get("SELECT version FROM schema_version LIMIT 1");return e?e.version:(this.executor.run("INSERT INTO schema_version (version) VALUES (?)",[0]),0)}async setSchemaVersion(e){if(!this.executor.get("SELECT 1 FROM schema_version LIMIT 1")){this.executor.run("INSERT INTO schema_version (version) VALUES (?)",[e]);return}this.executor.run("UPDATE schema_version SET version = ?",[e])}async addTable(e){let t=e.columns.map(r=>{let n=`${this.executor.escapeIdentifier(r.name)} ${this.executor.mapType(r.type)}`;return r.isOptional||(n+=" NOT NULL"),r.name==="id"&&(n+=" PRIMARY KEY"),n}).join(", ");this.executor.run(`CREATE TABLE IF NOT EXISTS ${this.executor.escapeIdentifier(e.name)} (${t})`);for(let r of e.columns){if(!r.isIndexed)continue;let n=`idx_${e.name}_${r.name}`;this.executor.run(`CREATE INDEX IF NOT EXISTS ${this.executor.escapeIdentifier(n)} ON ${this.executor.escapeIdentifier(e.name)}(${this.executor.escapeIdentifier(r.name)})`)}this.schemaMap.set(e.name,e)}async addColumn(e,t){if(this.executor.all(`PRAGMA table_info(${this.executor.escapeIdentifier(e)})`).some(s=>s.name===t.name))return;let n=`ALTER TABLE ${this.executor.escapeIdentifier(e)} ADD COLUMN ${this.executor.escapeIdentifier(t.name)} ${this.executor.mapType(t.type)}`;if(!t.isOptional){let s=t.type==="boolean"?"0":t.type==="number"?"0.0":"''";n+=` DEFAULT ${s} NOT NULL`}if(this.executor.run(n),t.isIndexed){let s=`idx_${e}_${t.name}`;this.executor.run(`CREATE INDEX IF NOT EXISTS ${this.executor.escapeIdentifier(s)} ON ${this.executor.escapeIdentifier(e)}(${this.executor.escapeIdentifier(t.name)})`)}let i=this.schemaMap.get(e);i&&!i.columns.find(s=>s.name===t.name)&&(i.columns.push(t),this.schemaMap.set(e,i))}async get(e,t){var s;let r,n;return typeof t=="string"?n={id:t}:(r=t,n=r.filters),(s=(await this.query(e,{...r,filters:n,limit:1}))[0])!=null?s:null}async query(e,t){var a;let{sql:r,params:n}=this.executor.buildSelect(e,t),i=this.executor.all(r,n);if((a=t==null?void 0:t.aggregates)!=null&&a.length||t!=null&&t.fields)return i;let s=this.schemaMap.get(e);if(!s)return i;let o=Object.fromEntries(s.columns.map(l=>[l.name,l]));return i.map(l=>{let u=this.executor.denormalizeRow(l);return this.executor.parseRow(u,o)})}async create(e,t){var u,p;let{sql:r,params:n}=this.executor.buildInsert(e,t),i=this.executor.run(r,n),s=(p=t==null?void 0:t.id)!=null?p:(u=i.lastInsertRowid)==null?void 0:u.toString(),o=s?{...t,id:s}:{...t},a=this.schemaMap.get(e);if(!a)return o;let l=Object.fromEntries(a.columns.map(h=>[h.name,h]));return this.executor.parseRow(o,l)}async update(e,t,r){if(!Object.keys(r).length){let o=await this.get(e,t);if(!o)throw new Error(`Record not found: ${e}#${t}`);return o}let{sql:n,params:i}=this.executor.buildUpdate(e,t,r);this.executor.run(n,i);let s=await this.get(e,t);if(!s)throw new Error(`Record not found after update: ${e}#${t}`);return s}async delete(e,t){let{sql:r,params:n}=this.executor.buildDelete(e,t);this.executor.run(r,n)}async bulkCreate(e,t){return t.length?this.transaction(async r=>{let n=[];for(let i of t)n.push(await r.create(e,i));return n}):[]}async bulkUpdate(e,t){return t.length?this.transaction(async r=>{let n=[];for(let i of t)Object.keys(i.updates).length&&n.push(await r.update(e,i.id,i.updates));return n}):[]}async transaction(e){let t=this.savepointDepth===0,r=t?"BEGIN IMMEDIATE":`SAVEPOINT sp${this.savepointDepth}`;this.executor.run(r),this.savepointDepth+=1;try{let n=await e(this),i=t?"COMMIT":`RELEASE SAVEPOINT sp${this.savepointDepth-1}`;return this.executor.run(i),n}catch(n){let i=t?"ROLLBACK":`ROLLBACK TO SAVEPOINT sp${this.savepointDepth-1}`;try{this.executor.run(i)}catch(s){}throw n}finally{this.savepointDepth-=1}}};import{createHash as S}from"node:crypto";var y=class{getEnvVar(e){return process.env[e]}isOnline(){return typeof navigator!="undefined"&&typeof navigator.onLine!="undefined"?navigator.onLine:!0}async hashPin(e){return S("sha256").update(e).digest("hex")}async verifyPin(e,t){return await this.hashPin(e)===t}};var T=class{constructor(e){this.options=e}configure(e){}createChannel(e){}localNotification(e){this.options.notify(e)}};import b from"node:fs/promises";import x from"node:path";var I=class{constructor(e){this.options=e}getDocumentPath(e){return x.join(this.options.documentsPath,e)}async writeFile(e,t){await b.writeFile(e,t,"utf8")}async share(e){var t,r;await((r=(t=this.options).revealFile)==null?void 0:r.call(t,e))}};var J=!1;var K=!1;import m from"node:fs";import N from"node:path";var $=class{constructor(e){var r;let t=(r=e.fileName)!=null?r:"storage.json";m.mkdirSync(e.storageDir,{recursive:!0}),this.filePath=N.join(e.storageDir,t);try{this.data=JSON.parse(m.readFileSync(this.filePath,"utf-8"))}catch{this.data={}}}async getItem(e){let t=this.data[e];return t!==void 0?t:null}async setItem(e,t){this.data[e]=t,await m.promises.writeFile(this.filePath,JSON.stringify(this.data,null,2),"utf-8")}async removeItem(e){delete this.data[e],await m.promises.writeFile(this.filePath,JSON.stringify(this.data,null,2),"utf-8")}};var ie={platform:"electron",ready:!1,notes:"Scaffold only. Add concrete adapters (db/http/socket/storage) in this package."};export{I as ElectronExportAdapter,T as ElectronNotificationAdapter,y as ElectronPlatformAdapter,$ as ElectronStorageAdapter,J as OFPLATFORM_ELECTRON_HTTP_ADAPTERS_READY,K as OFPLATFORM_ELECTRON_SOCKET_ADAPTERS_READY,ie as OFPLATFORM_ELECTRON_STATUS,E as SQLiteAdapter,d as SQLiteExecutor,c as SQLiteQueryBuilder};
9
+ `)}run(e,t=[]){return this.db.prepare(e).run(...t)}get(e,t=[]){return this.db.prepare(e).get(...t)}all(e,t=[]){return this.db.prepare(e).all(...t)}transaction(e){return this.db.transaction(e)()}escapeIdentifier(e){if(!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(e))throw new Error(`Invalid identifier: "${e}"`);return`"${e}"`}mapType(e){switch(e){case"string":return"TEXT";case"string[]":return"TEXT";case"number":return"REAL";case"boolean":return"INTEGER";case"json":return"TEXT";case"date":return"TEXT";default:return"TEXT"}}parseRow(e,t){let r={};for(let[i,n]of Object.entries(e!=null?e:{})){let s=t[i],a=n;if(n!=null&&s){if(s.type==="boolean")a=!!n;else if(s.type==="date"&&typeof n=="string"){let c=new Date(n);Number.isNaN(c.getTime())||(a=c)}else if((s.type==="json"||s.type==="string[]")&&typeof n=="string")try{a=JSON.parse(n)}catch(c){}}r[i]=a}return r}buildFilter(e,t="t0"){return y.buildFilter(e,t)}buildGroupBy(e,t="t0"){return y.buildGroupBy(e,t)}buildSort(e,t="t0"){return y.buildSort(e,t)}buildSelect(e,t){return y.buildSelect(e,t)}buildInsert(e,t){return y.buildInsert(e,t)}buildUpdate(e,t,r){return y.buildUpdate(e,t,r)}buildDelete(e,t){return y.buildDelete(e,t)}};var b=class{constructor(e){this.savepointDepth=0;this.schemaMap=new Map;var t;this.executor=new T(e.dbPath),this.executor.initMetaTable(),this.tableNamePrefix=(t=e.tableNamePrefix)!=null?t:"",this.tableNameResolver=e.tableNameMapper}setTableNameResolver(e){this.tableNameResolver=e}resolveTableName(e){return this.tableNameResolver?String(this.tableNameResolver(e)||"").trim()||e:!this.tableNamePrefix||e.startsWith(this.tableNamePrefix)?e:`${this.tableNamePrefix}${e}`}async getSchemaVersion(){let e=this.executor.get("SELECT version FROM schema_version LIMIT 1");return e?e.version:(this.executor.run("INSERT INTO schema_version (version) VALUES (?)",[0]),0)}async setSchemaVersion(e){if(!this.executor.get("SELECT 1 FROM schema_version LIMIT 1")){this.executor.run("INSERT INTO schema_version (version) VALUES (?)",[e]);return}this.executor.run("UPDATE schema_version SET version = ?",[e])}async addTable(e){let t=this.resolveTableName(e.name),r=e.columns.map(i=>{let n=`${this.executor.escapeIdentifier(i.name)} ${this.executor.mapType(i.type)}`;return i.isOptional||(n+=" NOT NULL"),i.name==="id"&&(n+=" PRIMARY KEY"),n}).join(", ");this.executor.run(`CREATE TABLE IF NOT EXISTS ${this.executor.escapeIdentifier(t)} (${r})`);for(let i of e.columns){if(!i.isIndexed)continue;let n=`idx_${t}_${i.name}`;this.executor.run(`CREATE INDEX IF NOT EXISTS ${this.executor.escapeIdentifier(n)} ON ${this.executor.escapeIdentifier(t)}(${this.executor.escapeIdentifier(i.name)})`)}this.schemaMap.set(e.name,e)}async addColumn(e,t){let r=this.resolveTableName(e);if(this.executor.all(`PRAGMA table_info(${this.executor.escapeIdentifier(r)})`).some(a=>a.name===t.name))return;let n=`ALTER TABLE ${this.executor.escapeIdentifier(r)} ADD COLUMN ${this.executor.escapeIdentifier(t.name)} ${this.executor.mapType(t.type)}`;if(!t.isOptional){let a=t.type==="boolean"?"0":t.type==="number"?"0.0":"''";n+=` DEFAULT ${a} NOT NULL`}if(this.executor.run(n),t.isIndexed){let a=`idx_${r}_${t.name}`;this.executor.run(`CREATE INDEX IF NOT EXISTS ${this.executor.escapeIdentifier(a)} ON ${this.executor.escapeIdentifier(r)}(${this.executor.escapeIdentifier(t.name)})`)}let s=this.schemaMap.get(e);s&&!s.columns.find(a=>a.name===t.name)&&(s.columns.push(t),this.schemaMap.set(e,s))}async get(e,t){var s;let r,i;return typeof t=="string"?i={id:t}:(r=t,i=r.filters),(s=(await this.query(e,{...r,filters:i,limit:1}))[0])!=null?s:null}async query(e,t){var o,p,f;let r=this.resolveTableName(e),i=(o=t==null?void 0:t.joins)!=null&&o.length?{...t,joins:t.joins.map(d=>({...d,table:this.resolveTableName(d.table)}))}:t,{sql:n,params:s,projectionMap:a}=this.executor.buildSelect(r,i),c=this.executor.all(n,s);if((p=t==null?void 0:t.aggregates)!=null&&p.length)return c;if(t!=null&&t.fields)return((f=t==null?void 0:t.resultShape)!=null?f:"flat")==="nested"&&(a!=null&&a.length)?c.map(g=>{let m={};for(let v of a)m[v.alias]||(m[v.alias]={}),m[v.alias][v.field]=g[v.key];return m}):c;let l=this.schemaMap.get(e);if(!l)return c;let u=Object.fromEntries(l.columns.map(d=>[d.name,d]));return c.map(d=>this.executor.parseRow(d,u))}async create(e,t){var o,p;let r=this.resolveTableName(e),{sql:i,params:n}=this.executor.buildInsert(r,t),s=this.executor.run(i,n),a=(p=t==null?void 0:t.id)!=null?p:(o=s.lastInsertRowid)==null?void 0:o.toString(),c=a?{...t,id:a}:{...t},l=this.schemaMap.get(e);if(!l)return c;let u=Object.fromEntries(l.columns.map(f=>[f.name,f]));return this.executor.parseRow(c,u)}async update(e,t,r){if(!Object.keys(r).length){let c=await this.get(e,t);if(!c)throw new Error(`Record not found: ${e}#${t}`);return c}let i=this.resolveTableName(e),{sql:n,params:s}=this.executor.buildUpdate(i,t,r);this.executor.run(n,s);let a=await this.get(e,t);if(!a)throw new Error(`Record not found after update: ${e}#${t}`);return a}async delete(e,t){let r=this.resolveTableName(e),{sql:i,params:n}=this.executor.buildDelete(r,t);this.executor.run(i,n)}async bulkCreate(e,t){return t.length?this.transaction(async r=>{let i=[];for(let n of t)i.push(await r.create(e,n));return i}):[]}async bulkUpdate(e,t){return t.length?this.transaction(async r=>{let i=[];for(let n of t)Object.keys(n.updates).length&&i.push(await r.update(e,n.id,n.updates));return i}):[]}async transaction(e){let t=this.savepointDepth===0,r=t?"BEGIN IMMEDIATE":`SAVEPOINT sp${this.savepointDepth}`;this.executor.run(r),this.savepointDepth+=1;try{let i=await e(this),n=t?"COMMIT":`RELEASE SAVEPOINT sp${this.savepointDepth-1}`;return this.executor.run(n),i}catch(i){let n=t?"ROLLBACK":`ROLLBACK TO SAVEPOINT sp${this.savepointDepth-1}`;try{this.executor.run(n)}catch(s){}throw i}finally{this.savepointDepth-=1}}};import{createHash as k}from"node:crypto";var S=class{getEnvVar(e){return process.env[e]}isOnline(){return typeof navigator!="undefined"&&typeof navigator.onLine!="undefined"?navigator.onLine:!0}async hashPin(e){return k("sha256").update(e).digest("hex")}async verifyPin(e,t){return await this.hashPin(e)===t}};var A=class{constructor(e){this.options=e}configure(e){}createChannel(e){}localNotification(e){this.options.notify(e)}};import P from"node:fs/promises";import D from"node:path";var I=class{constructor(e){this.options=e}getDocumentPath(e){return D.join(this.options.documentsPath,e)}async writeFile(e,t){await P.writeFile(e,t,"utf8")}async share(e){var t,r;await((r=(t=this.options).revealFile)==null?void 0:r.call(t,e))}};function L(h,e){if(!e||Object.keys(e).length===0)return h;let t=new URLSearchParams;for(let[i,n]of Object.entries(e))n!=null&&t.set(i,String(n));let r=t.toString();return r?h.includes("?")?`${h}&${r}`:`${h}?${r}`:h}var w=class{constructor(e={}){let t=e.fetch;if(t)this.fetchImpl=t;else{let r=globalThis.fetch;if(typeof r!="function")throw new Error("ElectronHttpAdapter: globalThis.fetch is not available. Pass a fetch implementation via options.fetch, or ensure the app is running on Electron \u2265 20 / Node.js \u2265 18.");this.fetchImpl=r}}async request(e){var n,s;let t=L(e.url,e.query),r=typeof AbortController!="undefined"?new AbortController:null,i=r&&e.timeoutMs?setTimeout(()=>r.abort(),e.timeoutMs):null;try{let a=e.body!==void 0&&e.body!==null,c={...(n=e.headers)!=null?n:{}};a&&!c["content-type"]&&!c["Content-Type"]&&(c["content-type"]="application/json");let l=await this.fetchImpl(t,{method:e.method,headers:c,body:a?JSON.stringify(e.body):void 0,signal:r==null?void 0:r.signal}),u={};l.headers.forEach((f,d)=>{u[d.toLowerCase()]=f});let p=((s=u["content-type"])!=null?s:"").includes("application/json")?await l.json():await l.text();return{status:l.status,headers:u,data:p}}finally{i!==null&&clearTimeout(i)}}};var le=!0;function M(h){let e=h.query;if(!e||Object.keys(e).length===0)return h.url;let r=new URLSearchParams(e).toString();return h.url.includes("?")?`${h.url}&${r}`:`${h.url}?${r}`}var R=class{constructor(e={}){this.ws=null;this.connectionOptions=null;this.listeners=new Map;this.reconnectAttempts=0;this.reconnectTimeout=null;this.intentionalClose=!1;var r,i,n,s,a;this.reconnect=(r=e.reconnect)!=null?r:!0,this.maxReconnectAttempts=Math.max(0,(i=e.maxReconnectAttempts)!=null?i:5),this.reconnectDelayMs=Math.max(0,(n=e.reconnectDelayMs)!=null?n:500),this.maxReconnectDelayMs=Math.max(this.reconnectDelayMs,(s=e.maxReconnectDelayMs)!=null?s:3e4);let t=(a=e.webSocket)!=null?a:globalThis.WebSocket;if(typeof t!="function")throw new Error("ElectronSocketAdapter: globalThis.WebSocket is not available. Pass a WebSocket constructor via options.webSocket. In the Electron main process you can use the `ws` package.");this.WebSocketCtor=t}connect(e){return this.connectionOptions=e,this.intentionalClose=!1,this.reconnectAttempts=0,this.openConnection(e)}async disconnect(){this.intentionalClose=!0,this.clearReconnectTimeout(),this.ws&&(this.ws.close(1e3,"client disconnect"),this.ws=null)}subscribe(e,t){return this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(t),()=>{let r=this.listeners.get(e);r&&(r.delete(t),r.size===0&&this.listeners.delete(e))}}async publish(e,t){var i,n;if(!this.ws||this.ws.readyState!==this.ws.OPEN)throw new Error(`ElectronSocketAdapter: cannot publish "${e}" \u2014 not connected (readyState=${(n=(i=this.ws)==null?void 0:i.readyState)!=null?n:"null"}).`);let r={event:e,payload:t};this.ws.send(JSON.stringify(r))}isConnected(){return this.ws!==null&&this.ws.readyState===this.ws.OPEN}openConnection(e){return new Promise((t,r)=>{let i=M(e),n;try{n=new this.WebSocketCtor(i)}catch(s){r(s instanceof Error?s:new Error(String(s)));return}n.onopen=()=>{this.ws=n,this.reconnectAttempts=0,t()},n.onerror=()=>{this.ws||r(new Error("ElectronSocketAdapter: connection failed"))},n.onclose=()=>{this.ws===n&&(this.ws=null),!this.intentionalClose&&this.reconnect&&this.connectionOptions&&this.reconnectAttempts<this.maxReconnectAttempts&&this.scheduleReconnect()},n.onmessage=s=>{this.handleMessage(s.data)}})}handleMessage(e){if(typeof e!="string")return;let t;try{t=JSON.parse(e)}catch{return}if(typeof t!="object"||t===null||typeof t.event!="string")return;let{event:r,payload:i}=t,n=this.listeners.get(r);if(n)for(let s of n)try{s(i)}catch{}}scheduleReconnect(){this.clearReconnectTimeout();let e=++this.reconnectAttempts,t=Math.min(this.reconnectDelayMs*Math.pow(2,e-1),this.maxReconnectDelayMs);this.reconnectTimeout=setTimeout(()=>{!this.intentionalClose&&this.connectionOptions&&this.openConnection(this.connectionOptions).catch(()=>{})},t)}clearReconnectTimeout(){this.reconnectTimeout!==null&&(clearTimeout(this.reconnectTimeout),this.reconnectTimeout=null)}};var he=!0;import E from"node:fs";import B from"node:path";var x=class{constructor(e){var r;let t=(r=e.fileName)!=null?r:"storage.json";E.mkdirSync(e.storageDir,{recursive:!0}),this.filePath=B.join(e.storageDir,t);try{this.data=JSON.parse(E.readFileSync(this.filePath,"utf-8"))}catch{this.data={}}}async getItem(e){let t=this.data[e];return t!==void 0?t:null}async setItem(e,t){this.data[e]=t,await E.promises.writeFile(this.filePath,JSON.stringify(this.data,null,2),"utf-8")}async removeItem(e){delete this.data[e],await E.promises.writeFile(this.filePath,JSON.stringify(this.data,null,2),"utf-8")}};import{AuthRejectionError as O,createServerAuthoritativeAuthBridge as q}from"ofauth-shared-core";var j={platform:"electron",implemented:!0,notes:"API and Runtime are fully implemented for use with Electron Desktop apps."};function C(h,e){var i,n,s,a,c;let t=new Date().toISOString(),r=h&&typeof h=="object"?h:{};return{sessionId:String((i=r.sessionId)!=null?i:""),identityId:String((n=r.identityId)!=null?n:""),assuranceLevel:String((s=r.assuranceLevel)!=null?s:"basic"),issuedAt:String((a=r.issuedAt)!=null?a:t),expiresAt:String((c=r.expiresAt)!=null?c:e)}}var $=class{constructor(e){this.options=e;var i,n,s,a,c;if(this.serverBaseUrl=((i=e.serverBaseUrl)!=null?i:"").trim().replace(/\/+$/,""),this.cacheKey=e.cacheKey.trim(),this.targetTenantId=((n=e.targetTenantId)==null?void 0:n.trim())||"",this.targetBranchId=((s=e.targetBranchId)==null?void 0:s.trim())||"",this.targetDomainId=((a=e.targetDomainId)==null?void 0:a.trim())||"",this.storage=e.storage,e.fetchImpl)this.fetchImpl=e.fetchImpl;else if(typeof globalThis.fetch=="function")this.fetchImpl=globalThis.fetch.bind(globalThis);else throw new Error("ElectronServerAuthoritativeAuthHostSupport requires a fetch implementation");if(this.isOnlineResolver=(c=e.isOnline)!=null?c:(()=>!0),!this.serverBaseUrl){this.serverBridge=null;return}let t={login:async({principal:l,secret:u})=>{var d,g,m,v;let o=await this.fetchImpl(`${this.serverBaseUrl}/v1/auth/login`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({principal:l,secret:u,...this.targetTenantId?{targetTenantId:this.targetTenantId}:{},...this.targetBranchId?{targetBranchId:this.targetBranchId}:{},...this.targetDomainId?{targetDomainId:this.targetDomainId}:{}})}),p=await o.json().catch(()=>({}));if(!o.ok)throw new O(String((d=p.code)!=null?d:"AUTH_UNAUTHORIZED"));let f=new Date(Date.now()+15*6e4).toISOString();return{accessToken:String((g=p.accessToken)!=null?g:""),refreshToken:String((m=p.refreshToken)!=null?m:""),session:C(p.session,f),activeContext:(v=p.activeContext)!=null?v:null,activeRoleRef:p.activeRoleRef==null?"":String(p.activeRoleRef)}},refresh:async({refreshToken:l})=>{var f,d,g,m;let u=await this.fetchImpl(`${this.serverBaseUrl}/v1/auth/refresh`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:l})}),o=await u.json().catch(()=>({}));if(!u.ok)throw new O(String((f=o.code)!=null?f:"AUTH_UNAUTHORIZED"));let p=new Date(Date.now()+15*6e4).toISOString();return{accessToken:String((d=o.accessToken)!=null?d:""),refreshToken:String((g=o.refreshToken)!=null?g:""),session:C(o.session,p),activeContext:(m=o.activeContext)!=null?m:null}},logout:async({accessToken:l,refreshToken:u})=>{let o={"Content-Type":"application/json"};l&&(o.Authorization=`Bearer ${l}`),await this.fetchImpl(`${this.serverBaseUrl}/v1/auth/logout`,{method:"POST",headers:o,body:JSON.stringify({refreshToken:u})}).catch(()=>{})}},r={getCachedIdentity:async l=>{var u,o;return(o=(u=await this.readCacheRecord(l))==null?void 0:u.identity)!=null?o:null},setCachedIdentity:async l=>{let u=await this.readCacheRecord(l.principal);await this.writeCacheRecord(l.principal,{...u,identity:l})},clearCachedIdentity:async l=>{let u=await this.readCacheRecord(l);u&&await this.writeCacheRecord(l,{...u,identity:void 0})},getSessionEnvelope:async l=>{var u,o;return(o=(u=await this.readCacheRecord(l))==null?void 0:u.envelope)!=null?o:null},setSessionEnvelope:async(l,u)=>{let o=await this.readCacheRecord(l);await this.writeCacheRecord(l,{...o,envelope:u})},clearSessionEnvelope:async l=>{let u=await this.readCacheRecord(l);u&&await this.writeCacheRecord(l,{...u,envelope:void 0})}};this.serverBridge=q({authority:t,cache:r,isOnline:()=>this.isOnlineResolver(),verifyOfflineSecret:this.options.verifyOfflineSecret||(async()=>!1)})}get isServerConfigured(){return this.serverBaseUrl.length>0}get capability(){return j}get cacheNamespace(){return this.cacheKey}async loadCacheMap(){if(!this.storage)return{};try{let e=await this.storage.getItem(this.cacheKey);if(!e)return{};let t=JSON.parse(e);return t&&typeof t=="object"?t:{}}catch{return{}}}async saveCacheMap(e){if(this.storage)try{Object.keys(e).length===0?await this.storage.removeItem(this.cacheKey):await this.storage.setItem(this.cacheKey,JSON.stringify(e))}catch{}}async readCacheRecord(e){var r;return(r=(await this.loadCacheMap())[e])!=null?r:null}async writeCacheRecord(e,t){let r=await this.loadCacheMap(),i={};t.identity&&(i.identity=t.identity),t.envelope&&(i.envelope=t.envelope),!i.identity&&!i.envelope?delete r[e]:r[e]=i,await this.saveCacheMap(r)}async clearCachedSessionEnvelope(e){let t=await this.readCacheRecord(e);t&&await this.writeCacheRecord(e,{...t,envelope:void 0})}async clearAllCachedSessionEnvelopes(){let e=await this.loadCacheMap();for(let t of Object.keys(e)){let r=e[t];r&&(e[t]=r.identity?{identity:r.identity}:{},e[t].identity||delete e[t])}await this.saveCacheMap(e)}async login(e,t){var i,n;if(!this.serverBridge)return{status:"rejected",reasonCode:"AUTH_BRIDGE_NOT_CONFIGURED"};let r=await this.serverBridge.login({principal:e,secret:t,verifyMethod:"pin"});return r.status==="authenticated"?{status:"authenticated",mode:(i=r.mode)!=null?i:"online"}:{status:"rejected",reasonCode:(n=r.reasonCode)!=null?n:"AUTH_UNAUTHORIZED"}}async logout(e){this.serverBridge&&await this.serverBridge.logout(e)}async restoreCachedEnvelope(){var t,r,i;let e=await this.loadCacheMap();for(let n of Object.keys(e)){let s=(t=e[n])==null?void 0:t.envelope;if(!s)continue;if(this.isOnlineResolver()&&s.refreshToken&&this.serverBaseUrl)try{let c=await this.fetchImpl(`${this.serverBaseUrl}/v1/auth/refresh`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:s.refreshToken})}),l=await c.json().catch(()=>({}));if(c.ok&&l.accessToken){let u={...s,accessToken:String(l.accessToken),refreshToken:l.refreshToken?String(l.refreshToken):s.refreshToken,activeContext:(i=(r=l.activeContext)!=null?r:s.activeContext)!=null?i:null};return await this.writeCacheRecord(n,{...e[n],envelope:u}),{principal:n,envelope:u}}}catch{}let a=Date.parse(s.expiresAt);if(Number.isFinite(a)&&a>Date.now())return{principal:n,envelope:s}}return null}async getStoredAccessToken(e){var r,i;let t=await this.readCacheRecord(e);return(i=(r=t==null?void 0:t.envelope)==null?void 0:r.accessToken)!=null?i:null}async getStoredEnvelope(e){var r;let t=await this.readCacheRecord(e);return(r=t==null?void 0:t.envelope)!=null?r:null}};var Ie={platform:"electron",ready:!0,notes:"All adapter domains implemented: SQLiteAdapter (db), ElectronHttpAdapter (http), ElectronSocketAdapter (socket), ElectronStorageAdapter (storage), ElectronPlatformAdapter (platform), ElectronExportAdapter + ElectronNotificationAdapter (desktop). Auth bridge surface is scaffolded for parity with ofplatform-web."};export{I as ElectronExportAdapter,w as ElectronHttpAdapter,A as ElectronNotificationAdapter,S as ElectronPlatformAdapter,$ as ElectronServerAuthoritativeAuthHostSupport,R as ElectronSocketAdapter,x as ElectronStorageAdapter,j as OFPLATFORM_ELECTRON_AUTH_STATUS,le as OFPLATFORM_ELECTRON_HTTP_ADAPTERS_READY,he as OFPLATFORM_ELECTRON_SOCKET_ADAPTERS_READY,Ie as OFPLATFORM_ELECTRON_STATUS,b as SQLiteAdapter,T as SQLiteExecutor,y as SQLiteQueryBuilder};
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- "use strict";var L=Object.create;var E=Object.defineProperty;var q=Object.getOwnPropertyDescriptor;var w=Object.getOwnPropertyNames;var P=Object.getPrototypeOf,D=Object.prototype.hasOwnProperty;var F=(u,e)=>{for(var t in e)E(u,t,{get:e[t],enumerable:!0})},b=(u,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of w(e))!D.call(u,i)&&i!==t&&E(u,i,{get:()=>e[i],enumerable:!(r=q(e,i))||r.enumerable});return u};var d=(u,e,t)=>(t=u!=null?L(P(u)):{},b(e||!u||!u.__esModule?E(t,"default",{value:u,enumerable:!0}):t,u)),C=u=>b(E({},"__esModule",{value:!0}),u);var M={};F(M,{ElectronExportAdapter:()=>$,ElectronNotificationAdapter:()=>I,ElectronPlatformAdapter:()=>T,ElectronStorageAdapter:()=>A,OFPLATFORM_ELECTRON_HTTP_ADAPTERS_READY:()=>ie,OFPLATFORM_ELECTRON_SOCKET_ADAPTERS_READY:()=>ae,OFPLATFORM_ELECTRON_STATUS:()=>V,SQLiteAdapter:()=>y,SQLiteExecutor:()=>m,SQLiteQueryBuilder:()=>f});module.exports=C(M);var x=d(require("better-sqlite3"));var f=class{static escapeIdentifier(e){if(!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(e))throw new Error(`Invalid identifier: "${e}"`);return`"${e}"`}static normalizeValue(e){return e==null?null:typeof e=="boolean"?e?1:0:e instanceof Date?e.toISOString():Array.isArray(e)||typeof e=="object"&&e!==null?JSON.stringify(e):e}static buildValueExpr(e,t){if(e.type==="column"){let r=e.tableAlias||t;return{sql:`${this.escapeIdentifier(r)}.${this.escapeIdentifier(e.field)}`,params:[]}}return{sql:"?",params:[this.normalizeValue(e.value)]}}static buildFieldCondition(e,t){if("field"in e){let s=e.alias||t,a=`${this.escapeIdentifier(s)}.${this.escapeIdentifier(e.field)}`,o=e.operator||"=";if(o==="BETWEEN"){if(!Array.isArray(e.value)||e.value.length!==2)throw new Error("BETWEEN requires value tuple [min,max]");return{sql:`${a} BETWEEN ? AND ?`,params:[this.normalizeValue(e.value[0]),this.normalizeValue(e.value[1])]}}if(o==="IN"||o==="NOT IN"){if(!Array.isArray(e.value))throw new Error(`${o} requires array value`);return e.value.length===0?{sql:o==="IN"?"FALSE":"TRUE",params:[]}:{sql:`${a} ${o} (${e.value.map(()=>"?").join(", ")})`,params:e.value.map(n=>this.normalizeValue(n))}}if(o==="IS NULL"||o==="IS NOT NULL")return{sql:`${a} ${o}`,params:[]};if(o==="LIKE"){let l=("likeMode"in e?e.likeMode:"default")==="case-sensitive"?" COLLATE BINARY":"";return{sql:`${a} LIKE${l} ?`,params:[this.normalizeValue(e.value)]}}return{sql:`${a} ${o} ?`,params:[this.normalizeValue(e.value)]}}let r=this.buildValueExpr(e.left,t);if(e.operator==="IS NULL"||e.operator==="IS NOT NULL")return{sql:`${r.sql} ${e.operator}`,params:r.params};let i=this.buildValueExpr(e.right,t);if(e.operator==="IN"||e.operator==="NOT IN"){if(e.right.type!=="literal"||!Array.isArray(e.right.value))throw new Error(`${e.operator} requires literal array on right side`);return e.right.value.length===0?{sql:e.operator==="IN"?"FALSE":"TRUE",params:[]}:{sql:`${r.sql} ${e.operator} (${e.right.value.map(()=>"?").join(", ")})`,params:[...r.params,...e.right.value.map(s=>this.normalizeValue(s))]}}if(e.operator==="BETWEEN"){if(e.right.type!=="literal"||!Array.isArray(e.right.value)||e.right.value.length!==2)throw new Error("BETWEEN requires literal tuple [min,max] on right side");return{sql:`${r.sql} BETWEEN ? AND ?`,params:[...r.params,this.normalizeValue(e.right.value[0]),this.normalizeValue(e.right.value[1])]}}if(e.operator==="LIKE"){let s=e.likeMode==="case-sensitive"?" COLLATE BINARY":"";return{sql:`${r.sql} LIKE${s} ${i.sql}`,params:[...r.params,...i.params]}}return{sql:`${r.sql} ${e.operator} ${i.sql}`,params:[...r.params,...i.params]}}static buildFilter(e,t="t0"){let r=e.and;if(Array.isArray(r)){let s=r.map(a=>this.buildFilter(a,t));return{sql:s.map(a=>`(${a.sql})`).join(" AND "),params:s.flatMap(a=>a.params)}}let i=e.or;if(Array.isArray(i)){let s=i.map(a=>this.buildFilter(a,t));return{sql:s.map(a=>`(${a.sql})`).join(" OR "),params:s.flatMap(a=>a.params)}}if(e!==null&&typeof e=="object"&&!Array.isArray(e)){if("left"in e||"field"in e)return this.buildFieldCondition(e,t);let s=Object.entries(e);if(s.length===0)return{sql:"TRUE",params:[]};let a=s.map(([o,n])=>({sql:`${`${this.escapeIdentifier(t)}.${this.escapeIdentifier(o)}`} = ?`,params:[this.normalizeValue(n)]}));return{sql:a.map(o=>`(${o.sql})`).join(" AND "),params:a.flatMap(o=>o.params)}}throw new Error(`Invalid filter expression: ${JSON.stringify(e)}`)}static buildGroupBy(e,t="t0"){return!e||!e.length?"":` GROUP BY ${e.map(i=>{let s=typeof i.field=="string"?{tableAlias:t,field:i.field}:i.field;return`${this.escapeIdentifier(s.tableAlias)}.${this.escapeIdentifier(s.field)}`}).join(", ")}`}static buildSort(e,t="t0"){return!e||!e.length?"":e.map(r=>{let i=typeof r.field=="string"?{tableAlias:t,field:r.field}:r.field;return`${this.escapeIdentifier(i.tableAlias)}.${this.escapeIdentifier(i.field)} ${r.direction.toUpperCase()}`}).join(", ")}static buildSelect(e,t){let r=(t==null?void 0:t.mainAlias)||"t0",i=(t==null?void 0:t.joins)||[],s=[],a=[];if(t!=null&&t.aggregates&&t.aggregates.length>0)t.groupBy&&t.groupBy.forEach(n=>{let l=typeof n.field=="string"?{tableAlias:r,field:n.field}:n.field;a.push(`${this.escapeIdentifier(l.tableAlias)}.${this.escapeIdentifier(l.field)} AS ${this.escapeIdentifier(`${l.tableAlias}_${l.field}`)}`)}),t.aggregates.forEach(n=>{let l=typeof n.field=="string"?{tableAlias:r,field:n.field}:n.field,c=`${this.escapeIdentifier(l.tableAlias)}.${this.escapeIdentifier(l.field)}`;a.push(`${n.function}(${c}) AS ${this.escapeIdentifier(n.as)}`)});else if(t!=null&&t.fields)for(let[n,l]of Object.entries(t.fields))for(let c of l)a.push(`${this.escapeIdentifier(n)}.${this.escapeIdentifier(c)} AS ${this.escapeIdentifier(`${n}_${c}`)}`);else a.push(`${this.escapeIdentifier(r)}.*`);let o=`SELECT ${a.join(", ")} FROM ${this.escapeIdentifier(e)} AS ${this.escapeIdentifier(r)}`;if(i.forEach(n=>{let l=(n.type||"inner").toUpperCase();if(l==="RIGHT"||l==="FULL")throw new Error(`SQLite adapter does not support ${l} JOIN`);let c=[];n.conditions.forEach(p=>{let h=this.buildValueExpr(p.left,r),S=this.buildValueExpr(p.right,r);c.push(`${h.sql} ${p.operator} ${S.sql}`),s.push(...h.params,...S.params)}),o+=` ${l} JOIN ${this.escapeIdentifier(n.table)} AS ${this.escapeIdentifier(n.alias)} ON ${c.join(" AND ")}`}),t!=null&&t.filters){let n=this.buildFilter(t.filters,r);n.sql&&n.sql!=="TRUE"&&(o+=` WHERE ${n.sql}`,s.push(...n.params))}if(t!=null&&t.aggregates&&t.aggregates.length>0){if(o+=this.buildGroupBy(t.groupBy,r),t.having){let n=this.buildFilter(t.having,r);n.sql&&n.sql!=="TRUE"&&(o+=` HAVING ${n.sql}`,s.push(...n.params))}return{sql:o,params:s}}if(o+=this.buildGroupBy(t==null?void 0:t.groupBy,r),t!=null&&t.having){let n=this.buildFilter(t.having,r);n.sql&&n.sql!=="TRUE"&&(o+=` HAVING ${n.sql}`,s.push(...n.params))}if(t!=null&&t.sort&&t.sort.length){let n=this.buildSort(t.sort,r);n&&(o+=` ORDER BY ${n}`)}return(t==null?void 0:t.limit)!==void 0&&(o+=" LIMIT ?",s.push(t.limit)),(t==null?void 0:t.offset)!==void 0&&(o+=" OFFSET ?",s.push(t.offset)),{sql:o,params:s}}static buildInsert(e,t){let r=Object.keys(t),i=r.map(n=>this.escapeIdentifier(n)).join(", "),s=r.map(()=>"?").join(", "),a=r.map(n=>this.normalizeValue(t[n]));return{sql:`INSERT INTO ${this.escapeIdentifier(e)} (${i}) VALUES (${s})`,params:a}}static buildUpdate(e,t,r){let i=Object.keys(r);if(!i.length)throw new Error("Cannot build UPDATE query with empty updates payload");let s=i.map(n=>`${this.escapeIdentifier(n)} = ?`).join(", "),a=i.map(n=>this.normalizeValue(r[n]));return{sql:`UPDATE ${this.escapeIdentifier(e)} SET ${s} WHERE id = ?`,params:[...a,t]}}static buildDelete(e,t){return{sql:`DELETE FROM ${this.escapeIdentifier(e)} WHERE id = ?`,params:[t]}}};var m=class{constructor(e){this.db=new x.default(e)}initMetaTable(){this.db.exec(`
1
+ "use strict";var F=Object.create;var A=Object.defineProperty;var U=Object.getOwnPropertyDescriptor;var _=Object.getOwnPropertyNames;var V=Object.getPrototypeOf,W=Object.prototype.hasOwnProperty;var H=(u,e)=>{for(var t in e)A(u,t,{get:e[t],enumerable:!0})},k=(u,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of _(e))!W.call(u,i)&&i!==t&&A(u,i,{get:()=>e[i],enumerable:!(r=U(e,i))||r.enumerable});return u};var T=(u,e,t)=>(t=u!=null?F(V(u)):{},k(e||!u||!u.__esModule?A(t,"default",{value:u,enumerable:!0}):t,u)),Q=u=>k(A({},"__esModule",{value:!0}),u);var Y={};H(Y,{ElectronExportAdapter:()=>x,ElectronHttpAdapter:()=>O,ElectronNotificationAdapter:()=>R,ElectronPlatformAdapter:()=>w,ElectronServerAuthoritativeAuthHostSupport:()=>N,ElectronSocketAdapter:()=>C,ElectronStorageAdapter:()=>$,OFPLATFORM_ELECTRON_AUTH_STATUS:()=>j,OFPLATFORM_ELECTRON_HTTP_ADAPTERS_READY:()=>J,OFPLATFORM_ELECTRON_SOCKET_ADAPTERS_READY:()=>K,OFPLATFORM_ELECTRON_STATUS:()=>X,SQLiteAdapter:()=>I,SQLiteExecutor:()=>E,SQLiteQueryBuilder:()=>y});module.exports=Q(Y);var P=T(require("better-sqlite3"));var y=class{static escapeIdentifier(e){if(!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(e))throw new Error(`Invalid identifier: "${e}"`);return`"${e}"`}static normalizeValue(e){return e==null?null:typeof e=="boolean"?e?1:0:e instanceof Date?e.toISOString():Array.isArray(e)||typeof e=="object"&&e!==null?JSON.stringify(e):e}static buildValueExpr(e,t){if(e.type==="column"){let r=e.tableAlias||t;return{sql:`${this.escapeIdentifier(r)}.${this.escapeIdentifier(e.field)}`,params:[]}}return{sql:"?",params:[this.normalizeValue(e.value)]}}static buildFieldCondition(e,t){if("field"in e){let n=e.alias||t,s=`${this.escapeIdentifier(n)}.${this.escapeIdentifier(e.field)}`,a=e.operator||"=";if(a==="BETWEEN"){if(!Array.isArray(e.value)||e.value.length!==2)throw new Error("BETWEEN requires value tuple [min,max]");return{sql:`${s} BETWEEN ? AND ?`,params:[this.normalizeValue(e.value[0]),this.normalizeValue(e.value[1])]}}if(a==="IN"||a==="NOT IN"){if(!Array.isArray(e.value))throw new Error(`${a} requires array value`);return e.value.length===0?{sql:a==="IN"?"FALSE":"TRUE",params:[]}:{sql:`${s} ${a} (${e.value.map(()=>"?").join(", ")})`,params:e.value.map(c=>this.normalizeValue(c))}}if(a==="IS NULL"||a==="IS NOT NULL")return{sql:`${s} ${a}`,params:[]};if(a==="LIKE"){let l=("likeMode"in e?e.likeMode:"default")==="case-sensitive"?" COLLATE BINARY":"";return{sql:`${s} LIKE${l} ?`,params:[this.normalizeValue(e.value)]}}return{sql:`${s} ${a} ?`,params:[this.normalizeValue(e.value)]}}let r=this.buildValueExpr(e.left,t);if(e.operator==="IS NULL"||e.operator==="IS NOT NULL")return{sql:`${r.sql} ${e.operator}`,params:r.params};let i=this.buildValueExpr(e.right,t);if(e.operator==="IN"||e.operator==="NOT IN"){if(e.right.type!=="literal"||!Array.isArray(e.right.value))throw new Error(`${e.operator} requires literal array on right side`);return e.right.value.length===0?{sql:e.operator==="IN"?"FALSE":"TRUE",params:[]}:{sql:`${r.sql} ${e.operator} (${e.right.value.map(()=>"?").join(", ")})`,params:[...r.params,...e.right.value.map(n=>this.normalizeValue(n))]}}if(e.operator==="BETWEEN"){if(e.right.type!=="literal"||!Array.isArray(e.right.value)||e.right.value.length!==2)throw new Error("BETWEEN requires literal tuple [min,max] on right side");return{sql:`${r.sql} BETWEEN ? AND ?`,params:[...r.params,this.normalizeValue(e.right.value[0]),this.normalizeValue(e.right.value[1])]}}if(e.operator==="LIKE"){let n=e.likeMode==="case-sensitive"?" COLLATE BINARY":"";return{sql:`${r.sql} LIKE${n} ${i.sql}`,params:[...r.params,...i.params]}}return{sql:`${r.sql} ${e.operator} ${i.sql}`,params:[...r.params,...i.params]}}static buildFilter(e,t="t0"){let r=e.and;if(Array.isArray(r)){let n=r.map(s=>this.buildFilter(s,t));return{sql:n.map(s=>`(${s.sql})`).join(" AND "),params:n.flatMap(s=>s.params)}}let i=e.or;if(Array.isArray(i)){let n=i.map(s=>this.buildFilter(s,t));return{sql:n.map(s=>`(${s.sql})`).join(" OR "),params:n.flatMap(s=>s.params)}}if(e!==null&&typeof e=="object"&&!Array.isArray(e)){if("left"in e||"field"in e)return this.buildFieldCondition(e,t);let n=Object.entries(e);if(n.length===0)return{sql:"TRUE",params:[]};let s=n.map(([a,c])=>({sql:`${`${this.escapeIdentifier(t)}.${this.escapeIdentifier(a)}`} = ?`,params:[this.normalizeValue(c)]}));return{sql:s.map(a=>`(${a.sql})`).join(" AND "),params:s.flatMap(a=>a.params)}}throw new Error(`Invalid filter expression: ${JSON.stringify(e)}`)}static buildGroupBy(e,t="t0"){return!e||!e.length?"":` GROUP BY ${e.map(i=>{let n=typeof i.field=="string"?{tableAlias:t,field:i.field}:i.field;return`${this.escapeIdentifier(n.tableAlias)}.${this.escapeIdentifier(n.field)}`}).join(", ")}`}static buildSort(e,t="t0"){return!e||!e.length?"":e.map(r=>{let i=typeof r.field=="string"?{tableAlias:t,field:r.field}:r.field;return`${this.escapeIdentifier(i.tableAlias)}.${this.escapeIdentifier(i.field)} ${r.direction.toUpperCase()}`}).join(", ")}static buildSelect(e,t){var p;let r=t,i=(t==null?void 0:t.mainAlias)||"t0",n=(t==null?void 0:t.joins)||[],s=[],a=[],c=[];if(t!=null&&t.aggregates&&t.aggregates.length>0)t.groupBy&&t.groupBy.forEach(o=>{let h=typeof o.field=="string"?{tableAlias:i,field:o.field}:o.field;c.push(`${this.escapeIdentifier(h.tableAlias)}.${this.escapeIdentifier(h.field)} AS ${this.escapeIdentifier(`${h.tableAlias}_${h.field}`)}`)}),t.aggregates.forEach(o=>{let h=typeof o.field=="string"?{tableAlias:i,field:o.field}:o.field,f=`${this.escapeIdentifier(h.tableAlias)}.${this.escapeIdentifier(h.field)}`;c.push(`${o.function}(${f}) AS ${this.escapeIdentifier(o.as)}`)});else if(t!=null&&t.fields){let o=0;for(let[h,f]of Object.entries(t.fields))for(let d of f){let m=((p=r==null?void 0:r.resultShape)!=null?p:"flat")==="nested"?`__c${o++}`:`${h}_${d}`;a.push({key:m,alias:h,field:d}),c.push(`${this.escapeIdentifier(h)}.${this.escapeIdentifier(d)} AS ${this.escapeIdentifier(m)}`)}}else c.push(`${this.escapeIdentifier(i)}.*`);let l=`SELECT ${c.join(", ")} FROM ${this.escapeIdentifier(e)} AS ${this.escapeIdentifier(i)}`;if(n.forEach(o=>{let h=(o.type||"inner").toUpperCase();if(h==="RIGHT"||h==="FULL")throw new Error(`SQLite adapter does not support ${h} JOIN`);let f=[];o.conditions.forEach(d=>{let g=this.buildValueExpr(d.left,i),m=this.buildValueExpr(d.right,i);f.push(`${g.sql} ${d.operator} ${m.sql}`),s.push(...g.params,...m.params)}),l+=` ${h} JOIN ${this.escapeIdentifier(o.table)} AS ${this.escapeIdentifier(o.alias)} ON ${f.join(" AND ")}`}),t!=null&&t.filters){let o=this.buildFilter(t.filters,i);o.sql&&o.sql!=="TRUE"&&(l+=` WHERE ${o.sql}`,s.push(...o.params))}if(t!=null&&t.aggregates&&t.aggregates.length>0){if(l+=this.buildGroupBy(t.groupBy,i),t.having){let o=this.buildFilter(t.having,i);o.sql&&o.sql!=="TRUE"&&(l+=` HAVING ${o.sql}`,s.push(...o.params))}return{sql:l,params:s,projectionMap:a.length?a:void 0}}if(l+=this.buildGroupBy(t==null?void 0:t.groupBy,i),t!=null&&t.having){let o=this.buildFilter(t.having,i);o.sql&&o.sql!=="TRUE"&&(l+=` HAVING ${o.sql}`,s.push(...o.params))}if(t!=null&&t.sort&&t.sort.length){let o=this.buildSort(t.sort,i);o&&(l+=` ORDER BY ${o}`)}return(t==null?void 0:t.limit)!==void 0&&(l+=" LIMIT ?",s.push(t.limit)),(t==null?void 0:t.offset)!==void 0&&(l+=" OFFSET ?",s.push(t.offset)),{sql:l,params:s,projectionMap:a.length?a:void 0}}static buildInsert(e,t){let r=Object.keys(t),i=r.map(c=>this.escapeIdentifier(c)).join(", "),n=r.map(()=>"?").join(", "),s=r.map(c=>this.normalizeValue(t[c]));return{sql:`INSERT INTO ${this.escapeIdentifier(e)} (${i}) VALUES (${n})`,params:s}}static buildUpdate(e,t,r){let i=Object.keys(r);if(!i.length)throw new Error("Cannot build UPDATE query with empty updates payload");let n=i.map(c=>`${this.escapeIdentifier(c)} = ?`).join(", "),s=i.map(c=>this.normalizeValue(r[c]));return{sql:`UPDATE ${this.escapeIdentifier(e)} SET ${n} WHERE id = ?`,params:[...s,t]}}static buildDelete(e,t){return{sql:`DELETE FROM ${this.escapeIdentifier(e)} WHERE id = ?`,params:[t]}}};var E=class{constructor(e){this.db=new P.default(e)}initMetaTable(){this.db.exec(`
2
2
  CREATE TABLE IF NOT EXISTS _meta (
3
3
  key TEXT PRIMARY KEY,
4
4
  value TEXT
@@ -6,4 +6,4 @@
6
6
  CREATE TABLE IF NOT EXISTS schema_version (
7
7
  version INTEGER NOT NULL
8
8
  );
9
- `)}run(e,t=[]){return this.db.prepare(e).run(...t)}get(e,t=[]){return this.db.prepare(e).get(...t)}all(e,t=[]){return this.db.prepare(e).all(...t)}transaction(e){return this.db.transaction(e)()}escapeIdentifier(e){if(!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(e))throw new Error(`Invalid identifier: "${e}"`);return`"${e}"`}mapType(e){switch(e){case"string":return"TEXT";case"string[]":return"TEXT";case"number":return"REAL";case"boolean":return"INTEGER";case"json":return"TEXT";case"date":return"TEXT";default:return"TEXT"}}parseRow(e,t){let r={};for(let[i,s]of Object.entries(e!=null?e:{})){let a=t[i],o=s;if(s!=null&&a){if(a.type==="boolean")o=!!s;else if(a.type==="date"&&typeof s=="string"){let n=new Date(s);Number.isNaN(n.getTime())||(o=n)}else if((a.type==="json"||a.type==="string[]")&&typeof s=="string")try{o=JSON.parse(s)}catch(n){}}r[i]=o}return r}denormalizeRow(e,t="t0"){let r={},i={};for(let[s,a]of Object.entries(e!=null?e:{})){if(!s.includes("_")){r[s]=a;continue}let[o,n]=s.split("_",2);if(!o||!n){r[s]=a;continue}o===t?r[n]=a:(i[o]||(i[o]={}),i[o][n]=a)}return{...r,...i}}buildFilter(e,t="t0"){return f.buildFilter(e,t)}buildGroupBy(e,t="t0"){return f.buildGroupBy(e,t)}buildSort(e,t="t0"){return f.buildSort(e,t)}buildSelect(e,t){return f.buildSelect(e,t)}buildInsert(e,t){return f.buildInsert(e,t)}buildUpdate(e,t,r){return f.buildUpdate(e,t,r)}buildDelete(e,t){return f.buildDelete(e,t)}};var y=class{constructor(e){this.savepointDepth=0;this.schemaMap=new Map;this.executor=new m(e.dbPath),this.executor.initMetaTable()}async getSchemaVersion(){let e=this.executor.get("SELECT version FROM schema_version LIMIT 1");return e?e.version:(this.executor.run("INSERT INTO schema_version (version) VALUES (?)",[0]),0)}async setSchemaVersion(e){if(!this.executor.get("SELECT 1 FROM schema_version LIMIT 1")){this.executor.run("INSERT INTO schema_version (version) VALUES (?)",[e]);return}this.executor.run("UPDATE schema_version SET version = ?",[e])}async addTable(e){let t=e.columns.map(r=>{let i=`${this.executor.escapeIdentifier(r.name)} ${this.executor.mapType(r.type)}`;return r.isOptional||(i+=" NOT NULL"),r.name==="id"&&(i+=" PRIMARY KEY"),i}).join(", ");this.executor.run(`CREATE TABLE IF NOT EXISTS ${this.executor.escapeIdentifier(e.name)} (${t})`);for(let r of e.columns){if(!r.isIndexed)continue;let i=`idx_${e.name}_${r.name}`;this.executor.run(`CREATE INDEX IF NOT EXISTS ${this.executor.escapeIdentifier(i)} ON ${this.executor.escapeIdentifier(e.name)}(${this.executor.escapeIdentifier(r.name)})`)}this.schemaMap.set(e.name,e)}async addColumn(e,t){if(this.executor.all(`PRAGMA table_info(${this.executor.escapeIdentifier(e)})`).some(a=>a.name===t.name))return;let i=`ALTER TABLE ${this.executor.escapeIdentifier(e)} ADD COLUMN ${this.executor.escapeIdentifier(t.name)} ${this.executor.mapType(t.type)}`;if(!t.isOptional){let a=t.type==="boolean"?"0":t.type==="number"?"0.0":"''";i+=` DEFAULT ${a} NOT NULL`}if(this.executor.run(i),t.isIndexed){let a=`idx_${e}_${t.name}`;this.executor.run(`CREATE INDEX IF NOT EXISTS ${this.executor.escapeIdentifier(a)} ON ${this.executor.escapeIdentifier(e)}(${this.executor.escapeIdentifier(t.name)})`)}let s=this.schemaMap.get(e);s&&!s.columns.find(a=>a.name===t.name)&&(s.columns.push(t),this.schemaMap.set(e,s))}async get(e,t){var a;let r,i;return typeof t=="string"?i={id:t}:(r=t,i=r.filters),(a=(await this.query(e,{...r,filters:i,limit:1}))[0])!=null?a:null}async query(e,t){var n;let{sql:r,params:i}=this.executor.buildSelect(e,t),s=this.executor.all(r,i);if((n=t==null?void 0:t.aggregates)!=null&&n.length||t!=null&&t.fields)return s;let a=this.schemaMap.get(e);if(!a)return s;let o=Object.fromEntries(a.columns.map(l=>[l.name,l]));return s.map(l=>{let c=this.executor.denormalizeRow(l);return this.executor.parseRow(c,o)})}async create(e,t){var c,p;let{sql:r,params:i}=this.executor.buildInsert(e,t),s=this.executor.run(r,i),a=(p=t==null?void 0:t.id)!=null?p:(c=s.lastInsertRowid)==null?void 0:c.toString(),o=a?{...t,id:a}:{...t},n=this.schemaMap.get(e);if(!n)return o;let l=Object.fromEntries(n.columns.map(h=>[h.name,h]));return this.executor.parseRow(o,l)}async update(e,t,r){if(!Object.keys(r).length){let o=await this.get(e,t);if(!o)throw new Error(`Record not found: ${e}#${t}`);return o}let{sql:i,params:s}=this.executor.buildUpdate(e,t,r);this.executor.run(i,s);let a=await this.get(e,t);if(!a)throw new Error(`Record not found after update: ${e}#${t}`);return a}async delete(e,t){let{sql:r,params:i}=this.executor.buildDelete(e,t);this.executor.run(r,i)}async bulkCreate(e,t){return t.length?this.transaction(async r=>{let i=[];for(let s of t)i.push(await r.create(e,s));return i}):[]}async bulkUpdate(e,t){return t.length?this.transaction(async r=>{let i=[];for(let s of t)Object.keys(s.updates).length&&i.push(await r.update(e,s.id,s.updates));return i}):[]}async transaction(e){let t=this.savepointDepth===0,r=t?"BEGIN IMMEDIATE":`SAVEPOINT sp${this.savepointDepth}`;this.executor.run(r),this.savepointDepth+=1;try{let i=await e(this),s=t?"COMMIT":`RELEASE SAVEPOINT sp${this.savepointDepth-1}`;return this.executor.run(s),i}catch(i){let s=t?"ROLLBACK":`ROLLBACK TO SAVEPOINT sp${this.savepointDepth-1}`;try{this.executor.run(s)}catch(a){}throw i}finally{this.savepointDepth-=1}}};var N=require("node:crypto"),T=class{getEnvVar(e){return process.env[e]}isOnline(){return typeof navigator!="undefined"&&typeof navigator.onLine!="undefined"?navigator.onLine:!0}async hashPin(e){return(0,N.createHash)("sha256").update(e).digest("hex")}async verifyPin(e,t){return await this.hashPin(e)===t}};var I=class{constructor(e){this.options=e}configure(e){}createChannel(e){}localNotification(e){this.options.notify(e)}};var O=d(require("node:fs/promises")),v=d(require("node:path")),$=class{constructor(e){this.options=e}getDocumentPath(e){return v.default.join(this.options.documentsPath,e)}async writeFile(e,t){await O.default.writeFile(e,t,"utf8")}async share(e){var t,r;await((r=(t=this.options).revealFile)==null?void 0:r.call(t,e))}};var ie=!1;var ae=!1;var g=d(require("node:fs")),R=d(require("node:path")),A=class{constructor(e){var r;let t=(r=e.fileName)!=null?r:"storage.json";g.default.mkdirSync(e.storageDir,{recursive:!0}),this.filePath=R.default.join(e.storageDir,t);try{this.data=JSON.parse(g.default.readFileSync(this.filePath,"utf-8"))}catch{this.data={}}}async getItem(e){let t=this.data[e];return t!==void 0?t:null}async setItem(e,t){this.data[e]=t,await g.default.promises.writeFile(this.filePath,JSON.stringify(this.data,null,2),"utf-8")}async removeItem(e){delete this.data[e],await g.default.promises.writeFile(this.filePath,JSON.stringify(this.data,null,2),"utf-8")}};var V={platform:"electron",ready:!1,notes:"Scaffold only. Add concrete adapters (db/http/socket/storage) in this package."};0&&(module.exports={ElectronExportAdapter,ElectronNotificationAdapter,ElectronPlatformAdapter,ElectronStorageAdapter,OFPLATFORM_ELECTRON_HTTP_ADAPTERS_READY,OFPLATFORM_ELECTRON_SOCKET_ADAPTERS_READY,OFPLATFORM_ELECTRON_STATUS,SQLiteAdapter,SQLiteExecutor,SQLiteQueryBuilder});
9
+ `)}run(e,t=[]){return this.db.prepare(e).run(...t)}get(e,t=[]){return this.db.prepare(e).get(...t)}all(e,t=[]){return this.db.prepare(e).all(...t)}transaction(e){return this.db.transaction(e)()}escapeIdentifier(e){if(!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(e))throw new Error(`Invalid identifier: "${e}"`);return`"${e}"`}mapType(e){switch(e){case"string":return"TEXT";case"string[]":return"TEXT";case"number":return"REAL";case"boolean":return"INTEGER";case"json":return"TEXT";case"date":return"TEXT";default:return"TEXT"}}parseRow(e,t){let r={};for(let[i,n]of Object.entries(e!=null?e:{})){let s=t[i],a=n;if(n!=null&&s){if(s.type==="boolean")a=!!n;else if(s.type==="date"&&typeof n=="string"){let c=new Date(n);Number.isNaN(c.getTime())||(a=c)}else if((s.type==="json"||s.type==="string[]")&&typeof n=="string")try{a=JSON.parse(n)}catch(c){}}r[i]=a}return r}buildFilter(e,t="t0"){return y.buildFilter(e,t)}buildGroupBy(e,t="t0"){return y.buildGroupBy(e,t)}buildSort(e,t="t0"){return y.buildSort(e,t)}buildSelect(e,t){return y.buildSelect(e,t)}buildInsert(e,t){return y.buildInsert(e,t)}buildUpdate(e,t,r){return y.buildUpdate(e,t,r)}buildDelete(e,t){return y.buildDelete(e,t)}};var I=class{constructor(e){this.savepointDepth=0;this.schemaMap=new Map;var t;this.executor=new E(e.dbPath),this.executor.initMetaTable(),this.tableNamePrefix=(t=e.tableNamePrefix)!=null?t:"",this.tableNameResolver=e.tableNameMapper}setTableNameResolver(e){this.tableNameResolver=e}resolveTableName(e){return this.tableNameResolver?String(this.tableNameResolver(e)||"").trim()||e:!this.tableNamePrefix||e.startsWith(this.tableNamePrefix)?e:`${this.tableNamePrefix}${e}`}async getSchemaVersion(){let e=this.executor.get("SELECT version FROM schema_version LIMIT 1");return e?e.version:(this.executor.run("INSERT INTO schema_version (version) VALUES (?)",[0]),0)}async setSchemaVersion(e){if(!this.executor.get("SELECT 1 FROM schema_version LIMIT 1")){this.executor.run("INSERT INTO schema_version (version) VALUES (?)",[e]);return}this.executor.run("UPDATE schema_version SET version = ?",[e])}async addTable(e){let t=this.resolveTableName(e.name),r=e.columns.map(i=>{let n=`${this.executor.escapeIdentifier(i.name)} ${this.executor.mapType(i.type)}`;return i.isOptional||(n+=" NOT NULL"),i.name==="id"&&(n+=" PRIMARY KEY"),n}).join(", ");this.executor.run(`CREATE TABLE IF NOT EXISTS ${this.executor.escapeIdentifier(t)} (${r})`);for(let i of e.columns){if(!i.isIndexed)continue;let n=`idx_${t}_${i.name}`;this.executor.run(`CREATE INDEX IF NOT EXISTS ${this.executor.escapeIdentifier(n)} ON ${this.executor.escapeIdentifier(t)}(${this.executor.escapeIdentifier(i.name)})`)}this.schemaMap.set(e.name,e)}async addColumn(e,t){let r=this.resolveTableName(e);if(this.executor.all(`PRAGMA table_info(${this.executor.escapeIdentifier(r)})`).some(a=>a.name===t.name))return;let n=`ALTER TABLE ${this.executor.escapeIdentifier(r)} ADD COLUMN ${this.executor.escapeIdentifier(t.name)} ${this.executor.mapType(t.type)}`;if(!t.isOptional){let a=t.type==="boolean"?"0":t.type==="number"?"0.0":"''";n+=` DEFAULT ${a} NOT NULL`}if(this.executor.run(n),t.isIndexed){let a=`idx_${r}_${t.name}`;this.executor.run(`CREATE INDEX IF NOT EXISTS ${this.executor.escapeIdentifier(a)} ON ${this.executor.escapeIdentifier(r)}(${this.executor.escapeIdentifier(t.name)})`)}let s=this.schemaMap.get(e);s&&!s.columns.find(a=>a.name===t.name)&&(s.columns.push(t),this.schemaMap.set(e,s))}async get(e,t){var s;let r,i;return typeof t=="string"?i={id:t}:(r=t,i=r.filters),(s=(await this.query(e,{...r,filters:i,limit:1}))[0])!=null?s:null}async query(e,t){var o,h,f;let r=this.resolveTableName(e),i=(o=t==null?void 0:t.joins)!=null&&o.length?{...t,joins:t.joins.map(d=>({...d,table:this.resolveTableName(d.table)}))}:t,{sql:n,params:s,projectionMap:a}=this.executor.buildSelect(r,i),c=this.executor.all(n,s);if((h=t==null?void 0:t.aggregates)!=null&&h.length)return c;if(t!=null&&t.fields)return((f=t==null?void 0:t.resultShape)!=null?f:"flat")==="nested"&&(a!=null&&a.length)?c.map(g=>{let m={};for(let v of a)m[v.alias]||(m[v.alias]={}),m[v.alias][v.field]=g[v.key];return m}):c;let l=this.schemaMap.get(e);if(!l)return c;let p=Object.fromEntries(l.columns.map(d=>[d.name,d]));return c.map(d=>this.executor.parseRow(d,p))}async create(e,t){var o,h;let r=this.resolveTableName(e),{sql:i,params:n}=this.executor.buildInsert(r,t),s=this.executor.run(i,n),a=(h=t==null?void 0:t.id)!=null?h:(o=s.lastInsertRowid)==null?void 0:o.toString(),c=a?{...t,id:a}:{...t},l=this.schemaMap.get(e);if(!l)return c;let p=Object.fromEntries(l.columns.map(f=>[f.name,f]));return this.executor.parseRow(c,p)}async update(e,t,r){if(!Object.keys(r).length){let c=await this.get(e,t);if(!c)throw new Error(`Record not found: ${e}#${t}`);return c}let i=this.resolveTableName(e),{sql:n,params:s}=this.executor.buildUpdate(i,t,r);this.executor.run(n,s);let a=await this.get(e,t);if(!a)throw new Error(`Record not found after update: ${e}#${t}`);return a}async delete(e,t){let r=this.resolveTableName(e),{sql:i,params:n}=this.executor.buildDelete(r,t);this.executor.run(i,n)}async bulkCreate(e,t){return t.length?this.transaction(async r=>{let i=[];for(let n of t)i.push(await r.create(e,n));return i}):[]}async bulkUpdate(e,t){return t.length?this.transaction(async r=>{let i=[];for(let n of t)Object.keys(n.updates).length&&i.push(await r.update(e,n.id,n.updates));return i}):[]}async transaction(e){let t=this.savepointDepth===0,r=t?"BEGIN IMMEDIATE":`SAVEPOINT sp${this.savepointDepth}`;this.executor.run(r),this.savepointDepth+=1;try{let i=await e(this),n=t?"COMMIT":`RELEASE SAVEPOINT sp${this.savepointDepth-1}`;return this.executor.run(n),i}catch(i){let n=t?"ROLLBACK":`ROLLBACK TO SAVEPOINT sp${this.savepointDepth-1}`;try{this.executor.run(n)}catch(s){}throw i}finally{this.savepointDepth-=1}}};var D=require("node:crypto"),w=class{getEnvVar(e){return process.env[e]}isOnline(){return typeof navigator!="undefined"&&typeof navigator.onLine!="undefined"?navigator.onLine:!0}async hashPin(e){return(0,D.createHash)("sha256").update(e).digest("hex")}async verifyPin(e,t){return await this.hashPin(e)===t}};var R=class{constructor(e){this.options=e}configure(e){}createChannel(e){}localNotification(e){this.options.notify(e)}};var L=T(require("node:fs/promises")),M=T(require("node:path")),x=class{constructor(e){this.options=e}getDocumentPath(e){return M.default.join(this.options.documentsPath,e)}async writeFile(e,t){await L.default.writeFile(e,t,"utf8")}async share(e){var t,r;await((r=(t=this.options).revealFile)==null?void 0:r.call(t,e))}};function z(u,e){if(!e||Object.keys(e).length===0)return u;let t=new URLSearchParams;for(let[i,n]of Object.entries(e))n!=null&&t.set(i,String(n));let r=t.toString();return r?u.includes("?")?`${u}&${r}`:`${u}?${r}`:u}var O=class{constructor(e={}){let t=e.fetch;if(t)this.fetchImpl=t;else{let r=globalThis.fetch;if(typeof r!="function")throw new Error("ElectronHttpAdapter: globalThis.fetch is not available. Pass a fetch implementation via options.fetch, or ensure the app is running on Electron \u2265 20 / Node.js \u2265 18.");this.fetchImpl=r}}async request(e){var n,s;let t=z(e.url,e.query),r=typeof AbortController!="undefined"?new AbortController:null,i=r&&e.timeoutMs?setTimeout(()=>r.abort(),e.timeoutMs):null;try{let a=e.body!==void 0&&e.body!==null,c={...(n=e.headers)!=null?n:{}};a&&!c["content-type"]&&!c["Content-Type"]&&(c["content-type"]="application/json");let l=await this.fetchImpl(t,{method:e.method,headers:c,body:a?JSON.stringify(e.body):void 0,signal:r==null?void 0:r.signal}),p={};l.headers.forEach((f,d)=>{p[d.toLowerCase()]=f});let h=((s=p["content-type"])!=null?s:"").includes("application/json")?await l.json():await l.text();return{status:l.status,headers:p,data:h}}finally{i!==null&&clearTimeout(i)}}};var J=!0;function G(u){let e=u.query;if(!e||Object.keys(e).length===0)return u.url;let r=new URLSearchParams(e).toString();return u.url.includes("?")?`${u.url}&${r}`:`${u.url}?${r}`}var C=class{constructor(e={}){this.ws=null;this.connectionOptions=null;this.listeners=new Map;this.reconnectAttempts=0;this.reconnectTimeout=null;this.intentionalClose=!1;var r,i,n,s,a;this.reconnect=(r=e.reconnect)!=null?r:!0,this.maxReconnectAttempts=Math.max(0,(i=e.maxReconnectAttempts)!=null?i:5),this.reconnectDelayMs=Math.max(0,(n=e.reconnectDelayMs)!=null?n:500),this.maxReconnectDelayMs=Math.max(this.reconnectDelayMs,(s=e.maxReconnectDelayMs)!=null?s:3e4);let t=(a=e.webSocket)!=null?a:globalThis.WebSocket;if(typeof t!="function")throw new Error("ElectronSocketAdapter: globalThis.WebSocket is not available. Pass a WebSocket constructor via options.webSocket. In the Electron main process you can use the `ws` package.");this.WebSocketCtor=t}connect(e){return this.connectionOptions=e,this.intentionalClose=!1,this.reconnectAttempts=0,this.openConnection(e)}async disconnect(){this.intentionalClose=!0,this.clearReconnectTimeout(),this.ws&&(this.ws.close(1e3,"client disconnect"),this.ws=null)}subscribe(e,t){return this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(t),()=>{let r=this.listeners.get(e);r&&(r.delete(t),r.size===0&&this.listeners.delete(e))}}async publish(e,t){var i,n;if(!this.ws||this.ws.readyState!==this.ws.OPEN)throw new Error(`ElectronSocketAdapter: cannot publish "${e}" \u2014 not connected (readyState=${(n=(i=this.ws)==null?void 0:i.readyState)!=null?n:"null"}).`);let r={event:e,payload:t};this.ws.send(JSON.stringify(r))}isConnected(){return this.ws!==null&&this.ws.readyState===this.ws.OPEN}openConnection(e){return new Promise((t,r)=>{let i=G(e),n;try{n=new this.WebSocketCtor(i)}catch(s){r(s instanceof Error?s:new Error(String(s)));return}n.onopen=()=>{this.ws=n,this.reconnectAttempts=0,t()},n.onerror=()=>{this.ws||r(new Error("ElectronSocketAdapter: connection failed"))},n.onclose=()=>{this.ws===n&&(this.ws=null),!this.intentionalClose&&this.reconnect&&this.connectionOptions&&this.reconnectAttempts<this.maxReconnectAttempts&&this.scheduleReconnect()},n.onmessage=s=>{this.handleMessage(s.data)}})}handleMessage(e){if(typeof e!="string")return;let t;try{t=JSON.parse(e)}catch{return}if(typeof t!="object"||t===null||typeof t.event!="string")return;let{event:r,payload:i}=t,n=this.listeners.get(r);if(n)for(let s of n)try{s(i)}catch{}}scheduleReconnect(){this.clearReconnectTimeout();let e=++this.reconnectAttempts,t=Math.min(this.reconnectDelayMs*Math.pow(2,e-1),this.maxReconnectDelayMs);this.reconnectTimeout=setTimeout(()=>{!this.intentionalClose&&this.connectionOptions&&this.openConnection(this.connectionOptions).catch(()=>{})},t)}clearReconnectTimeout(){this.reconnectTimeout!==null&&(clearTimeout(this.reconnectTimeout),this.reconnectTimeout=null)}};var K=!0;var b=T(require("node:fs")),B=T(require("node:path")),$=class{constructor(e){var r;let t=(r=e.fileName)!=null?r:"storage.json";b.default.mkdirSync(e.storageDir,{recursive:!0}),this.filePath=B.default.join(e.storageDir,t);try{this.data=JSON.parse(b.default.readFileSync(this.filePath,"utf-8"))}catch{this.data={}}}async getItem(e){let t=this.data[e];return t!==void 0?t:null}async setItem(e,t){this.data[e]=t,await b.default.promises.writeFile(this.filePath,JSON.stringify(this.data,null,2),"utf-8")}async removeItem(e){delete this.data[e],await b.default.promises.writeFile(this.filePath,JSON.stringify(this.data,null,2),"utf-8")}};var S=require("ofauth-shared-core"),j={platform:"electron",implemented:!0,notes:"API and Runtime are fully implemented for use with Electron Desktop apps."};function q(u,e){var i,n,s,a,c;let t=new Date().toISOString(),r=u&&typeof u=="object"?u:{};return{sessionId:String((i=r.sessionId)!=null?i:""),identityId:String((n=r.identityId)!=null?n:""),assuranceLevel:String((s=r.assuranceLevel)!=null?s:"basic"),issuedAt:String((a=r.issuedAt)!=null?a:t),expiresAt:String((c=r.expiresAt)!=null?c:e)}}var N=class{constructor(e){this.options=e;var i,n,s,a,c;if(this.serverBaseUrl=((i=e.serverBaseUrl)!=null?i:"").trim().replace(/\/+$/,""),this.cacheKey=e.cacheKey.trim(),this.targetTenantId=((n=e.targetTenantId)==null?void 0:n.trim())||"",this.targetBranchId=((s=e.targetBranchId)==null?void 0:s.trim())||"",this.targetDomainId=((a=e.targetDomainId)==null?void 0:a.trim())||"",this.storage=e.storage,e.fetchImpl)this.fetchImpl=e.fetchImpl;else if(typeof globalThis.fetch=="function")this.fetchImpl=globalThis.fetch.bind(globalThis);else throw new Error("ElectronServerAuthoritativeAuthHostSupport requires a fetch implementation");if(this.isOnlineResolver=(c=e.isOnline)!=null?c:(()=>!0),!this.serverBaseUrl){this.serverBridge=null;return}let t={login:async({principal:l,secret:p})=>{var d,g,m,v;let o=await this.fetchImpl(`${this.serverBaseUrl}/v1/auth/login`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({principal:l,secret:p,...this.targetTenantId?{targetTenantId:this.targetTenantId}:{},...this.targetBranchId?{targetBranchId:this.targetBranchId}:{},...this.targetDomainId?{targetDomainId:this.targetDomainId}:{}})}),h=await o.json().catch(()=>({}));if(!o.ok)throw new S.AuthRejectionError(String((d=h.code)!=null?d:"AUTH_UNAUTHORIZED"));let f=new Date(Date.now()+15*6e4).toISOString();return{accessToken:String((g=h.accessToken)!=null?g:""),refreshToken:String((m=h.refreshToken)!=null?m:""),session:q(h.session,f),activeContext:(v=h.activeContext)!=null?v:null,activeRoleRef:h.activeRoleRef==null?"":String(h.activeRoleRef)}},refresh:async({refreshToken:l})=>{var f,d,g,m;let p=await this.fetchImpl(`${this.serverBaseUrl}/v1/auth/refresh`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:l})}),o=await p.json().catch(()=>({}));if(!p.ok)throw new S.AuthRejectionError(String((f=o.code)!=null?f:"AUTH_UNAUTHORIZED"));let h=new Date(Date.now()+15*6e4).toISOString();return{accessToken:String((d=o.accessToken)!=null?d:""),refreshToken:String((g=o.refreshToken)!=null?g:""),session:q(o.session,h),activeContext:(m=o.activeContext)!=null?m:null}},logout:async({accessToken:l,refreshToken:p})=>{let o={"Content-Type":"application/json"};l&&(o.Authorization=`Bearer ${l}`),await this.fetchImpl(`${this.serverBaseUrl}/v1/auth/logout`,{method:"POST",headers:o,body:JSON.stringify({refreshToken:p})}).catch(()=>{})}},r={getCachedIdentity:async l=>{var p,o;return(o=(p=await this.readCacheRecord(l))==null?void 0:p.identity)!=null?o:null},setCachedIdentity:async l=>{let p=await this.readCacheRecord(l.principal);await this.writeCacheRecord(l.principal,{...p,identity:l})},clearCachedIdentity:async l=>{let p=await this.readCacheRecord(l);p&&await this.writeCacheRecord(l,{...p,identity:void 0})},getSessionEnvelope:async l=>{var p,o;return(o=(p=await this.readCacheRecord(l))==null?void 0:p.envelope)!=null?o:null},setSessionEnvelope:async(l,p)=>{let o=await this.readCacheRecord(l);await this.writeCacheRecord(l,{...o,envelope:p})},clearSessionEnvelope:async l=>{let p=await this.readCacheRecord(l);p&&await this.writeCacheRecord(l,{...p,envelope:void 0})}};this.serverBridge=(0,S.createServerAuthoritativeAuthBridge)({authority:t,cache:r,isOnline:()=>this.isOnlineResolver(),verifyOfflineSecret:this.options.verifyOfflineSecret||(async()=>!1)})}get isServerConfigured(){return this.serverBaseUrl.length>0}get capability(){return j}get cacheNamespace(){return this.cacheKey}async loadCacheMap(){if(!this.storage)return{};try{let e=await this.storage.getItem(this.cacheKey);if(!e)return{};let t=JSON.parse(e);return t&&typeof t=="object"?t:{}}catch{return{}}}async saveCacheMap(e){if(this.storage)try{Object.keys(e).length===0?await this.storage.removeItem(this.cacheKey):await this.storage.setItem(this.cacheKey,JSON.stringify(e))}catch{}}async readCacheRecord(e){var r;return(r=(await this.loadCacheMap())[e])!=null?r:null}async writeCacheRecord(e,t){let r=await this.loadCacheMap(),i={};t.identity&&(i.identity=t.identity),t.envelope&&(i.envelope=t.envelope),!i.identity&&!i.envelope?delete r[e]:r[e]=i,await this.saveCacheMap(r)}async clearCachedSessionEnvelope(e){let t=await this.readCacheRecord(e);t&&await this.writeCacheRecord(e,{...t,envelope:void 0})}async clearAllCachedSessionEnvelopes(){let e=await this.loadCacheMap();for(let t of Object.keys(e)){let r=e[t];r&&(e[t]=r.identity?{identity:r.identity}:{},e[t].identity||delete e[t])}await this.saveCacheMap(e)}async login(e,t){var i,n;if(!this.serverBridge)return{status:"rejected",reasonCode:"AUTH_BRIDGE_NOT_CONFIGURED"};let r=await this.serverBridge.login({principal:e,secret:t,verifyMethod:"pin"});return r.status==="authenticated"?{status:"authenticated",mode:(i=r.mode)!=null?i:"online"}:{status:"rejected",reasonCode:(n=r.reasonCode)!=null?n:"AUTH_UNAUTHORIZED"}}async logout(e){this.serverBridge&&await this.serverBridge.logout(e)}async restoreCachedEnvelope(){var t,r,i;let e=await this.loadCacheMap();for(let n of Object.keys(e)){let s=(t=e[n])==null?void 0:t.envelope;if(!s)continue;if(this.isOnlineResolver()&&s.refreshToken&&this.serverBaseUrl)try{let c=await this.fetchImpl(`${this.serverBaseUrl}/v1/auth/refresh`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refreshToken:s.refreshToken})}),l=await c.json().catch(()=>({}));if(c.ok&&l.accessToken){let p={...s,accessToken:String(l.accessToken),refreshToken:l.refreshToken?String(l.refreshToken):s.refreshToken,activeContext:(i=(r=l.activeContext)!=null?r:s.activeContext)!=null?i:null};return await this.writeCacheRecord(n,{...e[n],envelope:p}),{principal:n,envelope:p}}}catch{}let a=Date.parse(s.expiresAt);if(Number.isFinite(a)&&a>Date.now())return{principal:n,envelope:s}}return null}async getStoredAccessToken(e){var r,i;let t=await this.readCacheRecord(e);return(i=(r=t==null?void 0:t.envelope)==null?void 0:r.accessToken)!=null?i:null}async getStoredEnvelope(e){var r;let t=await this.readCacheRecord(e);return(r=t==null?void 0:t.envelope)!=null?r:null}};var X={platform:"electron",ready:!0,notes:"All adapter domains implemented: SQLiteAdapter (db), ElectronHttpAdapter (http), ElectronSocketAdapter (socket), ElectronStorageAdapter (storage), ElectronPlatformAdapter (platform), ElectronExportAdapter + ElectronNotificationAdapter (desktop). Auth bridge surface is scaffolded for parity with ofplatform-web."};0&&(module.exports={ElectronExportAdapter,ElectronHttpAdapter,ElectronNotificationAdapter,ElectronPlatformAdapter,ElectronServerAuthoritativeAuthHostSupport,ElectronSocketAdapter,ElectronStorageAdapter,OFPLATFORM_ELECTRON_AUTH_STATUS,OFPLATFORM_ELECTRON_HTTP_ADAPTERS_READY,OFPLATFORM_ELECTRON_SOCKET_ADAPTERS_READY,OFPLATFORM_ELECTRON_STATUS,SQLiteAdapter,SQLiteExecutor,SQLiteQueryBuilder});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ofplatform-electron",
3
- "version": "0.1.0-alpha.0",
3
+ "version": "0.1.0-alpha.2",
4
4
  "private": false,
5
5
  "description": "Electron platform adapters for of* host apps.",
6
6
  "author": {
@@ -24,19 +24,24 @@
24
24
  "clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
25
25
  "build": "npm run clean && tsc -p tsconfig.build.json && node esbuild.config.js",
26
26
  "typecheck": "tsc -p tsconfig.json --noEmit",
27
- "ci:check": "npm run typecheck && npm run build",
27
+ "test": "npm run build && node --test ./tests/*.test.js",
28
+ "ci:check": "npm run typecheck && npm run test",
28
29
  "prepublishOnly": "npm run ci:check",
29
30
  "prepack": "npm run build",
30
31
  "bundle": "node esbuild.config.js"
31
32
  },
32
33
  "dependencies": {
33
34
  "better-sqlite3": "^11.8.1",
34
- "ofcore": "0.1.0-alpha.0"
35
+ "ofcore": "0.2.0-alpha.0"
36
+ },
37
+ "peerDependencies": {
38
+ "ofauth-shared-core": "0.2.0-alpha.0"
35
39
  },
36
40
  "devDependencies": {
37
41
  "@types/better-sqlite3": "^7.6.12",
38
- "typescript": "^5.9.3",
39
- "esbuild": "^0.25.11"
42
+ "esbuild": "^0.25.11",
43
+ "ofauth-shared-core": "0.2.0-alpha.0",
44
+ "typescript": "^5.9.3"
40
45
  },
41
46
  "publishConfig": {
42
47
  "access": "public"