ofcore 0.1.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +29 -0
  2. package/dist/contracts/ActivitySink.d.ts +13 -0
  3. package/dist/contracts/ChecksumPrimitives.d.ts +12 -0
  4. package/dist/contracts/ContractVersioning.d.ts +16 -0
  5. package/dist/contracts/CrossDomainPrimitives.d.ts +51 -0
  6. package/dist/contracts/EncryptionAdapter.d.ts +14 -0
  7. package/dist/contracts/ExportAdapter.d.ts +5 -0
  8. package/dist/contracts/HttpAdapter.d.ts +17 -0
  9. package/dist/contracts/HttpRetryPolicy.d.ts +16 -0
  10. package/dist/contracts/LoggerAdapter.d.ts +5 -0
  11. package/dist/contracts/NotificationAdapter.d.ts +15 -0
  12. package/dist/contracts/ResponseEnvelope.d.ts +27 -0
  13. package/dist/contracts/SocketAdapter.d.ts +12 -0
  14. package/dist/defaults/ConsoleLoggerAdapter.d.ts +7 -0
  15. package/dist/defaults/DefaultExportAdapter.d.ts +7 -0
  16. package/dist/defaults/DefaultHttpAdapter.d.ts +15 -0
  17. package/dist/defaults/ExponentialBackoffHttpPolicy.d.ts +16 -0
  18. package/dist/defaults/InMemoryDbAdapter.d.ts +65 -0
  19. package/dist/defaults/InMemorySocketAdapter.d.ts +12 -0
  20. package/dist/defaults/MemoryActivitySink.d.ts +8 -0
  21. package/dist/defaults/MemoryStorageAdapter.d.ts +8 -0
  22. package/dist/defaults/NoRetryHttpPolicy.d.ts +6 -0
  23. package/dist/defaults/NoopActivitySink.d.ts +5 -0
  24. package/dist/defaults/NoopChecksumAdapter.d.ts +6 -0
  25. package/dist/defaults/NoopEncryptionAdapter.d.ts +6 -0
  26. package/dist/defaults/NoopNotificationAdapter.d.ts +11 -0
  27. package/dist/index.cjs.js +1 -0
  28. package/dist/index.d.ts +34 -0
  29. package/dist/index.esm.js +1 -0
  30. package/dist/migration/applyPendingMigrations.d.ts +10 -0
  31. package/dist/migration/migrations.d.ts +6 -0
  32. package/dist/migration/types.d.ts +5 -0
  33. package/dist/ports/DbAdapter.d.ts +306 -0
  34. package/dist/ports/PlatformAdapter.d.ts +21 -0
  35. package/dist/ports/StorageAdapter.d.ts +5 -0
  36. package/dist/runtime/CoreRuntime.d.ts +91 -0
  37. package/dist/runtime/createStore.d.ts +11 -0
  38. package/dist/testing/DbAdapterContractHarness.d.ts +5 -0
  39. package/package.json +46 -0
package/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # ofcore
2
+
3
+ `ofcore` adalah runtime core ringan untuk aplikasi TypeScript offline-first.
4
+ Paket ini fokus pada primitive generik lintas domain, bukan logika bisnis domain spesifik.
5
+
6
+ ## What You Get
7
+ - kontrak runtime inti (adapter + envelope + versioning),
8
+ - primitive store framework-agnostic,
9
+ - harness kontrak adapter untuk validasi perilaku.
10
+
11
+ ## Install
12
+ ```bash
13
+ npm install ofcore
14
+ ```
15
+
16
+ ## Build and Validate
17
+ - `npm run build`
18
+ - `npm run verify:contract`
19
+ - `npm run verify:logic`
20
+ - `npm run ci:check`
21
+
22
+ ## Publish Checklist
23
+ 1. `npm run ci:check`
24
+ 2. `npm pack --dry-run`
25
+ 3. `npm publish --access public`
26
+
27
+ ## Documentation
28
+ - Developer notes (internal detail): `docs/10-DEVELOPER-NOTES.md`
29
+ - Documentation hub: `docs/00-DOCUMENTATION-HUB.md`
@@ -0,0 +1,13 @@
1
+ import type { DomainAuditActor, ScopeRef } from './CrossDomainPrimitives';
2
+ export interface ActivityRecord {
3
+ activityId: string;
4
+ activityType: string;
5
+ occurredAt: string;
6
+ scopeRef?: ScopeRef;
7
+ actor?: DomainAuditActor;
8
+ payload?: Record<string, unknown>;
9
+ correlationId?: string;
10
+ }
11
+ export interface ActivitySink {
12
+ publish(record: ActivityRecord): Promise<void> | void;
13
+ }
@@ -0,0 +1,12 @@
1
+ export type ChecksumAlgorithm = 'sha256' | 'hmac-sha256';
2
+ export interface ChecksumInput {
3
+ algorithm: ChecksumAlgorithm;
4
+ payload: string;
5
+ secret?: string;
6
+ }
7
+ export interface ChecksumAdapter {
8
+ compute(input: ChecksumInput): Promise<string> | string;
9
+ verify?(input: ChecksumInput, expectedChecksum: string): Promise<boolean> | boolean;
10
+ }
11
+ export declare function normalizeChecksum(value: string): string;
12
+ export declare function areChecksumsEqual(left: string, right: string): boolean;
@@ -0,0 +1,16 @@
1
+ export interface ContractVersionInfo {
2
+ contractName: string;
3
+ version: string;
4
+ compatibleWith: string[];
5
+ deprecated?: boolean;
6
+ sunsetAt?: string;
7
+ }
8
+ export interface ContractChangeNote {
9
+ contractName: string;
10
+ fromVersion: string;
11
+ toVersion: string;
12
+ summary: string;
13
+ breaking: boolean;
14
+ migrationRequired: boolean;
15
+ }
16
+ export declare function isContractVersionCompatible(currentVersion: string, compatibleWith: string[], targetVersion: string): boolean;
@@ -0,0 +1,51 @@
1
+ export interface ScopeRef {
2
+ tenantId?: string;
3
+ branchId?: string;
4
+ domain?: string;
5
+ }
6
+ export interface DomainAuditActor {
7
+ userId?: string;
8
+ role?: string;
9
+ source?: string;
10
+ }
11
+ export interface DomainAuditEntityRef {
12
+ domain: string;
13
+ entity: string;
14
+ id: string;
15
+ }
16
+ export interface DomainAuditEvent {
17
+ eventId: string;
18
+ eventType: string;
19
+ actor: DomainAuditActor;
20
+ entityRef: DomainAuditEntityRef;
21
+ changes: Record<string, unknown>;
22
+ correlationId?: string;
23
+ timestamp: string;
24
+ }
25
+ export type WorklistUrgency = 'low' | 'medium' | 'high' | 'critical';
26
+ export interface WorklistItem {
27
+ id: string;
28
+ scopeRef: ScopeRef;
29
+ urgency: WorklistUrgency;
30
+ reasonCode: string;
31
+ reasonText: string;
32
+ suggestedActions: string[];
33
+ metrics?: Record<string, number>;
34
+ timestamp: string;
35
+ }
36
+ export interface PolicyVersionedConfig<TPayload = Record<string, unknown>> {
37
+ policyId: string;
38
+ scopeRef: ScopeRef;
39
+ version: number;
40
+ effectiveDate: string;
41
+ approvalRef?: string | null;
42
+ isActive: boolean;
43
+ payload: TPayload;
44
+ }
45
+ export interface ComplianceEvaluationResult {
46
+ evaluationId: string;
47
+ scopeRef: ScopeRef;
48
+ summary: Record<string, number>;
49
+ worklist: WorklistItem[];
50
+ evaluatedAt: string;
51
+ }
@@ -0,0 +1,14 @@
1
+ export interface EncryptionContext {
2
+ keyId?: string;
3
+ aad?: string;
4
+ metadata?: Record<string, unknown>;
5
+ }
6
+ export interface EncryptionAdapter {
7
+ encrypt(plainText: string, context?: EncryptionContext): Promise<string> | string;
8
+ decrypt(cipherText: string, context?: EncryptionContext): Promise<string | null> | string | null;
9
+ rotateKey?(context: {
10
+ fromKeyId?: string;
11
+ toKeyId: string;
12
+ dryRun?: boolean;
13
+ }): Promise<void> | void;
14
+ }
@@ -0,0 +1,5 @@
1
+ export interface ExportAdapter {
2
+ getDocumentPath(fileName: string): string;
3
+ writeFile(path: string, content: string): Promise<void>;
4
+ share(path: string): Promise<void>;
5
+ }
@@ -0,0 +1,17 @@
1
+ export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
2
+ export interface HttpRequest {
3
+ method: HttpMethod;
4
+ url: string;
5
+ headers?: Record<string, string>;
6
+ query?: Record<string, string | number | boolean | null | undefined>;
7
+ body?: unknown;
8
+ timeoutMs?: number;
9
+ }
10
+ export interface HttpResponse<T = unknown> {
11
+ status: number;
12
+ headers: Record<string, string>;
13
+ data: T;
14
+ }
15
+ export interface HttpAdapter {
16
+ request<T = unknown>(request: HttpRequest): Promise<HttpResponse<T>>;
17
+ }
@@ -0,0 +1,16 @@
1
+ import type { HttpAdapter, HttpRequest, HttpResponse } from './HttpAdapter';
2
+ export interface HttpRetryContext<T = unknown> {
3
+ request: HttpRequest;
4
+ attempt: number;
5
+ response?: HttpResponse<T>;
6
+ error?: unknown;
7
+ }
8
+ export interface HttpRetryPolicy {
9
+ shouldRetry<T = unknown>(context: HttpRetryContext<T>): boolean;
10
+ nextDelayMs<T = unknown>(context: HttpRetryContext<T>): number;
11
+ }
12
+ export interface HttpRetryExecutorOptions {
13
+ policy?: HttpRetryPolicy;
14
+ sleep?: (ms: number) => Promise<void>;
15
+ }
16
+ export declare function executeHttpRequestWithRetry<T = unknown>(adapter: HttpAdapter, request: HttpRequest, options?: HttpRetryExecutorOptions): Promise<HttpResponse<T>>;
@@ -0,0 +1,5 @@
1
+ export interface LoggerAdapter {
2
+ logInfo(message: string, context?: Record<string, any>): void;
3
+ logWarn(message: string, context?: Record<string, any>): void;
4
+ logError(error: any, context?: Record<string, any>): void;
5
+ }
@@ -0,0 +1,15 @@
1
+ export interface NotificationOptions {
2
+ channelId?: string;
3
+ title: string;
4
+ message: string;
5
+ [key: string]: unknown;
6
+ }
7
+ export interface NotificationAdapter {
8
+ configure(options: unknown): void;
9
+ createChannel(options: {
10
+ channelId: string;
11
+ channelName: string;
12
+ importance?: number;
13
+ }): void;
14
+ localNotification(options: NotificationOptions): void;
15
+ }
@@ -0,0 +1,27 @@
1
+ export interface ErrorDetail {
2
+ field?: string;
3
+ issue: string;
4
+ code?: string;
5
+ }
6
+ export interface ErrorEnvelope {
7
+ code: string;
8
+ message: string;
9
+ details?: ErrorDetail[];
10
+ correlationId?: string;
11
+ timestamp?: string;
12
+ retryable?: boolean;
13
+ }
14
+ export interface MetaEnvelope {
15
+ request_id?: string | null;
16
+ timestamp?: string;
17
+ }
18
+ export interface SuccessEnvelope<T> {
19
+ data: T;
20
+ meta?: MetaEnvelope;
21
+ }
22
+ export interface FailureEnvelope {
23
+ error: ErrorEnvelope;
24
+ meta?: MetaEnvelope;
25
+ }
26
+ export type ResponseEnvelope<T> = SuccessEnvelope<T> | FailureEnvelope;
27
+ export declare function isFailureEnvelope<T>(value: ResponseEnvelope<T>): value is FailureEnvelope;
@@ -0,0 +1,12 @@
1
+ export type SocketEventHandler = (payload: unknown) => void;
2
+ export interface SocketConnectOptions {
3
+ url: string;
4
+ headers?: Record<string, string>;
5
+ query?: Record<string, string>;
6
+ }
7
+ export interface SocketAdapter {
8
+ connect(options: SocketConnectOptions): Promise<void>;
9
+ disconnect(): Promise<void>;
10
+ subscribe(eventName: string, handler: SocketEventHandler): () => void;
11
+ publish(eventName: string, payload: unknown): Promise<void>;
12
+ }
@@ -0,0 +1,7 @@
1
+ import type { LoggerAdapter } from '../contracts/LoggerAdapter';
2
+ export declare class ConsoleLoggerAdapter implements LoggerAdapter {
3
+ constructor(_context?: unknown);
4
+ logInfo(message: string, context?: Record<string, any>): void;
5
+ logWarn(message: string, context?: Record<string, any>): void;
6
+ logError(error: any, context?: Record<string, any>): void;
7
+ }
@@ -0,0 +1,7 @@
1
+ import type { ExportAdapter } from '../contracts/ExportAdapter';
2
+ export declare class DefaultExportAdapter implements ExportAdapter {
3
+ constructor(_context?: unknown);
4
+ getDocumentPath(fileName: string): string;
5
+ writeFile(_path: string, _content: string): Promise<void>;
6
+ share(_path: string): Promise<void>;
7
+ }
@@ -0,0 +1,15 @@
1
+ import type { HttpAdapter, HttpRequest, HttpResponse } from '../contracts/HttpAdapter';
2
+ type FetchLike = (input: string, init?: Record<string, unknown>) => Promise<{
3
+ status: number;
4
+ headers?: {
5
+ forEach?: (callback: (value: string, key: string) => void) => void;
6
+ };
7
+ json?: () => Promise<unknown>;
8
+ text?: () => Promise<string>;
9
+ }>;
10
+ export declare class DefaultHttpAdapter implements HttpAdapter {
11
+ private readonly fetchImpl?;
12
+ constructor(fetchImpl?: FetchLike | undefined);
13
+ request<T = unknown>(request: HttpRequest): Promise<HttpResponse<T>>;
14
+ }
15
+ export {};
@@ -0,0 +1,16 @@
1
+ import type { HttpRetryContext, HttpRetryPolicy } from '../contracts/HttpRetryPolicy';
2
+ export interface ExponentialBackoffHttpPolicyOptions {
3
+ maxAttempts?: number;
4
+ baseDelayMs?: number;
5
+ maxDelayMs?: number;
6
+ retryableStatusCodes?: number[];
7
+ }
8
+ export declare class ExponentialBackoffHttpPolicy implements HttpRetryPolicy {
9
+ private readonly maxAttempts;
10
+ private readonly baseDelayMs;
11
+ private readonly maxDelayMs;
12
+ private readonly retryableStatusCodes;
13
+ constructor(options?: ExponentialBackoffHttpPolicyOptions);
14
+ shouldRetry<T = unknown>(context: HttpRetryContext<T>): boolean;
15
+ nextDelayMs<T = unknown>(context: HttpRetryContext<T>): number;
16
+ }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * In-memory implementation of DbAdapter for testing and development.
3
+ * Fully compliant with the latest DbAdapter interface.
4
+ */
5
+ import { DbAdapter, TableSchema, ColumnDefinition, DbSchema, QueryOptions, LiteralValue } from '../ports/DbAdapter';
6
+ export declare class InMemoryDbAdapter implements DbAdapter {
7
+ schema: DbSchema;
8
+ private data;
9
+ /**
10
+ * Initialize with a full schema (e.g., from `schemas`).
11
+ */
12
+ setUpWithSchema(schema: DbSchema): Promise<void>;
13
+ /**
14
+ * Normalize value for comparison (handles Date, boolean, etc.)
15
+ */
16
+ private normalizeForComparison;
17
+ /**
18
+ * Evaluate a ValueExpression against a record (and joined records).
19
+ * @param record Record from main table
20
+ * @param joinedRecords Map of alias → record (for joins)
21
+ * @param expr ValueExpression to evaluate
22
+ * @param mainAlias Main table alias (default: 't0')
23
+ */
24
+ private evalValueExpr;
25
+ private buildFieldCondition;
26
+ /**
27
+ * Recursively evaluate FilterExpression.
28
+ */
29
+ private matchFilterExpression;
30
+ /**
31
+ * Simulate JOINs by pre-matching records (for testing only).
32
+ * Limited to one-to-many/many-to-one; no many-to-many or complex conditions.
33
+ */
34
+ private simulateJoins;
35
+ /**
36
+ * Extract field from record (handles alias.field and string field).
37
+ */
38
+ private extractField;
39
+ private extractAggregateField;
40
+ getSchemaVersion(): Promise<number>;
41
+ setSchemaVersion(version: number): Promise<void>;
42
+ addTable(table: TableSchema): Promise<void>;
43
+ addColumn(_table: string, _column: ColumnDefinition): Promise<void>;
44
+ get<T>(table: string, idOrOptions: string | QueryOptions): Promise<T | null>;
45
+ query<T>(table: string, options?: QueryOptions): Promise<T[]>;
46
+ create<T>(table: string, data: Partial<Record<string, LiteralValue>> & {
47
+ id: string;
48
+ }): Promise<T>;
49
+ update<T>(table: string, id: string, updates: Partial<Record<string, LiteralValue>>): Promise<T>;
50
+ delete(table: string, id: string): Promise<void>;
51
+ bulkCreate<T>(table: string, rows: Array<Partial<Record<string, LiteralValue>> & {
52
+ id: string;
53
+ }>): Promise<T[]>;
54
+ bulkUpdate<T>(table: string, rows: Array<{
55
+ id: string;
56
+ updates: Partial<Record<string, LiteralValue>>;
57
+ }>): Promise<T[]>;
58
+ private transactionDepth;
59
+ private snapshots;
60
+ /**
61
+ * Simpan snapshot seluruh database (shallow copy cukup karena data immutable dalam transaksi)
62
+ */
63
+ private takeSnapshot;
64
+ transaction<T>(callback: (tx: this) => Promise<T>): Promise<T>;
65
+ }
@@ -0,0 +1,12 @@
1
+ import type { SocketAdapter, SocketConnectOptions, SocketEventHandler } from '../contracts/SocketAdapter';
2
+ export declare class InMemorySocketAdapter implements SocketAdapter {
3
+ private connected;
4
+ private listeners;
5
+ private connectionOptions?;
6
+ connect(options: SocketConnectOptions): Promise<void>;
7
+ disconnect(): Promise<void>;
8
+ subscribe(eventName: string, handler: SocketEventHandler): () => void;
9
+ publish(eventName: string, payload: unknown): Promise<void>;
10
+ isConnected(): boolean;
11
+ getConnectionOptions(): SocketConnectOptions | undefined;
12
+ }
@@ -0,0 +1,8 @@
1
+ import type { ActivityRecord, ActivitySink } from '../contracts/ActivitySink';
2
+ export declare class MemoryActivitySink implements ActivitySink {
3
+ private readonly records;
4
+ constructor(_context?: unknown);
5
+ publish(record: ActivityRecord): void;
6
+ getRecords(): ActivityRecord[];
7
+ clear(): void;
8
+ }
@@ -0,0 +1,8 @@
1
+ import type { StorageAdapter } from '../ports/StorageAdapter';
2
+ export declare class MemoryStorageAdapter implements StorageAdapter {
3
+ private readonly map;
4
+ constructor(_context?: unknown);
5
+ getItem(key: string): string | null;
6
+ setItem(key: string, value: string): void;
7
+ removeItem(key: string): void;
8
+ }
@@ -0,0 +1,6 @@
1
+ import type { HttpRetryContext, HttpRetryPolicy } from '../contracts/HttpRetryPolicy';
2
+ export declare class NoRetryHttpPolicy implements HttpRetryPolicy {
3
+ constructor(_context?: unknown);
4
+ shouldRetry<T = unknown>(_context: HttpRetryContext<T>): boolean;
5
+ nextDelayMs<T = unknown>(_context: HttpRetryContext<T>): number;
6
+ }
@@ -0,0 +1,5 @@
1
+ import type { ActivityRecord, ActivitySink } from '../contracts/ActivitySink';
2
+ export declare class NoopActivitySink implements ActivitySink {
3
+ constructor(_context?: unknown);
4
+ publish(_record: ActivityRecord): void;
5
+ }
@@ -0,0 +1,6 @@
1
+ import type { ChecksumAdapter, ChecksumInput } from '../contracts/ChecksumPrimitives';
2
+ export declare class NoopChecksumAdapter implements ChecksumAdapter {
3
+ constructor(_context?: unknown);
4
+ compute(_input: ChecksumInput): string;
5
+ verify(_input: ChecksumInput, expectedChecksum: string): boolean;
6
+ }
@@ -0,0 +1,6 @@
1
+ import type { EncryptionAdapter, EncryptionContext } from '../contracts/EncryptionAdapter';
2
+ export declare class NoopEncryptionAdapter implements EncryptionAdapter {
3
+ constructor(_context?: unknown);
4
+ encrypt(plainText: string, _context?: EncryptionContext): string;
5
+ decrypt(cipherText: string, _context?: EncryptionContext): string;
6
+ }
@@ -0,0 +1,11 @@
1
+ import type { NotificationAdapter, NotificationOptions } from '../contracts/NotificationAdapter';
2
+ export declare class NoopNotificationAdapter implements NotificationAdapter {
3
+ constructor(_context?: unknown);
4
+ configure(_options: unknown): void;
5
+ createChannel(_options: {
6
+ channelId: string;
7
+ channelName: string;
8
+ importance?: number;
9
+ }): void;
10
+ localNotification(_options: NotificationOptions): void;
11
+ }
@@ -0,0 +1 @@
1
+ "use strict";var C=Object.defineProperty;var B=Object.getOwnPropertyDescriptor;var W=Object.getOwnPropertyNames;var J=Object.prototype.hasOwnProperty;var Q=(o,t)=>{for(var e in t)C(o,e,{get:t[e],enumerable:!0})},G=(o,t,e,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of W(t))!J.call(o,n)&&n!==e&&C(o,n,{get:()=>t[n],enumerable:!(r=B(t,n))||r.enumerable});return o};var K=o=>G(C({},"__esModule",{value:!0}),o);var pt={};Q(pt,{ConsoleLoggerAdapter:()=>H,CoreAdapterRegistry:()=>k,CoreRuntime:()=>T,CoreRuntimeBuilder:()=>R,DefaultExportAdapter:()=>_,DefaultHttpAdapter:()=>I,DefaultPlatformAdapter:()=>P,ExponentialBackoffHttpPolicy:()=>F,InMemoryDbAdapter:()=>D,InMemorySocketAdapter:()=>M,MemoryActivitySink:()=>L,MemoryStorageAdapter:()=>$,NoRetryHttpPolicy:()=>w,NoopActivitySink:()=>x,NoopChecksumAdapter:()=>S,NoopEncryptionAdapter:()=>V,NoopNotificationAdapter:()=>O,applyPendingMigrations:()=>it,areChecksumsEqual:()=>Z,asReadonlyStore:()=>ct,createStore:()=>at,executeHttpRequestWithRetry:()=>Y,isContractVersionCompatible:()=>et,isFailureEnvelope:()=>tt,migrations:()=>U,normalizeChecksum:()=>E,runDbAdapterContractHarness:()=>st});module.exports=K(pt);var P=class{constructor(t){}getEnvVar(t){let e=globalThis.process;if(typeof e!="undefined"&&e.env)return e.env[t]}isOnline(){return typeof globalThis.navigator!="undefined"&&typeof globalThis.navigator.onLine!="undefined"?!!globalThis.navigator.onLine:!0}async hashPin(t){return`sha256:${t}`}async verifyPin(t,e){return await this.hashPin(t)===e}};async function X(o){o<=0||await new Promise(t=>setTimeout(t,o))}async function Y(o,t,e={}){var s;let r=e.policy,n=(s=e.sleep)!=null?s:X,i=1;for(;;)try{let a=await o.request(t);if(!r)return a;let p={request:t,attempt:i,response:a};if(!r.shouldRetry(p))return a;let c=Math.max(0,r.nextDelayMs(p));await n(c),i+=1}catch(a){if(!r)throw a;let p={request:t,attempt:i,error:a};if(!r.shouldRetry(p))throw a;let c=Math.max(0,r.nextDelayMs(p));await n(c),i+=1}}function E(o){return String(o||"").trim().toLowerCase()}function Z(o,t){return E(o)===E(t)}function tt(o){return typeof(o==null?void 0:o.error)=="object"&&o!==null}function et(o,t,e){return e===o?!0:t.includes(e)}var D=class{constructor(){this.schema={version:0,tables:[]};this.data={};this.transactionDepth=0;this.snapshots=[]}async setUpWithSchema(t){this.schema={...t},this.data={},t.tables.forEach(e=>{this.data[e.name]=[]})}normalizeForComparison(t){if(t instanceof Date)return t.getTime();if(typeof t=="boolean")return t?1:0;if(typeof t=="string"||typeof t=="number"||t==null)return t;if(Array.isArray(t))return t.map(e=>this.normalizeForComparison(e));if(typeof t=="object"&&t!==null)try{return JSON.stringify(t)}catch{return String(t)}return t}evalValueExpr(t,e,r,n="t0"){if(r.type==="column"){let i=r.tableAlias||n,s=i===n?t:e[i]||{};return this.normalizeForComparison(s[r.field])}if(r.type==="literal")return this.normalizeForComparison(r.value);throw new Error(`Unsupported ValueExpression type: ${r.type}`)}buildFieldCondition(t,e){if("field"in t){let n={type:"column",tableAlias:t.alias||e,field:t.field},i={type:"literal",value:t.value};return{left:n,operator:t.operator||"=",right:i,...t.operator==="BETWEEN"?{}:{likeMode:t.likeMode}}}if("left"in t)return t;throw new Error("Invalid FieldCondition")}matchFilterExpression(t,e,r,n="t0"){if(r&&typeof r=="object"&&!Array.isArray(r)&&"and"in r)return r.and.every(i=>this.matchFilterExpression(t,e,i,n));if(r&&typeof r=="object"&&!Array.isArray(r)&&"or"in r)return r.or.some(i=>this.matchFilterExpression(t,e,i,n));if(r!==null&&typeof r=="object"&&!Array.isArray(r)){if("left"in r||"field"in r){let i=this.buildFieldCondition(r,n),s=this.evalValueExpr(t,e,i.left,n),a=this.evalValueExpr(t,e,i.right,n);if(i.operator==="IS NULL")return s==null;if(i.operator==="IS NOT NULL")return s!=null;if(i.operator==="IN"||i.operator==="NOT IN"){if(!Array.isArray(a))throw new Error("IN/NOT IN requires array on right side");let p=a.includes(s);return i.operator==="IN"?p:!p}switch(i.operator){case"=":return s===a;case"!=":return s!==a;case"<":return s<a;case"<=":return s<=a;case">":return s>a;case">=":return s>=a;case"LIKE":{if(typeof s!="string"||typeof a!="string")return!1;let p=a.replace(/%/g,".*").replace(/_/g,"."),c=i.likeMode;return new RegExp(`^${p}$`,c==="case-insensitive"?"i":"").test(s)}default:throw new Error(`Unsupported operator: ${i.operator}`)}}return Object.entries(r).every(([i,s])=>{let a=this.normalizeForComparison(t[i]),p=this.normalizeForComparison(s);return a===p})}throw new Error(`Invalid filter expression: ${JSON.stringify(r)}`)}simulateJoins(t,e,r){if(r.length===0)return e.map(i=>({record:i,joined:{}}));let n=[];for(let i of e){let s={},a=!0;for(let p of r){let c=p.table,d=this.data[c]||[],m=null;if(p.conditions.length===1){let l=p.conditions[0];if(l.operator==="="&&l.left.type==="column"&&l.right.type==="column"){let g=l.left.tableAlias||"t0",f=l.right.tableAlias;if(g==="t0"&&f===p.alias){let y=l.left.field,u=l.right.field,h=i[y];m=d.find(A=>this.normalizeForComparison(A[u])===this.normalizeForComparison(h))}}}if(!m&&(p.type==="inner"||p.type===void 0)){a=!1;break}s[p.alias]=m||null}a&&n.push({record:i,joined:s})}return n}extractField(t,e,r,n="t0"){if(typeof r=="string")return this.normalizeForComparison(t[r]);let i=r.tableAlias||n,s=i===n?t:e[i]||{};return this.normalizeForComparison(s[r.field])}extractAggregateField(t,e,r,n="t0"){if(typeof r=="string")return this.normalizeForComparison(t[r]);let i=r.tableAlias||n,s=i===n?t:e[i]||{};return this.normalizeForComparison(s[r.field])}async getSchemaVersion(){return this.schema.version}async setSchemaVersion(t){this.schema.version=t}async addTable(t){this.data[t.name]||(this.data[t.name]=[]),this.schema.tables.find(e=>e.name===t.name)||this.schema.tables.push(t)}async addColumn(t,e){}async get(t,e){var n;if(typeof e=="string"){let s=(this.data[t]||[]).find(a=>a.id===e);return s||null}return(n=(await this.query(t,{...e,limit:1}))[0])!=null?n:null}async query(t,e={}){var c;let n=[...this.data[t]||[]],i=this.simulateJoins(t,n,e.joins||[]);if(e.filters&&(i=i.filter(({record:d,joined:m})=>this.matchFilterExpression(d,m,e.filters,"t0"))),e.aggregates&&e.aggregates.length>0){let d={};for(let m of e.aggregates){let l=i;m.filter&&(l=l.filter(({record:u,joined:h})=>this.matchFilterExpression(u,h,m.filter,"t0")));let g=l.map(({record:u,joined:h})=>this.extractAggregateField(u,h,m.field,"t0")),f=g.map(u=>typeof u=="number"?u:Number(u)).filter(u=>Number.isFinite(u)),y=null;switch(m.function){case"COUNT":y=g.filter(u=>u!=null).length;break;case"SUM":y=f.reduce((u,h)=>u+h,0);break;case"AVG":y=f.length?f.reduce((u,h)=>u+h,0)/f.length:0;break;case"MIN":y=f.length?Math.min(...f):null;break;case"MAX":y=f.length?Math.max(...f):null;break;default:throw new Error(`Unsupported aggregate function: ${m.function}`)}d[m.as]=y}return[d]}let s=i.map(({record:d,joined:m})=>{if(e.fields){let l={};for(let[g,f]of Object.entries(e.fields)){let y=g==="t0"?d:m[g]||{};for(let u of f)l[`${g}_${u}`]=y[u]}return l}return d});e.sort&&e.sort.length>0&&s.sort((d,m)=>{for(let l of e.sort){let g=this.extractField(d,{},l.field,"t0"),f=this.extractField(m,{},l.field,"t0");if(g<f)return l.direction==="asc"?-1:1;if(g>f)return l.direction==="asc"?1:-1}return 0});let a=(c=e.offset)!=null?c:0,p=e.limit!=null?a+e.limit:void 0;return s.slice(a,p)}async create(t,e){this.data[t]||(this.data[t]=[]);let r={...e};return this.data[t].push(r),r}async update(t,e,r){let n=this.data[t]||[],i=n.findIndex(a=>a.id===e);if(i===-1)throw new Error(`Record not found: ${t} with id ${e}`);let s={...n[i],...r};return n[i]=s,s}async delete(t,e){let r=this.data[t]||[],n=r.findIndex(i=>i.id===e);n>=0&&r.splice(n,1)}async bulkCreate(t,e){this.data[t]||(this.data[t]=[]);let r=[...this.data[t]];try{let n=[];for(let i of e){let s={...i};this.data[t].push(s),n.push(s)}return n}catch(n){throw this.data[t]=r,n}}async bulkUpdate(t,e){let r=[...this.data[t]||[]];try{let n=[];for(let{id:i,updates:s}of e){let a=await this.update(t,i,s);n.push(a)}return n}catch(n){throw this.data[t]=r,n}}takeSnapshot(){let t={};for(let e in this.data)t[e]=[...this.data[e]];return t}async transaction(t){let e=this.transactionDepth===0;e&&this.snapshots.push(this.takeSnapshot()),this.transactionDepth++;try{let r=await t(this);return e&&this.snapshots.pop(),this.transactionDepth--,r}catch(r){if(e){let n=this.snapshots.pop();n&&(this.data=n)}throw this.transactionDepth--,r}}};var H=class{constructor(t){}logInfo(t,e){console.log(t,e)}logWarn(t,e){console.warn(t,e)}logError(t,e){console.error(t,e)}};function rt(o,t){if(!t||Object.keys(t).length===0)return o;let e=new URLSearchParams;for(let[n,i]of Object.entries(t))i!=null&&e.set(n,String(i));let r=e.toString();return r?o.includes("?")?`${o}&${r}`:`${o}?${r}`:o}var I=class{constructor(t){this.fetchImpl=t}async request(t){var s,a,p,c,d,m,l,g,f;let e=(s=this.fetchImpl)!=null?s:globalThis.fetch;if(typeof e!="function")throw new Error("DefaultHttpAdapter requires fetch implementation");let r=rt(t.url,t.query),n=typeof AbortController!="undefined"?new AbortController:null,i=n&&t.timeoutMs?setTimeout(()=>n.abort(),t.timeoutMs):null;try{let y=t.body!==void 0&&t.body!==null,u={...(a=t.headers)!=null?a:{}};y&&!u["content-type"]&&(u["content-type"]="application/json");let h=await e(r,{method:t.method,headers:u,body:y?JSON.stringify(t.body):void 0,signal:n==null?void 0:n.signal}),A={};(c=(p=h.headers)==null?void 0:p.forEach)==null||c.call(p,(z,q)=>{A[q.toLowerCase()]=z});let j=((d=A["content-type"])!=null?d:"").includes("application/json")?await((l=(m=h.json)==null?void 0:m.call(h))!=null?l:Promise.resolve(void 0)):await((f=(g=h.text)==null?void 0:g.call(h))!=null?f:Promise.resolve(""));return{status:h.status,headers:A,data:j}}finally{i&&clearTimeout(i)}}};var w=class{constructor(t){}shouldRetry(t){return!1}nextDelayMs(t){return 0}};var nt=[408,425,429,500,502,503,504],F=class{constructor(t={}){var e,r,n,i;this.maxAttempts=Math.max(1,(e=t.maxAttempts)!=null?e:3),this.baseDelayMs=Math.max(0,(r=t.baseDelayMs)!=null?r:250),this.maxDelayMs=Math.max(this.baseDelayMs,(n=t.maxDelayMs)!=null?n:3e3),this.retryableStatusCodes=new Set((i=t.retryableStatusCodes)!=null?i:nt)}shouldRetry(t){var r,n;if(t.attempt>=this.maxAttempts)return!1;if(t.error)return!0;let e=(n=(r=t.response)==null?void 0:r.status)!=null?n:0;return this.retryableStatusCodes.has(e)}nextDelayMs(t){let e=Math.max(0,t.attempt-1),r=this.baseDelayMs*Math.pow(2,e);return Math.min(this.maxDelayMs,r)}};var M=class{constructor(){this.connected=!1;this.listeners=new Map}async connect(t){this.connectionOptions=t,this.connected=!0}async disconnect(){this.connected=!1}subscribe(t,e){this.listeners.has(t)||this.listeners.set(t,new Set);let r=this.listeners.get(t);return r.add(e),()=>{r.delete(e),r.size===0&&this.listeners.delete(t)}}async publish(t,e){if(!this.connected)throw new Error(`InMemorySocketAdapter is not connected (event=${t})`);let r=this.listeners.get(t);if(!(!r||r.size===0))for(let n of r)n(e)}isConnected(){return this.connected}getConnectionOptions(){return this.connectionOptions}};var O=class{constructor(t){}configure(t){}createChannel(t){}localNotification(t){}};var V=class{constructor(t){}encrypt(t,e){return String(t)}decrypt(t,e){return String(t)}};var S=class{constructor(t){}compute(t){return""}verify(t,e){return String(e||"").trim()===""}};var x=class{constructor(t){}publish(t){}};var L=class{constructor(t){this.records=[]}publish(t){this.records.push(t)}getRecords(){return[...this.records]}clear(){this.records.length=0}};var _=class{constructor(t){}getDocumentPath(t){return t}async writeFile(t,e){throw new Error("exportAdapter.writeFile is not implemented for this platform")}async share(t){throw new Error("exportAdapter.share is not implemented for this platform")}};var $=class{constructor(t){this.map=new Map}getItem(t){var e;return(e=this.map.get(t))!=null?e:null}setItem(t,e){this.map.set(t,e)}removeItem(t){this.map.delete(t)}};var U=[];async function it(o,t){if(typeof o.getSchemaVersion!="function"||typeof o.setSchemaVersion!="function"){let e="Adapter does not support schema versioning (getSchemaVersion/setSchemaVersion required)";throw t.logError(new Error(e),{message:e}),new Error(e)}t.logInfo("Starting database migration process");try{let e=[...U].sort((i,s)=>i.toVersion-s.toVersion),r=e.length>0?e[e.length-1].toVersion:0,n=await o.getSchemaVersion();if((n==null||n<0)&&(n=0),n>=r){t.logInfo(`Database is already up-to-date at v${n}`);return}t.logInfo(`Migrating database from v${n} to v${r}`);for(let i of e)i.toVersion>n&&(t.logInfo(`Applying migration: v${i.toVersion}`),await i.up(o),await o.setSchemaVersion(i.toVersion),n=i.toVersion);t.logInfo("Database migration completed successfully")}catch(e){throw t.logError(e,{message:"Database migration failed"}),e}}function v(o,t){if(!o)throw new Error(t)}function b(o,t){return`${o}-${t}`}function ot(o){let t=[{name:"id",type:"string"},{name:"name",type:"string"},{name:"age",type:"number"},{name:"tenantId",type:"string"},{name:"isActive",type:"boolean"}];return{name:`${o}_users`,columns:t,skipMetadataColumns:!0}}async function st(o,t={}){var u;let e=((u=t.prefix)!=null?u:`dbadapter_${Date.now()}`).replace(/[^a-zA-Z0-9_]/g,"_"),r=`${e}_users`;await o.setSchemaVersion(7);let n=await o.getSchemaVersion();v(n===7,`Expected schema version 7, got ${n}`),await o.addTable(ot(e)),await o.addColumn(r,{name:"email",type:"string",isOptional:!0}),await o.create(r,{id:b(e,"u1"),name:"Alice",age:31,tenantId:"tenant-1",isActive:!0,email:"alice@example.com"}),await o.create(r,{id:b(e,"u2"),name:"Bob",age:24,tenantId:"tenant-1",isActive:!1,email:"bob@example.com"}),await o.bulkCreate(r,[{id:b(e,"u3"),name:"Carla",age:29,tenantId:"tenant-2",isActive:!0,email:"carla@example.com"},{id:b(e,"u4"),name:"Dian",age:22,tenantId:"tenant-2",isActive:!0}]);let i=await o.get(r,b(e,"u1"));v((i==null?void 0:i.name)==="Alice","Expected get(table, id) to return Alice");let s=await o.query(r,{filters:{tenantId:"tenant-1"},sort:[{field:"age",direction:"desc"}]});v(s.length===2,`Expected 2 tenant-1 users, got ${s.length}`),v(s[0].age>=s[1].age,"Expected sorted by age desc");let a={and:[{field:"tenantId",value:"tenant-2"},{field:"isActive",value:!0}]},p=await o.query(r,{filters:a,sort:[{field:"name",direction:"asc"}]});v(p.length===2,`Expected 2 active tenant-2 users, got ${p.length}`);let c={filters:{and:[{field:"id",value:b(e,"u2")},{field:"tenantId",value:"tenant-1"}]}},d=await o.get(r,c);v((d==null?void 0:d.name)==="Bob","Expected get(table, options) to resolve Bob");let m=await o.update(r,b(e,"u1"),{age:32});v(m.age===32,`Expected age updated to 32, got ${m.age}`);let l=await o.bulkUpdate(r,[{id:b(e,"u3"),updates:{age:30}},{id:b(e,"u4"),updates:{age:23}}]);v(l.length===2,`Expected 2 bulk-updated rows, got ${l.length}`);let g=await o.query(r,{sort:[{field:"age",direction:"asc"}],limit:2,offset:1});v(g.length===2,`Expected 2 paged rows, got ${g.length}`);try{throw await o.transaction(async h=>{throw await h.create(r,{id:b(e,"tx_rollback"),name:"TxRollback",age:40,tenantId:"tenant-1",isActive:!0}),new Error("rollback-intent")}),new Error("Expected transaction rollback to throw")}catch(h){let A=h instanceof Error?h.message:String(h);v(A.includes("rollback-intent"),`Expected rollback-intent error, got ${A}`)}let f=await o.get(r,b(e,"tx_rollback"));v(f===null,"Expected rolled back record to not exist"),await o.delete(r,b(e,"u2"));let y=await o.get(r,b(e,"u2"));v(y===null,"Expected deleted record to be null")}var k=class{constructor(t,e){this.extensions=new Map;var r,n,i,s,a,p,c,d,m,l,g,f,y;this.platformAdapter=(r=e.platformAdapter)==null?void 0:r.call(e,t),this.dbAdapter=(n=e.dbAdapter)==null?void 0:n.call(e,t),this.loggerAdapter=(i=e.loggerAdapter)==null?void 0:i.call(e,t),this.httpAdapter=(s=e.httpAdapter)==null?void 0:s.call(e,t),this.httpRetryPolicy=(p=(a=e.httpRetryPolicy)==null?void 0:a.call(e,t))!=null?p:new w(t),this.socketAdapter=(c=e.socketAdapter)==null?void 0:c.call(e,t),this.encryptionAdapter=(d=e.encryptionAdapter)==null?void 0:d.call(e,t),this.checksumAdapter=(l=(m=e.checksumAdapter)==null?void 0:m.call(e,t))!=null?l:new S(t),this.activitySink=(f=(g=e.activitySink)==null?void 0:g.call(e,t))!=null?f:new x(t);for(let[u,h]of Object.entries((y=e.extensions)!=null?y:{}))this.extensions.set(u,h(t))}getExtension(t){return this.extensions.get(t)}clear(){this.extensions.clear()}};function N(o){return!!o&&typeof o.shutdown=="function"}var T=class{constructor(){this.correlationCounter=0;this.started=!1;this.pluginsLoaded=!1;this.pendingPlugins=[];this.hooks={}}static builder(){return new R}setHooks(t){this.hooks={...this.hooks,...t}}use(t,e,r=[]){if(!t.trim())throw new Error("plugin name is required");return this.pendingPlugins.some(n=>n.name===t)?this:(this.pendingPlugins.push({name:t,init:e,deps:r}),this)}async init(t={runMigrations:!0,useSeedData:!1}){if(!this.registry)throw new Error("CoreRuntime registry is not initialized. Call builder().build() first.");if(t.runMigrations!==!1&&this.hooks.runMigrations&&await this.hooks.runMigrations(this),this.hooks.onInit&&await this.hooks.onInit(this),!this.domainServices&&this.hooks.createDomainServices&&(this.domainServices=await this.hooks.createDomainServices(this)),!this.integrationServices&&this.hooks.createIntegrationServices&&(this.integrationServices=await this.hooks.createIntegrationServices(this)),!this.pluginsLoaded){let n=this.resolvePluginLevels();for(let i of n)await Promise.all(i.map(s=>s.init(this)));this.pluginsLoaded=!0}t.useSeedData===!0&&this.hooks.runSeed&&await this.hooks.runSeed(this)}async start(t={}){if(this.started)throw new Error("CoreRuntime already started");try{await this.init(t),this.started=!0}catch(e){throw this.started=!1,e}}async stop(){var t;this.started&&(this.hooks.onStop&&await this.hooks.onStop(this),N(this.integrationServices)&&await this.integrationServices.shutdown(),N(this.domainServices)&&await this.domainServices.shutdown(),(t=this.registry)==null||t.clear(),this.started=!1)}async reset(){await this.stop(),this.domainServices=void 0,this.integrationServices=void 0,this.pendingPlugins=[],this.pluginsLoaded=!1,this.hooks={}}async restart(){let t=[...this.pendingPlugins],e={...this.hooks};await this.reset(),this.pendingPlugins=t,this.hooks=e,await this.start()}isStarted(){return this.started}createCorrelationId(t="cid"){return this.correlationCounter+=1,`${t}-${Date.now()}-${this.correlationCounter}`}logInfo(t,e){var r,n;(n=(r=this.registry)==null?void 0:r.loggerAdapter)==null||n.logInfo(t,e)}logWarn(t,e){var r,n;(n=(r=this.registry)==null?void 0:r.loggerAdapter)==null||n.logWarn(t,e)}logError(t,e){var r,n;(n=(r=this.registry)==null?void 0:r.loggerAdapter)==null||n.logError(t,e)}async emitActivity(t){var e;(e=this.registry)!=null&&e.activitySink&&await this.registry.activitySink.publish(t)}resolvePluginLevels(){var s,a,p;if(this.pendingPlugins.length===0)return[];let t=new Map,e=new Map,r=new Map;for(let c of this.pendingPlugins){if(t.has(c.name))throw new Error(`Duplicate plugin name: ${c.name}`);t.set(c.name,[]),e.set(c.name,0),r.set(c.name,c)}for(let c of this.pendingPlugins)for(let d of c.deps){if(!t.has(d))throw new Error(`Plugin "${c.name}" depends on unknown plugin "${d}"`);t.get(d).push(c.name),e.set(c.name,((s=e.get(c.name))!=null?s:0)+1)}let n=[],i=Array.from(e.entries()).filter(([,c])=>c===0).map(([c])=>c);for(;i.length>0;){let c=i.map(m=>r.get(m));n.push(c);let d=[];for(let m of i)for(let l of(a=t.get(m))!=null?a:[]){let g=((p=e.get(l))!=null?p:0)-1;e.set(l,g),g===0&&d.push(l)}i=d}if(n.flat().length!==this.pendingPlugins.length)throw new Error("Circular dependency detected among plugins");return n}},R=class{constructor(){this.runtime=new T;this.adapterOptions={};this.hooks={}}withPlatformAdapter(t){return this.adapterOptions.platformAdapter=t,this}withDbAdapter(t){return this.adapterOptions.dbAdapter=t,this}withHttpAdapter(t){return this.adapterOptions.httpAdapter=t,this}withHttpRetryPolicy(t){return this.adapterOptions.httpRetryPolicy=t,this}withSocketAdapter(t){return this.adapterOptions.socketAdapter=t,this}withEncryptionAdapter(t){return this.adapterOptions.encryptionAdapter=t,this}withChecksumAdapter(t){return this.adapterOptions.checksumAdapter=t,this}withActivitySink(t){return this.adapterOptions.activitySink=t,this}withLoggerAdapter(t){return this.adapterOptions.loggerAdapter=t,this}withExtension(t,e){var r;return this.adapterOptions.extensions=(r=this.adapterOptions.extensions)!=null?r:{},this.adapterOptions.extensions[t]=e,this}withHooks(t){return this.hooks={...this.hooks,...t},this}build(){return this.runtime.setHooks(this.hooks),this.runtime.registry=new k(this.runtime,this.adapterOptions),this.runtime}};function at(o){let t=o,e=new Set,r=(n,i)=>{for(let s of e)s(n,i)};return{getState(){return t},setState(n){let i=t;return t=typeof n=="function"?n(t):{...t,...n},r(t,i),t},reset(n){let i=t;return t=n!=null?n:o,r(t,i),t},subscribe(n){return e.add(n),()=>{e.delete(n)}}}}function ct(o){return{getState:o.getState,subscribe:o.subscribe}}
@@ -0,0 +1,34 @@
1
+ export * from './ports/DbAdapter';
2
+ export * from './ports/PlatformAdapter';
3
+ export * from './ports/StorageAdapter';
4
+ export * from './contracts/LoggerAdapter';
5
+ export * from './contracts/HttpAdapter';
6
+ export * from './contracts/HttpRetryPolicy';
7
+ export * from './contracts/SocketAdapter';
8
+ export * from './contracts/NotificationAdapter';
9
+ export * from './contracts/ExportAdapter';
10
+ export * from './contracts/EncryptionAdapter';
11
+ export * from './contracts/ActivitySink';
12
+ export * from './contracts/ChecksumPrimitives';
13
+ export * from './contracts/ResponseEnvelope';
14
+ export * from './contracts/ContractVersioning';
15
+ export * from './contracts/CrossDomainPrimitives';
16
+ export * from './defaults/InMemoryDbAdapter';
17
+ export * from './defaults/ConsoleLoggerAdapter';
18
+ export * from './defaults/DefaultHttpAdapter';
19
+ export * from './defaults/NoRetryHttpPolicy';
20
+ export * from './defaults/ExponentialBackoffHttpPolicy';
21
+ export * from './defaults/InMemorySocketAdapter';
22
+ export * from './defaults/NoopNotificationAdapter';
23
+ export * from './defaults/NoopEncryptionAdapter';
24
+ export * from './defaults/NoopChecksumAdapter';
25
+ export * from './defaults/NoopActivitySink';
26
+ export * from './defaults/MemoryActivitySink';
27
+ export * from './defaults/DefaultExportAdapter';
28
+ export * from './defaults/MemoryStorageAdapter';
29
+ export * from './migration/types';
30
+ export * from './migration/migrations';
31
+ export * from './migration/applyPendingMigrations';
32
+ export * from './testing/DbAdapterContractHarness';
33
+ export * from './runtime/CoreRuntime';
34
+ export * from './runtime/createStore';
@@ -0,0 +1 @@
1
+ var C=class{constructor(t){}getEnvVar(t){let e=globalThis.process;if(typeof e!="undefined"&&e.env)return e.env[t]}isOnline(){return typeof globalThis.navigator!="undefined"&&typeof globalThis.navigator.onLine!="undefined"?!!globalThis.navigator.onLine:!0}async hashPin(t){return`sha256:${t}`}async verifyPin(t,e){return await this.hashPin(t)===e}};async function q(o){o<=0||await new Promise(t=>setTimeout(t,o))}async function K(o,t,e={}){var s;let r=e.policy,n=(s=e.sleep)!=null?s:q,i=1;for(;;)try{let a=await o.request(t);if(!r)return a;let p={request:t,attempt:i,response:a};if(!r.shouldRetry(p))return a;let c=Math.max(0,r.nextDelayMs(p));await n(c),i+=1}catch(a){if(!r)throw a;let p={request:t,attempt:i,error:a};if(!r.shouldRetry(p))throw a;let c=Math.max(0,r.nextDelayMs(p));await n(c),i+=1}}function P(o){return String(o||"").trim().toLowerCase()}function Y(o,t){return P(o)===P(t)}function tt(o){return typeof(o==null?void 0:o.error)=="object"&&o!==null}function rt(o,t,e){return e===o?!0:t.includes(e)}var E=class{constructor(){this.schema={version:0,tables:[]};this.data={};this.transactionDepth=0;this.snapshots=[]}async setUpWithSchema(t){this.schema={...t},this.data={},t.tables.forEach(e=>{this.data[e.name]=[]})}normalizeForComparison(t){if(t instanceof Date)return t.getTime();if(typeof t=="boolean")return t?1:0;if(typeof t=="string"||typeof t=="number"||t==null)return t;if(Array.isArray(t))return t.map(e=>this.normalizeForComparison(e));if(typeof t=="object"&&t!==null)try{return JSON.stringify(t)}catch{return String(t)}return t}evalValueExpr(t,e,r,n="t0"){if(r.type==="column"){let i=r.tableAlias||n,s=i===n?t:e[i]||{};return this.normalizeForComparison(s[r.field])}if(r.type==="literal")return this.normalizeForComparison(r.value);throw new Error(`Unsupported ValueExpression type: ${r.type}`)}buildFieldCondition(t,e){if("field"in t){let n={type:"column",tableAlias:t.alias||e,field:t.field},i={type:"literal",value:t.value};return{left:n,operator:t.operator||"=",right:i,...t.operator==="BETWEEN"?{}:{likeMode:t.likeMode}}}if("left"in t)return t;throw new Error("Invalid FieldCondition")}matchFilterExpression(t,e,r,n="t0"){if(r&&typeof r=="object"&&!Array.isArray(r)&&"and"in r)return r.and.every(i=>this.matchFilterExpression(t,e,i,n));if(r&&typeof r=="object"&&!Array.isArray(r)&&"or"in r)return r.or.some(i=>this.matchFilterExpression(t,e,i,n));if(r!==null&&typeof r=="object"&&!Array.isArray(r)){if("left"in r||"field"in r){let i=this.buildFieldCondition(r,n),s=this.evalValueExpr(t,e,i.left,n),a=this.evalValueExpr(t,e,i.right,n);if(i.operator==="IS NULL")return s==null;if(i.operator==="IS NOT NULL")return s!=null;if(i.operator==="IN"||i.operator==="NOT IN"){if(!Array.isArray(a))throw new Error("IN/NOT IN requires array on right side");let p=a.includes(s);return i.operator==="IN"?p:!p}switch(i.operator){case"=":return s===a;case"!=":return s!==a;case"<":return s<a;case"<=":return s<=a;case">":return s>a;case">=":return s>=a;case"LIKE":{if(typeof s!="string"||typeof a!="string")return!1;let p=a.replace(/%/g,".*").replace(/_/g,"."),c=i.likeMode;return new RegExp(`^${p}$`,c==="case-insensitive"?"i":"").test(s)}default:throw new Error(`Unsupported operator: ${i.operator}`)}}return Object.entries(r).every(([i,s])=>{let a=this.normalizeForComparison(t[i]),p=this.normalizeForComparison(s);return a===p})}throw new Error(`Invalid filter expression: ${JSON.stringify(r)}`)}simulateJoins(t,e,r){if(r.length===0)return e.map(i=>({record:i,joined:{}}));let n=[];for(let i of e){let s={},a=!0;for(let p of r){let c=p.table,d=this.data[c]||[],m=null;if(p.conditions.length===1){let l=p.conditions[0];if(l.operator==="="&&l.left.type==="column"&&l.right.type==="column"){let g=l.left.tableAlias||"t0",f=l.right.tableAlias;if(g==="t0"&&f===p.alias){let y=l.left.field,u=l.right.field,h=i[y];m=d.find(A=>this.normalizeForComparison(A[u])===this.normalizeForComparison(h))}}}if(!m&&(p.type==="inner"||p.type===void 0)){a=!1;break}s[p.alias]=m||null}a&&n.push({record:i,joined:s})}return n}extractField(t,e,r,n="t0"){if(typeof r=="string")return this.normalizeForComparison(t[r]);let i=r.tableAlias||n,s=i===n?t:e[i]||{};return this.normalizeForComparison(s[r.field])}extractAggregateField(t,e,r,n="t0"){if(typeof r=="string")return this.normalizeForComparison(t[r]);let i=r.tableAlias||n,s=i===n?t:e[i]||{};return this.normalizeForComparison(s[r.field])}async getSchemaVersion(){return this.schema.version}async setSchemaVersion(t){this.schema.version=t}async addTable(t){this.data[t.name]||(this.data[t.name]=[]),this.schema.tables.find(e=>e.name===t.name)||this.schema.tables.push(t)}async addColumn(t,e){}async get(t,e){var n;if(typeof e=="string"){let s=(this.data[t]||[]).find(a=>a.id===e);return s||null}return(n=(await this.query(t,{...e,limit:1}))[0])!=null?n:null}async query(t,e={}){var c;let n=[...this.data[t]||[]],i=this.simulateJoins(t,n,e.joins||[]);if(e.filters&&(i=i.filter(({record:d,joined:m})=>this.matchFilterExpression(d,m,e.filters,"t0"))),e.aggregates&&e.aggregates.length>0){let d={};for(let m of e.aggregates){let l=i;m.filter&&(l=l.filter(({record:u,joined:h})=>this.matchFilterExpression(u,h,m.filter,"t0")));let g=l.map(({record:u,joined:h})=>this.extractAggregateField(u,h,m.field,"t0")),f=g.map(u=>typeof u=="number"?u:Number(u)).filter(u=>Number.isFinite(u)),y=null;switch(m.function){case"COUNT":y=g.filter(u=>u!=null).length;break;case"SUM":y=f.reduce((u,h)=>u+h,0);break;case"AVG":y=f.length?f.reduce((u,h)=>u+h,0)/f.length:0;break;case"MIN":y=f.length?Math.min(...f):null;break;case"MAX":y=f.length?Math.max(...f):null;break;default:throw new Error(`Unsupported aggregate function: ${m.function}`)}d[m.as]=y}return[d]}let s=i.map(({record:d,joined:m})=>{if(e.fields){let l={};for(let[g,f]of Object.entries(e.fields)){let y=g==="t0"?d:m[g]||{};for(let u of f)l[`${g}_${u}`]=y[u]}return l}return d});e.sort&&e.sort.length>0&&s.sort((d,m)=>{for(let l of e.sort){let g=this.extractField(d,{},l.field,"t0"),f=this.extractField(m,{},l.field,"t0");if(g<f)return l.direction==="asc"?-1:1;if(g>f)return l.direction==="asc"?1:-1}return 0});let a=(c=e.offset)!=null?c:0,p=e.limit!=null?a+e.limit:void 0;return s.slice(a,p)}async create(t,e){this.data[t]||(this.data[t]=[]);let r={...e};return this.data[t].push(r),r}async update(t,e,r){let n=this.data[t]||[],i=n.findIndex(a=>a.id===e);if(i===-1)throw new Error(`Record not found: ${t} with id ${e}`);let s={...n[i],...r};return n[i]=s,s}async delete(t,e){let r=this.data[t]||[],n=r.findIndex(i=>i.id===e);n>=0&&r.splice(n,1)}async bulkCreate(t,e){this.data[t]||(this.data[t]=[]);let r=[...this.data[t]];try{let n=[];for(let i of e){let s={...i};this.data[t].push(s),n.push(s)}return n}catch(n){throw this.data[t]=r,n}}async bulkUpdate(t,e){let r=[...this.data[t]||[]];try{let n=[];for(let{id:i,updates:s}of e){let a=await this.update(t,i,s);n.push(a)}return n}catch(n){throw this.data[t]=r,n}}takeSnapshot(){let t={};for(let e in this.data)t[e]=[...this.data[e]];return t}async transaction(t){let e=this.transactionDepth===0;e&&this.snapshots.push(this.takeSnapshot()),this.transactionDepth++;try{let r=await t(this);return e&&this.snapshots.pop(),this.transactionDepth--,r}catch(r){if(e){let n=this.snapshots.pop();n&&(this.data=n)}throw this.transactionDepth--,r}}};var D=class{constructor(t){}logInfo(t,e){console.log(t,e)}logWarn(t,e){console.warn(t,e)}logError(t,e){console.error(t,e)}};function B(o,t){if(!t||Object.keys(t).length===0)return o;let e=new URLSearchParams;for(let[n,i]of Object.entries(t))i!=null&&e.set(n,String(i));let r=e.toString();return r?o.includes("?")?`${o}&${r}`:`${o}?${r}`:o}var H=class{constructor(t){this.fetchImpl=t}async request(t){var s,a,p,c,d,m,l,g,f;let e=(s=this.fetchImpl)!=null?s:globalThis.fetch;if(typeof e!="function")throw new Error("DefaultHttpAdapter requires fetch implementation");let r=B(t.url,t.query),n=typeof AbortController!="undefined"?new AbortController:null,i=n&&t.timeoutMs?setTimeout(()=>n.abort(),t.timeoutMs):null;try{let y=t.body!==void 0&&t.body!==null,u={...(a=t.headers)!=null?a:{}};y&&!u["content-type"]&&(u["content-type"]="application/json");let h=await e(r,{method:t.method,headers:u,body:y?JSON.stringify(t.body):void 0,signal:n==null?void 0:n.signal}),A={};(c=(p=h.headers)==null?void 0:p.forEach)==null||c.call(p,(j,z)=>{A[z.toLowerCase()]=j});let N=((d=A["content-type"])!=null?d:"").includes("application/json")?await((l=(m=h.json)==null?void 0:m.call(h))!=null?l:Promise.resolve(void 0)):await((f=(g=h.text)==null?void 0:g.call(h))!=null?f:Promise.resolve(""));return{status:h.status,headers:A,data:N}}finally{i&&clearTimeout(i)}}};var w=class{constructor(t){}shouldRetry(t){return!1}nextDelayMs(t){return 0}};var W=[408,425,429,500,502,503,504],I=class{constructor(t={}){var e,r,n,i;this.maxAttempts=Math.max(1,(e=t.maxAttempts)!=null?e:3),this.baseDelayMs=Math.max(0,(r=t.baseDelayMs)!=null?r:250),this.maxDelayMs=Math.max(this.baseDelayMs,(n=t.maxDelayMs)!=null?n:3e3),this.retryableStatusCodes=new Set((i=t.retryableStatusCodes)!=null?i:W)}shouldRetry(t){var r,n;if(t.attempt>=this.maxAttempts)return!1;if(t.error)return!0;let e=(n=(r=t.response)==null?void 0:r.status)!=null?n:0;return this.retryableStatusCodes.has(e)}nextDelayMs(t){let e=Math.max(0,t.attempt-1),r=this.baseDelayMs*Math.pow(2,e);return Math.min(this.maxDelayMs,r)}};var F=class{constructor(){this.connected=!1;this.listeners=new Map}async connect(t){this.connectionOptions=t,this.connected=!0}async disconnect(){this.connected=!1}subscribe(t,e){this.listeners.has(t)||this.listeners.set(t,new Set);let r=this.listeners.get(t);return r.add(e),()=>{r.delete(e),r.size===0&&this.listeners.delete(t)}}async publish(t,e){if(!this.connected)throw new Error(`InMemorySocketAdapter is not connected (event=${t})`);let r=this.listeners.get(t);if(!(!r||r.size===0))for(let n of r)n(e)}isConnected(){return this.connected}getConnectionOptions(){return this.connectionOptions}};var M=class{constructor(t){}configure(t){}createChannel(t){}localNotification(t){}};var O=class{constructor(t){}encrypt(t,e){return String(t)}decrypt(t,e){return String(t)}};var S=class{constructor(t){}compute(t){return""}verify(t,e){return String(e||"").trim()===""}};var x=class{constructor(t){}publish(t){}};var V=class{constructor(t){this.records=[]}publish(t){this.records.push(t)}getRecords(){return[...this.records]}clear(){this.records.length=0}};var L=class{constructor(t){}getDocumentPath(t){return t}async writeFile(t,e){throw new Error("exportAdapter.writeFile is not implemented for this platform")}async share(t){throw new Error("exportAdapter.share is not implemented for this platform")}};var _=class{constructor(t){this.map=new Map}getItem(t){var e;return(e=this.map.get(t))!=null?e:null}setItem(t,e){this.map.set(t,e)}removeItem(t){this.map.delete(t)}};var $=[];async function vt(o,t){if(typeof o.getSchemaVersion!="function"||typeof o.setSchemaVersion!="function"){let e="Adapter does not support schema versioning (getSchemaVersion/setSchemaVersion required)";throw t.logError(new Error(e),{message:e}),new Error(e)}t.logInfo("Starting database migration process");try{let e=[...$].sort((i,s)=>i.toVersion-s.toVersion),r=e.length>0?e[e.length-1].toVersion:0,n=await o.getSchemaVersion();if((n==null||n<0)&&(n=0),n>=r){t.logInfo(`Database is already up-to-date at v${n}`);return}t.logInfo(`Migrating database from v${n} to v${r}`);for(let i of e)i.toVersion>n&&(t.logInfo(`Applying migration: v${i.toVersion}`),await i.up(o),await o.setSchemaVersion(i.toVersion),n=i.toVersion);t.logInfo("Database migration completed successfully")}catch(e){throw t.logError(e,{message:"Database migration failed"}),e}}function v(o,t){if(!o)throw new Error(t)}function b(o,t){return`${o}-${t}`}function J(o){let t=[{name:"id",type:"string"},{name:"name",type:"string"},{name:"age",type:"number"},{name:"tenantId",type:"string"},{name:"isActive",type:"boolean"}];return{name:`${o}_users`,columns:t,skipMetadataColumns:!0}}async function wt(o,t={}){var u;let e=((u=t.prefix)!=null?u:`dbadapter_${Date.now()}`).replace(/[^a-zA-Z0-9_]/g,"_"),r=`${e}_users`;await o.setSchemaVersion(7);let n=await o.getSchemaVersion();v(n===7,`Expected schema version 7, got ${n}`),await o.addTable(J(e)),await o.addColumn(r,{name:"email",type:"string",isOptional:!0}),await o.create(r,{id:b(e,"u1"),name:"Alice",age:31,tenantId:"tenant-1",isActive:!0,email:"alice@example.com"}),await o.create(r,{id:b(e,"u2"),name:"Bob",age:24,tenantId:"tenant-1",isActive:!1,email:"bob@example.com"}),await o.bulkCreate(r,[{id:b(e,"u3"),name:"Carla",age:29,tenantId:"tenant-2",isActive:!0,email:"carla@example.com"},{id:b(e,"u4"),name:"Dian",age:22,tenantId:"tenant-2",isActive:!0}]);let i=await o.get(r,b(e,"u1"));v((i==null?void 0:i.name)==="Alice","Expected get(table, id) to return Alice");let s=await o.query(r,{filters:{tenantId:"tenant-1"},sort:[{field:"age",direction:"desc"}]});v(s.length===2,`Expected 2 tenant-1 users, got ${s.length}`),v(s[0].age>=s[1].age,"Expected sorted by age desc");let a={and:[{field:"tenantId",value:"tenant-2"},{field:"isActive",value:!0}]},p=await o.query(r,{filters:a,sort:[{field:"name",direction:"asc"}]});v(p.length===2,`Expected 2 active tenant-2 users, got ${p.length}`);let c={filters:{and:[{field:"id",value:b(e,"u2")},{field:"tenantId",value:"tenant-1"}]}},d=await o.get(r,c);v((d==null?void 0:d.name)==="Bob","Expected get(table, options) to resolve Bob");let m=await o.update(r,b(e,"u1"),{age:32});v(m.age===32,`Expected age updated to 32, got ${m.age}`);let l=await o.bulkUpdate(r,[{id:b(e,"u3"),updates:{age:30}},{id:b(e,"u4"),updates:{age:23}}]);v(l.length===2,`Expected 2 bulk-updated rows, got ${l.length}`);let g=await o.query(r,{sort:[{field:"age",direction:"asc"}],limit:2,offset:1});v(g.length===2,`Expected 2 paged rows, got ${g.length}`);try{throw await o.transaction(async h=>{throw await h.create(r,{id:b(e,"tx_rollback"),name:"TxRollback",age:40,tenantId:"tenant-1",isActive:!0}),new Error("rollback-intent")}),new Error("Expected transaction rollback to throw")}catch(h){let A=h instanceof Error?h.message:String(h);v(A.includes("rollback-intent"),`Expected rollback-intent error, got ${A}`)}let f=await o.get(r,b(e,"tx_rollback"));v(f===null,"Expected rolled back record to not exist"),await o.delete(r,b(e,"u2"));let y=await o.get(r,b(e,"u2"));v(y===null,"Expected deleted record to be null")}var k=class{constructor(t,e){this.extensions=new Map;var r,n,i,s,a,p,c,d,m,l,g,f,y;this.platformAdapter=(r=e.platformAdapter)==null?void 0:r.call(e,t),this.dbAdapter=(n=e.dbAdapter)==null?void 0:n.call(e,t),this.loggerAdapter=(i=e.loggerAdapter)==null?void 0:i.call(e,t),this.httpAdapter=(s=e.httpAdapter)==null?void 0:s.call(e,t),this.httpRetryPolicy=(p=(a=e.httpRetryPolicy)==null?void 0:a.call(e,t))!=null?p:new w(t),this.socketAdapter=(c=e.socketAdapter)==null?void 0:c.call(e,t),this.encryptionAdapter=(d=e.encryptionAdapter)==null?void 0:d.call(e,t),this.checksumAdapter=(l=(m=e.checksumAdapter)==null?void 0:m.call(e,t))!=null?l:new S(t),this.activitySink=(f=(g=e.activitySink)==null?void 0:g.call(e,t))!=null?f:new x(t);for(let[u,h]of Object.entries((y=e.extensions)!=null?y:{}))this.extensions.set(u,h(t))}getExtension(t){return this.extensions.get(t)}clear(){this.extensions.clear()}};function U(o){return!!o&&typeof o.shutdown=="function"}var T=class{constructor(){this.correlationCounter=0;this.started=!1;this.pluginsLoaded=!1;this.pendingPlugins=[];this.hooks={}}static builder(){return new R}setHooks(t){this.hooks={...this.hooks,...t}}use(t,e,r=[]){if(!t.trim())throw new Error("plugin name is required");return this.pendingPlugins.some(n=>n.name===t)?this:(this.pendingPlugins.push({name:t,init:e,deps:r}),this)}async init(t={runMigrations:!0,useSeedData:!1}){if(!this.registry)throw new Error("CoreRuntime registry is not initialized. Call builder().build() first.");if(t.runMigrations!==!1&&this.hooks.runMigrations&&await this.hooks.runMigrations(this),this.hooks.onInit&&await this.hooks.onInit(this),!this.domainServices&&this.hooks.createDomainServices&&(this.domainServices=await this.hooks.createDomainServices(this)),!this.integrationServices&&this.hooks.createIntegrationServices&&(this.integrationServices=await this.hooks.createIntegrationServices(this)),!this.pluginsLoaded){let n=this.resolvePluginLevels();for(let i of n)await Promise.all(i.map(s=>s.init(this)));this.pluginsLoaded=!0}t.useSeedData===!0&&this.hooks.runSeed&&await this.hooks.runSeed(this)}async start(t={}){if(this.started)throw new Error("CoreRuntime already started");try{await this.init(t),this.started=!0}catch(e){throw this.started=!1,e}}async stop(){var t;this.started&&(this.hooks.onStop&&await this.hooks.onStop(this),U(this.integrationServices)&&await this.integrationServices.shutdown(),U(this.domainServices)&&await this.domainServices.shutdown(),(t=this.registry)==null||t.clear(),this.started=!1)}async reset(){await this.stop(),this.domainServices=void 0,this.integrationServices=void 0,this.pendingPlugins=[],this.pluginsLoaded=!1,this.hooks={}}async restart(){let t=[...this.pendingPlugins],e={...this.hooks};await this.reset(),this.pendingPlugins=t,this.hooks=e,await this.start()}isStarted(){return this.started}createCorrelationId(t="cid"){return this.correlationCounter+=1,`${t}-${Date.now()}-${this.correlationCounter}`}logInfo(t,e){var r,n;(n=(r=this.registry)==null?void 0:r.loggerAdapter)==null||n.logInfo(t,e)}logWarn(t,e){var r,n;(n=(r=this.registry)==null?void 0:r.loggerAdapter)==null||n.logWarn(t,e)}logError(t,e){var r,n;(n=(r=this.registry)==null?void 0:r.loggerAdapter)==null||n.logError(t,e)}async emitActivity(t){var e;(e=this.registry)!=null&&e.activitySink&&await this.registry.activitySink.publish(t)}resolvePluginLevels(){var s,a,p;if(this.pendingPlugins.length===0)return[];let t=new Map,e=new Map,r=new Map;for(let c of this.pendingPlugins){if(t.has(c.name))throw new Error(`Duplicate plugin name: ${c.name}`);t.set(c.name,[]),e.set(c.name,0),r.set(c.name,c)}for(let c of this.pendingPlugins)for(let d of c.deps){if(!t.has(d))throw new Error(`Plugin "${c.name}" depends on unknown plugin "${d}"`);t.get(d).push(c.name),e.set(c.name,((s=e.get(c.name))!=null?s:0)+1)}let n=[],i=Array.from(e.entries()).filter(([,c])=>c===0).map(([c])=>c);for(;i.length>0;){let c=i.map(m=>r.get(m));n.push(c);let d=[];for(let m of i)for(let l of(a=t.get(m))!=null?a:[]){let g=((p=e.get(l))!=null?p:0)-1;e.set(l,g),g===0&&d.push(l)}i=d}if(n.flat().length!==this.pendingPlugins.length)throw new Error("Circular dependency detected among plugins");return n}},R=class{constructor(){this.runtime=new T;this.adapterOptions={};this.hooks={}}withPlatformAdapter(t){return this.adapterOptions.platformAdapter=t,this}withDbAdapter(t){return this.adapterOptions.dbAdapter=t,this}withHttpAdapter(t){return this.adapterOptions.httpAdapter=t,this}withHttpRetryPolicy(t){return this.adapterOptions.httpRetryPolicy=t,this}withSocketAdapter(t){return this.adapterOptions.socketAdapter=t,this}withEncryptionAdapter(t){return this.adapterOptions.encryptionAdapter=t,this}withChecksumAdapter(t){return this.adapterOptions.checksumAdapter=t,this}withActivitySink(t){return this.adapterOptions.activitySink=t,this}withLoggerAdapter(t){return this.adapterOptions.loggerAdapter=t,this}withExtension(t,e){var r;return this.adapterOptions.extensions=(r=this.adapterOptions.extensions)!=null?r:{},this.adapterOptions.extensions[t]=e,this}withHooks(t){return this.hooks={...this.hooks,...t},this}build(){return this.runtime.setHooks(this.hooks),this.runtime.registry=new k(this.runtime,this.adapterOptions),this.runtime}};function Ct(o){let t=o,e=new Set,r=(n,i)=>{for(let s of e)s(n,i)};return{getState(){return t},setState(n){let i=t;return t=typeof n=="function"?n(t):{...t,...n},r(t,i),t},reset(n){let i=t;return t=n!=null?n:o,r(t,i),t},subscribe(n){return e.add(n),()=>{e.delete(n)}}}}function Pt(o){return{getState:o.getState,subscribe:o.subscribe}}export{D as ConsoleLoggerAdapter,k as CoreAdapterRegistry,T as CoreRuntime,R as CoreRuntimeBuilder,L as DefaultExportAdapter,H as DefaultHttpAdapter,C as DefaultPlatformAdapter,I as ExponentialBackoffHttpPolicy,E as InMemoryDbAdapter,F as InMemorySocketAdapter,V as MemoryActivitySink,_ as MemoryStorageAdapter,w as NoRetryHttpPolicy,x as NoopActivitySink,S as NoopChecksumAdapter,O as NoopEncryptionAdapter,M as NoopNotificationAdapter,vt as applyPendingMigrations,Y as areChecksumsEqual,Pt as asReadonlyStore,Ct as createStore,K as executeHttpRequestWithRetry,rt as isContractVersionCompatible,tt as isFailureEnvelope,$ as migrations,P as normalizeChecksum,wt as runDbAdapterContractHarness};
@@ -0,0 +1,10 @@
1
+ import type { DbAdapter } from '../ports/DbAdapter';
2
+ import type { LoggerAdapter } from '../contracts/LoggerAdapter';
3
+ /**
4
+ * Generic migration gate helper.
5
+ *
6
+ * Catatan fase skeleton:
7
+ * - Daftar migrasi disuplai dari pemanggil.
8
+ * - Tidak mengandung domain schema apapun.
9
+ */
10
+ export declare function applyPendingMigrations(adapter: Partial<DbAdapter>, logger: LoggerAdapter): Promise<void>;
@@ -0,0 +1,6 @@
1
+ import type { Migration } from './types';
2
+ /**
3
+ * Placeholder migration registry for ofcore skeleton.
4
+ * Domain packages may compose/extend this strategy in later phases.
5
+ */
6
+ export declare const migrations: Migration[];
@@ -0,0 +1,5 @@
1
+ import type { DbAdapter } from '../ports/DbAdapter';
2
+ export interface Migration {
3
+ toVersion: number;
4
+ up(adapter: DbAdapter): Promise<void>;
5
+ }
@@ -0,0 +1,306 @@
1
+ /**
2
+ * Database Adapter Interface — Database-Agnostic, Type-Safe, and Secure by Design.
3
+ *
4
+ * Design principles:
5
+ * - No raw strings for identifiers or values in interface (prevents injection at contract level)
6
+ * - All expressions structured and type-safe
7
+ * - Shorthand syntax preserved for great DX
8
+ * - Case behavior for LIKE controlled via 'likeMode' (intent-based, not syntax-based)
9
+ * - Fully implementable by PostgreSQL, SQLite, MySQL, SQL Server, etc.
10
+ */
11
+ /**
12
+ * Aggregate function yang didukung semua database utama.
13
+ */
14
+ export type AggregateFunction = 'SUM' | 'COUNT' | 'AVG' | 'MIN' | 'MAX';
15
+ /**
16
+ * Spesifikasi aggregate — field bisa dari main table atau joined table.
17
+ */
18
+ export interface AggregateSpec {
19
+ function: AggregateFunction;
20
+ /**
21
+ * Field yang di-aggregate.
22
+ * Bisa string (main table) atau qualified dengan alias.
23
+ */
24
+ field: string | {
25
+ tableAlias: string;
26
+ field: string;
27
+ };
28
+ /**
29
+ * Alias hasil aggregate (wajib, karena tidak bisa pakai nama field asli).
30
+ */
31
+ as: string;
32
+ /**
33
+ * Filter khusus untuk aggregate ini (misal hanya transaksi cash).
34
+ * Opsional — jarang dipakai tapi powerful.
35
+ */
36
+ filter?: FilterExpression;
37
+ }
38
+ /**
39
+ * Group by specification — field qualified.
40
+ */
41
+ export interface GroupBySpec {
42
+ field: string | {
43
+ tableAlias: string;
44
+ field: string;
45
+ };
46
+ }
47
+ /**
48
+ * Having condition — sama seperti filter, tapi untuk aggregate.
49
+ */
50
+ export type HavingExpression = FilterExpression;
51
+ /**
52
+ * Supported primitive column types (logical, not physical).
53
+ * Actual DDL mapping is adapter-specific.
54
+ */
55
+ export type ColumnType = 'string' | 'string[]' | 'number' | 'boolean' | 'date' | 'json';
56
+ /**
57
+ * Literal values only — no functions, no raw SQL, no objects with methods.
58
+ * Ensures safe parameter binding across all databases.
59
+ */
60
+ export type LiteralValue = string | number | boolean | Date | null | LiteralValue[] | {
61
+ [key: string]: LiteralValue;
62
+ };
63
+ /**
64
+ * Value expression: only two safe forms — column reference or literal.
65
+ * No raw strings, no arbitrary functions.
66
+ */
67
+ export type ValueExpression = {
68
+ type: 'column';
69
+ tableAlias?: string;
70
+ field: string;
71
+ } | {
72
+ type: 'literal';
73
+ value: LiteralValue;
74
+ };
75
+ /**
76
+ * Standard SQL operators supported by all major databases.
77
+ * Note: Behavior of 'LIKE' (case sensitivity) is DB-dependent — use 'likeMode' to control intent.
78
+ */
79
+ export type Operator = '=' | '!=' | '<' | '<=' | '>' | '>=' | 'IN' | 'NOT IN' | 'LIKE' | 'IS NULL' | 'IS NOT NULL' | 'BETWEEN';
80
+ /**
81
+ * Explicit form: full control, join-aware, type-safe.
82
+ * Recommended when:
83
+ * - Comparing column vs column (e.g., u.id = o.user_id)
84
+ * - Need precise alias control
85
+ * - Building complex queries
86
+ */
87
+ export interface ExplicitFieldCondition {
88
+ left: ValueExpression;
89
+ operator: Operator;
90
+ right: ValueExpression;
91
+ likeMode?: 'default' | 'case-sensitive' | 'case-insensitive';
92
+ }
93
+ /**
94
+ * Simple form: concise, intuitive, main-table focused.
95
+ * Recommended when:
96
+ * - Filtering on main table columns
97
+ * - Quick equality/LIKE checks
98
+ * - Developer prefers minimal syntax
99
+ */
100
+ export interface SimpleFieldCondition {
101
+ field: string;
102
+ operator?: Operator;
103
+ value: LiteralValue;
104
+ alias?: string;
105
+ likeMode?: 'default' | 'case-sensitive' | 'case-insensitive';
106
+ }
107
+ export interface BetweenCondition {
108
+ field: string;
109
+ operator: 'BETWEEN';
110
+ value: [LiteralValue, LiteralValue];
111
+ alias?: string;
112
+ }
113
+ /**
114
+ * Unified field condition — both forms are first-class.
115
+ */
116
+ export type FieldCondition = ExplicitFieldCondition | SimpleFieldCondition | BetweenCondition;
117
+ /**
118
+ * Shorthand for common AND-equality filters: { status: 'active', role: 'admin' }.
119
+ * Syntactic sugar — will be expanded to AND of FieldCondition during query building.
120
+ * Must be plain object with string keys and LiteralValue values.
121
+ */
122
+ export type ShorthandCondition = Record<string, LiteralValue>;
123
+ /**
124
+ * Recursive filter composition.
125
+ */
126
+ export interface AndNode {
127
+ and: FilterExpression[];
128
+ }
129
+ export interface OrNode {
130
+ or: FilterExpression[];
131
+ }
132
+ /**
133
+ * Unified filter expression type.
134
+ * Order matters: more specific types first to avoid overlap in type narrowing.
135
+ */
136
+ export type FilterExpression = FieldCondition | ShorthandCondition | AndNode | OrNode;
137
+ /**
138
+ * Canonical join subset for cross-runtime compatibility.
139
+ *
140
+ * Reason:
141
+ * - `ofpos-host app` commonly uses SQLite as local DB engine.
142
+ * - SQLite does not support RIGHT/FULL JOIN across standard adapters.
143
+ * - `ofpos-shared-core` contract must remain portable and deterministic.
144
+ *
145
+ * If an implementer needs RIGHT/FULL behavior, it must be rewritten using
146
+ * equivalent INNER/LEFT patterns in domain/query design.
147
+ */
148
+ export type JoinType = 'inner' | 'left';
149
+ /**
150
+ * A single join condition — always compares two ValueExpression.
151
+ * At least one condition is required per join.
152
+ */
153
+ export interface JoinCondition {
154
+ left: ValueExpression;
155
+ operator: Operator;
156
+ right: ValueExpression;
157
+ }
158
+ /**
159
+ * Join definition.
160
+ * alias is REQUIRED to avoid ambiguity and injection via auto-generated aliases.
161
+ */
162
+ export interface JoinDefinition {
163
+ table: string;
164
+ alias: string;
165
+ type?: JoinType;
166
+ conditions: JoinCondition[];
167
+ }
168
+ /**
169
+ * Sort specification — fully qualified field.
170
+ */
171
+ export interface SortSpec {
172
+ field: {
173
+ tableAlias: string;
174
+ field: string;
175
+ } | string;
176
+ direction: 'asc' | 'desc';
177
+ }
178
+ export type IncludeSpec = string[] | {
179
+ [key: string]: true | IncludeSpec;
180
+ };
181
+ export interface QueryOptions {
182
+ /**
183
+ * Select specific fields per alias.
184
+ * e.g. { t0: ['id', 'name'], p: ['role'] }
185
+ * If omitted, selects all fields from main table (and joined tables if joins exist).
186
+ */
187
+ fields?: {
188
+ [tableAlias: string]: string[];
189
+ };
190
+ /**
191
+ * Filter conditions — root node.
192
+ */
193
+ filters?: FilterExpression;
194
+ /**
195
+ * Sort specifications.
196
+ */
197
+ sort?: SortSpec[];
198
+ /**
199
+ * Pagination.
200
+ */
201
+ limit?: number;
202
+ offset?: number;
203
+ /**
204
+ * Joins to include.
205
+ */
206
+ joins?: JoinDefinition[];
207
+ /**
208
+ * Aggregation — jika ada, query berubah jadi SELECT dengan aggregate.
209
+ * Jika digunakan, `fields` akan diabaikan (kecuali untuk GROUP BY).
210
+ */
211
+ aggregates?: AggregateSpec[];
212
+ /**
213
+ * GROUP BY — wajib jika ada aggregates.
214
+ */
215
+ groupBy?: GroupBySpec[];
216
+ /**
217
+ * HAVING — filter setelah GROUP BY.
218
+ */
219
+ having?: HavingExpression;
220
+ /**
221
+ * Jika tidak di isi maka mainAlias = 't0'
222
+ */
223
+ mainAlias?: string;
224
+ include?: IncludeSpec;
225
+ }
226
+ export interface ColumnRelation {
227
+ table: string;
228
+ foreignKey?: string;
229
+ type: 'one-to-one' | 'one-to-many' | 'many-to-one' | 'many-to-many';
230
+ via?: string;
231
+ onDelete?: 'CASCADE' | 'SET NULL';
232
+ pivotLocalKey?: string;
233
+ /** Hanya untuk many-to-many – nama kolom di pivot yang nunjuk ke tabel target */
234
+ pivotForeignKey?: string;
235
+ }
236
+ export interface PolymorphicRelation {
237
+ typeField: string;
238
+ idField: string;
239
+ types: Record<string, {
240
+ table: string;
241
+ foreignKey?: string;
242
+ }>;
243
+ }
244
+ export interface ColumnDefinition {
245
+ name: string;
246
+ type: ColumnType;
247
+ enum?: LiteralValue[];
248
+ isOptional?: boolean;
249
+ isIndexed?: boolean;
250
+ relation?: ColumnRelation;
251
+ isFullTextSearchable?: boolean;
252
+ isPrimarySearchKey?: boolean;
253
+ notes?: string;
254
+ }
255
+ export interface TableSchema {
256
+ name: string;
257
+ columns: ColumnDefinition[];
258
+ skipMetadataColumns?: boolean;
259
+ polymorphic?: PolymorphicRelation[];
260
+ notes?: string;
261
+ }
262
+ export interface DbSchema {
263
+ version: number;
264
+ tables: TableSchema[];
265
+ }
266
+ export interface DbAdapter {
267
+ getSchemaVersion(): Promise<number>;
268
+ setSchemaVersion(version: number): Promise<void>;
269
+ addTable(table: TableSchema): Promise<void>;
270
+ addColumn(table: string, column: ColumnDefinition): Promise<void>;
271
+ /**
272
+ * Retrieve a single record by primary key `id`.
273
+ * `idOrOptions` support either an ID string or full QueryOptions
274
+ */
275
+ get<T>(table: string, idOrOptions: string | QueryOptions): Promise<T | null>;
276
+ /**
277
+ * Query records with filters, joins, sort, pagination.
278
+ * Result shape depends on `fields` and `joins`.
279
+ */
280
+ query<T>(table: string, options?: QueryOptions): Promise<T[]>;
281
+ /**
282
+ * Insert a single record.
283
+ * Keys must be valid column names; values must be LiteralValue.
284
+ */
285
+ create<T>(table: string, data: Partial<Record<string, LiteralValue>>): Promise<T>;
286
+ /**
287
+ * Update a record by `id`.
288
+ */
289
+ update<T>(table: string, id: string, updates: Partial<Record<string, LiteralValue>>): Promise<T>;
290
+ /**
291
+ * Delete a record by `id`.
292
+ */
293
+ delete(table: string, id: string): Promise<void>;
294
+ bulkCreate<T>(table: string, rows: Array<Partial<Record<string, LiteralValue>>>): Promise<T[]>;
295
+ bulkUpdate<T>(table: string, rows: Array<{
296
+ id: string;
297
+ updates: Partial<Record<string, LiteralValue>>;
298
+ }>): Promise<T[]>;
299
+ /**
300
+ * Jalankan callback dalam satu transaksi SQLite.
301
+ * Jika callback throw error atau return Promise reject → otomatis ROLLBACK.
302
+ * Jika sukses → COMMIT.
303
+ * Mendukung async/await di dalam callback (penting untuk domain logic yang kompleks).
304
+ */
305
+ transaction<T>(callback: (tx: this) => Promise<T>): Promise<T>;
306
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * PlatformAdapter menyediakan abstraksi operasi platform-spesifik.
3
+ * Kontrak ini domain-agnostic.
4
+ */
5
+ export interface PlatformAdapter {
6
+ getEnvVar(key: string): string | undefined;
7
+ isOnline(): boolean;
8
+ hashPin(pin: string): Promise<string>;
9
+ verifyPin(pin: string, hashedPin: string): Promise<boolean>;
10
+ }
11
+ /**
12
+ * Default implementation untuk fase skeleton.
13
+ * Catatan: implementasi ini netral untuk fase skeleton dan dapat dioverride host.
14
+ */
15
+ export declare class DefaultPlatformAdapter implements PlatformAdapter {
16
+ constructor(_context?: unknown);
17
+ getEnvVar(key: string): string | undefined;
18
+ isOnline(): boolean;
19
+ hashPin(pin: string): Promise<string>;
20
+ verifyPin(pin: string, hashedPin: string): Promise<boolean>;
21
+ }
@@ -0,0 +1,5 @@
1
+ export interface StorageAdapter {
2
+ getItem(key: string): Promise<string | null> | string | null;
3
+ setItem(key: string, value: string): Promise<void> | void;
4
+ removeItem(key: string): Promise<void> | void;
5
+ }
@@ -0,0 +1,91 @@
1
+ import type { LoggerAdapter } from '../contracts/LoggerAdapter';
2
+ import type { HttpAdapter } from '../contracts/HttpAdapter';
3
+ import type { HttpRetryPolicy } from '../contracts/HttpRetryPolicy';
4
+ import type { SocketAdapter } from '../contracts/SocketAdapter';
5
+ import type { EncryptionAdapter } from '../contracts/EncryptionAdapter';
6
+ import type { ChecksumAdapter } from '../contracts/ChecksumPrimitives';
7
+ import type { ActivityRecord, ActivitySink } from '../contracts/ActivitySink';
8
+ import type { DbAdapter } from '../ports/DbAdapter';
9
+ import type { PlatformAdapter } from '../ports/PlatformAdapter';
10
+ export type AdapterFactory<TAdapter> = (runtime: CoreRuntime<any, any>) => TAdapter;
11
+ export interface CoreAdapterRegistryOptions {
12
+ platformAdapter?: AdapterFactory<PlatformAdapter>;
13
+ dbAdapter?: AdapterFactory<DbAdapter>;
14
+ loggerAdapter?: AdapterFactory<LoggerAdapter>;
15
+ httpAdapter?: AdapterFactory<HttpAdapter>;
16
+ httpRetryPolicy?: AdapterFactory<HttpRetryPolicy>;
17
+ socketAdapter?: AdapterFactory<SocketAdapter>;
18
+ encryptionAdapter?: AdapterFactory<EncryptionAdapter>;
19
+ checksumAdapter?: AdapterFactory<ChecksumAdapter>;
20
+ activitySink?: AdapterFactory<ActivitySink>;
21
+ extensions?: Record<string, AdapterFactory<unknown>>;
22
+ }
23
+ export declare class CoreAdapterRegistry {
24
+ readonly platformAdapter?: PlatformAdapter;
25
+ readonly dbAdapter?: DbAdapter;
26
+ readonly loggerAdapter?: LoggerAdapter;
27
+ readonly httpAdapter?: HttpAdapter;
28
+ readonly httpRetryPolicy: HttpRetryPolicy;
29
+ readonly socketAdapter?: SocketAdapter;
30
+ readonly encryptionAdapter?: EncryptionAdapter;
31
+ readonly checksumAdapter: ChecksumAdapter;
32
+ readonly activitySink: ActivitySink;
33
+ private readonly extensions;
34
+ constructor(runtime: CoreRuntime<any, any>, options: CoreAdapterRegistryOptions);
35
+ getExtension<TValue>(name: string): TValue | undefined;
36
+ clear(): void;
37
+ }
38
+ export interface CoreRuntimeStartOptions {
39
+ runMigrations?: boolean;
40
+ useSeedData?: boolean;
41
+ }
42
+ export interface CoreRuntimeHooks<TDomainServices, TIntegrationServices> {
43
+ createDomainServices?: (runtime: CoreRuntime<TDomainServices, TIntegrationServices>) => Promise<TDomainServices> | TDomainServices;
44
+ createIntegrationServices?: (runtime: CoreRuntime<TDomainServices, TIntegrationServices>) => Promise<TIntegrationServices> | TIntegrationServices;
45
+ runMigrations?: (runtime: CoreRuntime<TDomainServices, TIntegrationServices>) => Promise<void>;
46
+ runSeed?: (runtime: CoreRuntime<TDomainServices, TIntegrationServices>) => Promise<void>;
47
+ onInit?: (runtime: CoreRuntime<TDomainServices, TIntegrationServices>) => Promise<void>;
48
+ onStop?: (runtime: CoreRuntime<TDomainServices, TIntegrationServices>) => Promise<void>;
49
+ }
50
+ export declare class CoreRuntime<TDomainServices = unknown, TIntegrationServices = unknown> {
51
+ private correlationCounter;
52
+ private started;
53
+ private pluginsLoaded;
54
+ private pendingPlugins;
55
+ private hooks;
56
+ registry?: CoreAdapterRegistry;
57
+ domainServices?: TDomainServices;
58
+ integrationServices?: TIntegrationServices;
59
+ static builder<TDomainServices = unknown, TIntegrationServices = unknown>(): CoreRuntimeBuilder<TDomainServices, TIntegrationServices>;
60
+ setHooks(hooks: Partial<CoreRuntimeHooks<TDomainServices, TIntegrationServices>>): void;
61
+ use(name: string, init: (runtime: CoreRuntime<TDomainServices, TIntegrationServices>) => Promise<void>, deps?: string[]): this;
62
+ init(options?: CoreRuntimeStartOptions): Promise<void>;
63
+ start(options?: CoreRuntimeStartOptions): Promise<void>;
64
+ stop(): Promise<void>;
65
+ reset(): Promise<void>;
66
+ restart(): Promise<void>;
67
+ isStarted(): boolean;
68
+ createCorrelationId(prefix?: string): string;
69
+ logInfo(message: string, context?: Record<string, unknown>): void;
70
+ logWarn(message: string, context?: Record<string, unknown>): void;
71
+ logError(error: unknown, context?: Record<string, unknown>): void;
72
+ emitActivity(record: ActivityRecord): Promise<void>;
73
+ private resolvePluginLevels;
74
+ }
75
+ export declare class CoreRuntimeBuilder<TDomainServices = unknown, TIntegrationServices = unknown> {
76
+ private readonly runtime;
77
+ private adapterOptions;
78
+ private hooks;
79
+ withPlatformAdapter(factory: AdapterFactory<PlatformAdapter>): this;
80
+ withDbAdapter(factory: AdapterFactory<DbAdapter>): this;
81
+ withHttpAdapter(factory: AdapterFactory<HttpAdapter>): this;
82
+ withHttpRetryPolicy(factory: AdapterFactory<HttpRetryPolicy>): this;
83
+ withSocketAdapter(factory: AdapterFactory<SocketAdapter>): this;
84
+ withEncryptionAdapter(factory: AdapterFactory<EncryptionAdapter>): this;
85
+ withChecksumAdapter(factory: AdapterFactory<ChecksumAdapter>): this;
86
+ withActivitySink(factory: AdapterFactory<ActivitySink>): this;
87
+ withLoggerAdapter(factory: AdapterFactory<LoggerAdapter>): this;
88
+ withExtension(name: string, factory: AdapterFactory<unknown>): this;
89
+ withHooks(hooks: Partial<CoreRuntimeHooks<TDomainServices, TIntegrationServices>>): this;
90
+ build(): CoreRuntime<TDomainServices, TIntegrationServices>;
91
+ }
@@ -0,0 +1,11 @@
1
+ export type StoreListener<T> = (state: T, previousState: T) => void;
2
+ export interface ReadonlyStore<T> {
3
+ getState(): T;
4
+ subscribe(listener: StoreListener<T>): () => void;
5
+ }
6
+ export interface Store<T extends object> extends ReadonlyStore<T> {
7
+ setState(nextState: Partial<T> | ((state: T) => T)): T;
8
+ reset(nextState?: T): T;
9
+ }
10
+ export declare function createStore<T extends object>(initialState: T): Store<T>;
11
+ export declare function asReadonlyStore<T>(store: ReadonlyStore<T>): ReadonlyStore<T>;
@@ -0,0 +1,5 @@
1
+ import type { DbAdapter } from '../ports/DbAdapter';
2
+ export interface DbAdapterContractHarnessOptions {
3
+ prefix?: string;
4
+ }
5
+ export declare function runDbAdapterContractHarness(adapter: DbAdapter, options?: DbAdapterContractHarnessOptions): Promise<void>;
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "ofcore",
3
+ "version": "0.1.0-alpha.0",
4
+ "description": "Lightweight runtime core for offline-first TypeScript apps.",
5
+ "author": {
6
+ "name": "Agus Made",
7
+ "email": "krisnaparta@gmail.com"
8
+ },
9
+ "private": false,
10
+ "main": "dist/index.cjs.js",
11
+ "module": "dist/index.esm.js",
12
+ "types": "dist/index.d.ts",
13
+ "exports": {
14
+ ".": {
15
+ "types": "./dist/index.d.ts",
16
+ "import": "./dist/index.esm.js",
17
+ "require": "./dist/index.cjs.js"
18
+ }
19
+ },
20
+ "files": [
21
+ "dist"
22
+ ],
23
+ "scripts": {
24
+ "clean": "rimraf dist",
25
+ "build": "npm run clean && tsc -b && node esbuild.config.js",
26
+ "bundle": "node esbuild.config.js",
27
+ "verify:contract": "node ./scripts/verify-contract-surface.js",
28
+ "verify:surface": "node ./scripts/verify-contract-surface.js",
29
+ "verify:logic": "node ./scripts/verify-logic-behavior.js && node ./scripts/verify-dbadapter-harness.js && node ./scripts/verify-core-runtime.js && node ./scripts/verify-store.js && node ./scripts/verify-adapter-conformance.js && node ./scripts/verify-adapter-default-vs-override.js",
30
+ "verify:adapter-conformance": "node ./scripts/verify-adapter-conformance.js",
31
+ "verify:adapter-default-vs-override": "node ./scripts/verify-adapter-default-vs-override.js",
32
+ "verify:dbadapter-harness": "node ./scripts/verify-dbadapter-harness.js",
33
+ "verify:core-runtime": "node ./scripts/verify-core-runtime.js",
34
+ "verify:store": "node ./scripts/verify-store.js",
35
+ "ci:check": "npm run build && npm run verify:contract && npm run verify:logic",
36
+ "prepublishOnly": "npm run ci:check"
37
+ },
38
+ "publishConfig": {
39
+ "access": "public"
40
+ },
41
+ "devDependencies": {
42
+ "esbuild": "^0.25.11",
43
+ "rimraf": "^6.0.1",
44
+ "typescript": "^5.9.3"
45
+ }
46
+ }