@walkeros/server-destination-sqlite 4.1.3-next-1780071196115 → 4.2.0-next-1780420352914
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/CHANGELOG.md +14 -3
- package/dist/index.d.mts +24 -81
- package/dist/index.d.ts +24 -81
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/walkerOS.json +1 -1
- package/package.json +9 -9
package/CHANGELOG.md
CHANGED
|
@@ -1,14 +1,25 @@
|
|
|
1
1
|
# @walkeros/server-destination-sqlite
|
|
2
2
|
|
|
3
|
-
## 4.
|
|
3
|
+
## 4.2.0-next-1780420352914
|
|
4
4
|
|
|
5
5
|
### Patch Changes
|
|
6
6
|
|
|
7
|
+
- 59aa9e1: Refresh runtime dependencies to their latest majors: Express 5,
|
|
8
|
+
Commander 15, better-sqlite3 12, @libsql/client 0.17, Google Cloud
|
|
9
|
+
functions-framework 5, mixpanel 0.22, and jsdom 29. No public API changes;
|
|
10
|
+
installs now pull the current versions of these SDKs.
|
|
11
|
+
- 776e5f9: Step examples are no longer bundled into production output. They were
|
|
12
|
+
accidentally exported from the production entry of these packages and pulled
|
|
13
|
+
into bundled JS. Examples remain available via the package `./dev` subpath for
|
|
14
|
+
simulation and testing.
|
|
7
15
|
- Updated dependencies [908d6f0]
|
|
16
|
+
- Updated dependencies [f4a9013]
|
|
17
|
+
- Updated dependencies [d65bbde]
|
|
8
18
|
- Updated dependencies [2d64ed2]
|
|
19
|
+
- Updated dependencies [654ba38]
|
|
9
20
|
- Updated dependencies [3eb2467]
|
|
10
|
-
- @walkeros/core@4.
|
|
11
|
-
- @walkeros/server-core@4.
|
|
21
|
+
- @walkeros/core@4.2.0-next-1780420352914
|
|
22
|
+
- @walkeros/server-core@4.2.0-next-1780420352914
|
|
12
23
|
|
|
13
24
|
## 4.1.2
|
|
14
25
|
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Destination as Destination$1, Mapping as Mapping$1, SetupFn as SetupFn$1,
|
|
1
|
+
import { Destination as Destination$1, Mapping as Mapping$1, SetupFn as SetupFn$1, WalkerOS, LifecycleContext } from '@walkeros/core';
|
|
2
2
|
import { DestinationServer } from '@walkeros/server-core';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -122,86 +122,29 @@ type PushEvents = DestinationServer.PushEvents<Mapping>;
|
|
|
122
122
|
type Rule = Mapping$1.Rule<Mapping>;
|
|
123
123
|
type Rules = Mapping$1.Rules<Rule>;
|
|
124
124
|
|
|
125
|
-
type
|
|
126
|
-
type
|
|
127
|
-
type
|
|
128
|
-
type
|
|
129
|
-
type
|
|
130
|
-
type
|
|
131
|
-
type
|
|
132
|
-
type
|
|
133
|
-
type
|
|
134
|
-
type
|
|
135
|
-
type
|
|
136
|
-
type
|
|
137
|
-
type
|
|
138
|
-
type
|
|
139
|
-
type
|
|
140
|
-
type
|
|
141
|
-
type
|
|
142
|
-
type
|
|
143
|
-
type
|
|
144
|
-
type
|
|
145
|
-
type
|
|
146
|
-
declare namespace index$1 {
|
|
147
|
-
export type { index$1_Config as Config, index$1_Destination as Destination, index$1_Env as Env, index$1_InitFn as InitFn, index$1_InitSettings as InitSettings, index$1_Mapping as Mapping, index$1_PartialConfig as PartialConfig, index$1_PushEvents as PushEvents, index$1_PushFn as PushFn, index$1_Rule as Rule, index$1_Rules as Rules, index$1_SchemaMode as SchemaMode, index$1_Settings as Settings, index$1_Setup as Setup, index$1_SetupColumn as SetupColumn, index$1_SetupFn as SetupFn, index$1_SetupIndex as SetupIndex, index$1_SqliteClient as SqliteClient, index$1_SqliteClientFactory as SqliteClientFactory, index$1_SqliteSettings as SqliteSettings, index$1_Types as Types };
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
declare const push: Env;
|
|
151
|
-
/**
|
|
152
|
-
* Simulation tracking paths. Specifies which function calls to record when
|
|
153
|
-
* running step examples through the collector.
|
|
154
|
-
*/
|
|
155
|
-
declare const simulation: string[];
|
|
156
|
-
|
|
157
|
-
declare const env_push: typeof push;
|
|
158
|
-
declare const env_simulation: typeof simulation;
|
|
159
|
-
declare namespace env {
|
|
160
|
-
export { env_push as push, env_simulation as simulation };
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Extended step example that may carry destination-level settings overrides.
|
|
165
|
-
*/
|
|
166
|
-
type SqliteStepExample = Flow.StepExample & {
|
|
167
|
-
settings?: Partial<Settings>;
|
|
168
|
-
};
|
|
169
|
-
/**
|
|
170
|
-
* Canonical insert into the default `events` table. `out` records the
|
|
171
|
-
* column args the prepared INSERT is bound with.
|
|
172
|
-
*/
|
|
173
|
-
declare const defaultInsert: SqliteStepExample;
|
|
174
|
-
/**
|
|
175
|
-
* Custom table name. Verifies table overrides while using the canonical column set.
|
|
176
|
-
*/
|
|
177
|
-
declare const customTable: SqliteStepExample;
|
|
178
|
-
/**
|
|
179
|
-
* Order event with numeric data. Confirms JSON serialization of nested values.
|
|
180
|
-
*/
|
|
181
|
-
declare const orderComplete: SqliteStepExample;
|
|
182
|
-
/**
|
|
183
|
-
* Table override per rule -- routes this event to a dedicated table.
|
|
184
|
-
*/
|
|
185
|
-
declare const tableOverride: SqliteStepExample;
|
|
186
|
-
/**
|
|
187
|
-
* Ignored event -- mapping.ignore: true produces no insert call.
|
|
188
|
-
*/
|
|
189
|
-
declare const ignoredEvent: SqliteStepExample;
|
|
190
|
-
|
|
191
|
-
type step_SqliteStepExample = SqliteStepExample;
|
|
192
|
-
declare const step_customTable: typeof customTable;
|
|
193
|
-
declare const step_defaultInsert: typeof defaultInsert;
|
|
194
|
-
declare const step_ignoredEvent: typeof ignoredEvent;
|
|
195
|
-
declare const step_orderComplete: typeof orderComplete;
|
|
196
|
-
declare const step_tableOverride: typeof tableOverride;
|
|
197
|
-
declare namespace step {
|
|
198
|
-
export { type step_SqliteStepExample as SqliteStepExample, step_customTable as customTable, step_defaultInsert as defaultInsert, step_ignoredEvent as ignoredEvent, step_orderComplete as orderComplete, step_tableOverride as tableOverride };
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
declare const index_env: typeof env;
|
|
202
|
-
declare const index_step: typeof step;
|
|
125
|
+
type index_Config = Config;
|
|
126
|
+
type index_Destination = Destination;
|
|
127
|
+
type index_Env = Env;
|
|
128
|
+
type index_InitFn = InitFn;
|
|
129
|
+
type index_InitSettings = InitSettings;
|
|
130
|
+
type index_Mapping = Mapping;
|
|
131
|
+
type index_PartialConfig = PartialConfig;
|
|
132
|
+
type index_PushEvents = PushEvents;
|
|
133
|
+
type index_PushFn = PushFn;
|
|
134
|
+
type index_Rule = Rule;
|
|
135
|
+
type index_Rules = Rules;
|
|
136
|
+
type index_SchemaMode = SchemaMode;
|
|
137
|
+
type index_Settings = Settings;
|
|
138
|
+
type index_Setup = Setup;
|
|
139
|
+
type index_SetupColumn = SetupColumn;
|
|
140
|
+
type index_SetupFn = SetupFn;
|
|
141
|
+
type index_SetupIndex = SetupIndex;
|
|
142
|
+
type index_SqliteClient = SqliteClient;
|
|
143
|
+
type index_SqliteClientFactory = SqliteClientFactory;
|
|
144
|
+
type index_SqliteSettings = SqliteSettings;
|
|
145
|
+
type index_Types = Types;
|
|
203
146
|
declare namespace index {
|
|
204
|
-
export {
|
|
147
|
+
export type { index_Config as Config, index_Destination as Destination, index_Env as Env, index_InitFn as InitFn, index_InitSettings as InitSettings, index_Mapping as Mapping, index_PartialConfig as PartialConfig, index_PushEvents as PushEvents, index_PushFn as PushFn, index_Rule as Rule, index_Rules as Rules, index_SchemaMode as SchemaMode, index_Settings as Settings, index_Setup as Setup, index_SetupColumn as SetupColumn, index_SetupFn as SetupFn, index_SetupIndex as SetupIndex, index_SqliteClient as SqliteClient, index_SqliteClientFactory as SqliteClientFactory, index_SqliteSettings as SqliteSettings, index_Types as Types };
|
|
205
148
|
}
|
|
206
149
|
|
|
207
150
|
/**
|
|
@@ -236,4 +179,4 @@ declare const setup: (ctx: LifecycleContext<WideConfig, Env>) => Promise<SetupRe
|
|
|
236
179
|
|
|
237
180
|
declare const destinationSQLite: Destination;
|
|
238
181
|
|
|
239
|
-
export { CANONICAL_COLUMNS, DEFAULT_PRAGMAS, DEFAULT_SCHEMA, DEFAULT_SETUP, index
|
|
182
|
+
export { CANONICAL_COLUMNS, DEFAULT_PRAGMAS, DEFAULT_SCHEMA, DEFAULT_SETUP, index as DestinationSQLite, buildCreateTableSql, buildInsertSql, destinationSQLite as default, destinationSQLite, eventToRow, isLibsqlUrl, setup };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Destination as Destination$1, Mapping as Mapping$1, SetupFn as SetupFn$1,
|
|
1
|
+
import { Destination as Destination$1, Mapping as Mapping$1, SetupFn as SetupFn$1, WalkerOS, LifecycleContext } from '@walkeros/core';
|
|
2
2
|
import { DestinationServer } from '@walkeros/server-core';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -122,86 +122,29 @@ type PushEvents = DestinationServer.PushEvents<Mapping>;
|
|
|
122
122
|
type Rule = Mapping$1.Rule<Mapping>;
|
|
123
123
|
type Rules = Mapping$1.Rules<Rule>;
|
|
124
124
|
|
|
125
|
-
type
|
|
126
|
-
type
|
|
127
|
-
type
|
|
128
|
-
type
|
|
129
|
-
type
|
|
130
|
-
type
|
|
131
|
-
type
|
|
132
|
-
type
|
|
133
|
-
type
|
|
134
|
-
type
|
|
135
|
-
type
|
|
136
|
-
type
|
|
137
|
-
type
|
|
138
|
-
type
|
|
139
|
-
type
|
|
140
|
-
type
|
|
141
|
-
type
|
|
142
|
-
type
|
|
143
|
-
type
|
|
144
|
-
type
|
|
145
|
-
type
|
|
146
|
-
declare namespace index$1 {
|
|
147
|
-
export type { index$1_Config as Config, index$1_Destination as Destination, index$1_Env as Env, index$1_InitFn as InitFn, index$1_InitSettings as InitSettings, index$1_Mapping as Mapping, index$1_PartialConfig as PartialConfig, index$1_PushEvents as PushEvents, index$1_PushFn as PushFn, index$1_Rule as Rule, index$1_Rules as Rules, index$1_SchemaMode as SchemaMode, index$1_Settings as Settings, index$1_Setup as Setup, index$1_SetupColumn as SetupColumn, index$1_SetupFn as SetupFn, index$1_SetupIndex as SetupIndex, index$1_SqliteClient as SqliteClient, index$1_SqliteClientFactory as SqliteClientFactory, index$1_SqliteSettings as SqliteSettings, index$1_Types as Types };
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
declare const push: Env;
|
|
151
|
-
/**
|
|
152
|
-
* Simulation tracking paths. Specifies which function calls to record when
|
|
153
|
-
* running step examples through the collector.
|
|
154
|
-
*/
|
|
155
|
-
declare const simulation: string[];
|
|
156
|
-
|
|
157
|
-
declare const env_push: typeof push;
|
|
158
|
-
declare const env_simulation: typeof simulation;
|
|
159
|
-
declare namespace env {
|
|
160
|
-
export { env_push as push, env_simulation as simulation };
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Extended step example that may carry destination-level settings overrides.
|
|
165
|
-
*/
|
|
166
|
-
type SqliteStepExample = Flow.StepExample & {
|
|
167
|
-
settings?: Partial<Settings>;
|
|
168
|
-
};
|
|
169
|
-
/**
|
|
170
|
-
* Canonical insert into the default `events` table. `out` records the
|
|
171
|
-
* column args the prepared INSERT is bound with.
|
|
172
|
-
*/
|
|
173
|
-
declare const defaultInsert: SqliteStepExample;
|
|
174
|
-
/**
|
|
175
|
-
* Custom table name. Verifies table overrides while using the canonical column set.
|
|
176
|
-
*/
|
|
177
|
-
declare const customTable: SqliteStepExample;
|
|
178
|
-
/**
|
|
179
|
-
* Order event with numeric data. Confirms JSON serialization of nested values.
|
|
180
|
-
*/
|
|
181
|
-
declare const orderComplete: SqliteStepExample;
|
|
182
|
-
/**
|
|
183
|
-
* Table override per rule -- routes this event to a dedicated table.
|
|
184
|
-
*/
|
|
185
|
-
declare const tableOverride: SqliteStepExample;
|
|
186
|
-
/**
|
|
187
|
-
* Ignored event -- mapping.ignore: true produces no insert call.
|
|
188
|
-
*/
|
|
189
|
-
declare const ignoredEvent: SqliteStepExample;
|
|
190
|
-
|
|
191
|
-
type step_SqliteStepExample = SqliteStepExample;
|
|
192
|
-
declare const step_customTable: typeof customTable;
|
|
193
|
-
declare const step_defaultInsert: typeof defaultInsert;
|
|
194
|
-
declare const step_ignoredEvent: typeof ignoredEvent;
|
|
195
|
-
declare const step_orderComplete: typeof orderComplete;
|
|
196
|
-
declare const step_tableOverride: typeof tableOverride;
|
|
197
|
-
declare namespace step {
|
|
198
|
-
export { type step_SqliteStepExample as SqliteStepExample, step_customTable as customTable, step_defaultInsert as defaultInsert, step_ignoredEvent as ignoredEvent, step_orderComplete as orderComplete, step_tableOverride as tableOverride };
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
declare const index_env: typeof env;
|
|
202
|
-
declare const index_step: typeof step;
|
|
125
|
+
type index_Config = Config;
|
|
126
|
+
type index_Destination = Destination;
|
|
127
|
+
type index_Env = Env;
|
|
128
|
+
type index_InitFn = InitFn;
|
|
129
|
+
type index_InitSettings = InitSettings;
|
|
130
|
+
type index_Mapping = Mapping;
|
|
131
|
+
type index_PartialConfig = PartialConfig;
|
|
132
|
+
type index_PushEvents = PushEvents;
|
|
133
|
+
type index_PushFn = PushFn;
|
|
134
|
+
type index_Rule = Rule;
|
|
135
|
+
type index_Rules = Rules;
|
|
136
|
+
type index_SchemaMode = SchemaMode;
|
|
137
|
+
type index_Settings = Settings;
|
|
138
|
+
type index_Setup = Setup;
|
|
139
|
+
type index_SetupColumn = SetupColumn;
|
|
140
|
+
type index_SetupFn = SetupFn;
|
|
141
|
+
type index_SetupIndex = SetupIndex;
|
|
142
|
+
type index_SqliteClient = SqliteClient;
|
|
143
|
+
type index_SqliteClientFactory = SqliteClientFactory;
|
|
144
|
+
type index_SqliteSettings = SqliteSettings;
|
|
145
|
+
type index_Types = Types;
|
|
203
146
|
declare namespace index {
|
|
204
|
-
export {
|
|
147
|
+
export type { index_Config as Config, index_Destination as Destination, index_Env as Env, index_InitFn as InitFn, index_InitSettings as InitSettings, index_Mapping as Mapping, index_PartialConfig as PartialConfig, index_PushEvents as PushEvents, index_PushFn as PushFn, index_Rule as Rule, index_Rules as Rules, index_SchemaMode as SchemaMode, index_Settings as Settings, index_Setup as Setup, index_SetupColumn as SetupColumn, index_SetupFn as SetupFn, index_SetupIndex as SetupIndex, index_SqliteClient as SqliteClient, index_SqliteClientFactory as SqliteClientFactory, index_SqliteSettings as SqliteSettings, index_Types as Types };
|
|
205
148
|
}
|
|
206
149
|
|
|
207
150
|
/**
|
|
@@ -236,4 +179,4 @@ declare const setup: (ctx: LifecycleContext<WideConfig, Env>) => Promise<SetupRe
|
|
|
236
179
|
|
|
237
180
|
declare const destinationSQLite: Destination;
|
|
238
181
|
|
|
239
|
-
export { CANONICAL_COLUMNS, DEFAULT_PRAGMAS, DEFAULT_SCHEMA, DEFAULT_SETUP, index
|
|
182
|
+
export { CANONICAL_COLUMNS, DEFAULT_PRAGMAS, DEFAULT_SCHEMA, DEFAULT_SETUP, index as DestinationSQLite, buildCreateTableSql, buildInsertSql, destinationSQLite as default, destinationSQLite, eventToRow, isLibsqlUrl, setup };
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var mod,__defProp=Object.defineProperty,__getOwnPropDesc=Object.getOwnPropertyDescriptor,__getOwnPropNames=Object.getOwnPropertyNames,__hasOwnProp=Object.prototype.hasOwnProperty,__export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})},index_exports={};__export(index_exports,{CANONICAL_COLUMNS:()=>CANONICAL_COLUMNS,DEFAULT_PRAGMAS:()=>DEFAULT_PRAGMAS,DEFAULT_SCHEMA:()=>DEFAULT_SCHEMA,DEFAULT_SETUP:()=>DEFAULT_SETUP,DestinationSQLite:()=>types_exports,buildCreateTableSql:()=>buildCreateTableSql,buildInsertSql:()=>buildInsertSql,default:()=>index_default,destinationSQLite:()=>destinationSQLite,eventToRow:()=>eventToRow,examples:()=>examples_exports,isLibsqlUrl:()=>isLibsqlUrl,setup:()=>setup}),module.exports=(mod=index_exports,((to,from,except,desc)=>{if(from&&"object"==typeof from||"function"==typeof from)for(let key of __getOwnPropNames(from))__hasOwnProp.call(to,key)||key===except||__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to})(__defProp({},"__esModule",{value:!0}),mod));var import_core=require("@walkeros/core");function isSqliteEnv(env){if(!(0,import_core.isObject)(env))return!1;const maybe=env;return"function"==typeof maybe.SqliteDriver||(0,import_core.isObject)(maybe.client??null)}var LIBSQL_URL_PREFIXES=["libsql://","http://","https://","ws://","wss://"];function isLibsqlUrl(url){return LIBSQL_URL_PREFIXES.some(p=>url.startsWith(p))}var defaultFactory=async(url,authToken)=>isLibsqlUrl(url)?async function(url,authToken){let createClient;try{const loaded=require("@libsql/client"),fn=loaded.createClient??loaded.default?.createClient;if("function"!=typeof fn)throw new Error("createClient export not found on @libsql/client");createClient=fn}catch(err){throw new Error(`@walkeros/server-destination-sqlite: @libsql/client is not installed. Install it with \`npm install @libsql/client\`, or use a local file URL. Original error: ${String(err)}`)}const client=createClient({url:url,authToken:authToken});return{async execute(sql,args=[]){await client.execute({sql:sql,args:args})},prepare:sql=>async args=>{await client.execute({sql:sql,args:args})},async query(sql,args=[]){const result=await client.execute({sql:sql,args:args});return result?.rows??[]},async close(){client.close()}}}(url,authToken):async function(filePath){let Database;try{const loaded=require("better-sqlite3");Database="default"in loaded&&"function"==typeof loaded.default?loaded.default:loaded}catch(err){throw new Error(`@walkeros/server-destination-sqlite: better-sqlite3 is not installed. Install it with \`npm install better-sqlite3\`, or switch settings.sqlite.url to a libSQL URL. Original error: ${String(err)}`)}const db=new Database(filePath);return{async execute(sql,args=[]){db.prepare(sql).run(...args)},prepare(sql){const stmt=db.prepare(sql);return async args=>{stmt.run(...args)}},query:async(sql,args=[])=>db.prepare(sql).all(...args),async close(){db.close()}}}(url);async function createClientFromSettings(url,authToken){return defaultFactory(url,authToken)}var import_core2=require("@walkeros/core"),CANONICAL_COLUMNS=["timestamp","event_id","name","entity","action","session_id","user_id","page_url","page_title","referrer_url","data","globals","consent"];function buildCreateTableSql(table){return`CREATE TABLE IF NOT EXISTS ${table} (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n timestamp INTEGER,\n event_id TEXT,\n name TEXT,\n entity TEXT,\n action TEXT,\n session_id TEXT,\n user_id TEXT,\n page_url TEXT,\n page_title TEXT,\n referrer_url TEXT,\n data TEXT,\n globals TEXT,\n consent TEXT\n)`}function buildInsertSql(table){return`INSERT INTO ${table} (${CANONICAL_COLUMNS.join(", ")}) VALUES (${CANONICAL_COLUMNS.map(()=>"?").join(", ")})`}function eventToRow(event){const data=(0,import_core2.isObject)(event.data)?event.data:{},title=(0,import_core2.isString)(data.title)?data.title:"";return["number"==typeof event.timestamp?event.timestamp:Date.now(),event.id??"",event.name??"",event.entity??"",event.action??"",event.user?.session??"",event.user?.id??"",event.source?.url??"",title,event.source?.referrer??"",JSON.stringify(data),JSON.stringify(event.globals??{}),JSON.stringify(event.consent??{})]}var import_core3=require("@walkeros/core"),import_core4=require("@walkeros/core"),DEFAULT_SCHEMA=[{name:"name",type:"TEXT",notNull:!0},{name:"data",type:"TEXT"},{name:"context",type:"TEXT"},{name:"globals",type:"TEXT"},{name:"custom",type:"TEXT"},{name:"user",type:"TEXT"},{name:"nested",type:"TEXT"},{name:"consent",type:"TEXT"},{name:"id",type:"TEXT"},{name:"trigger",type:"TEXT"},{name:"entity",type:"TEXT"},{name:"action",type:"TEXT"},{name:"timestamp",type:"TEXT"},{name:"timing",type:"INTEGER"},{name:"source",type:"TEXT"}],DEFAULT_PRAGMAS={journal_mode:"WAL",synchronous:"NORMAL",foreign_keys:"ON",temp_store:"MEMORY"},DEFAULT_SETUP={pragmas:DEFAULT_PRAGMAS,schema:DEFAULT_SCHEMA,indexes:[]};function isString3(value){return"string"==typeof value}function isPragmaTableInfoRow(value){if("object"!=typeof value||null===value)return!1;const obj={...value};return isString3(obj.name)&&isString3(obj.type)}function isSqliteMasterRow(value){if("object"!=typeof value||null===value)return!1;return isString3({...value}.name)}async function tableExists(client,table){const rows=await client.query("SELECT name FROM sqlite_master WHERE type='table' AND name=?",[table]);for(const row of rows)if(isSqliteMasterRow(row)&&row.name===table)return!0;return!1}var setup=async ctx=>{const{config:config,env:env,logger:logger}=ctx,merged=(0,import_core4.resolveSetup)(config.setup,DEFAULT_SETUP);if(!merged)return void logger.debug("setup: skipped (config.setup is false or unset)");const options={pragmas:{...DEFAULT_PRAGMAS,...merged.pragmas??{}},schema:merged.schema??DEFAULT_SCHEMA,indexes:merged.indexes??[]},sqlite=config.settings?.sqlite;if(!sqlite||!sqlite.url)return void logger.throw("setup: settings.sqlite.url is missing");const table=sqlite.table??"events";let client,owned=!0;isSqliteEnv(env)&&env.client?(client=env.client,owned=!1):client=isSqliteEnv(env)&&env.SqliteDriver?await env.SqliteDriver(sqlite.url,sqlite.authToken):await createClientFromSettings(sqlite.url,sqlite.authToken);try{const pragmasApplied=[];for(const[key,value]of Object.entries(options.pragmas))await client.execute(`PRAGMA ${key} = ${value}`),pragmasApplied.push(key);const tableExisted=await tableExists(client,table);await client.execute(function(table,columns){return`CREATE TABLE IF NOT EXISTS ${table} (\n ${columns.map(c=>{const parts=[c.name,c.type];return c.primaryKey&&parts.push("PRIMARY KEY"),c.notNull&&parts.push("NOT NULL"),parts.join(" ")}).join(",\n ")}\n)`}(table,options.schema)),tableExisted&&await async function(client,table,declared,logger){const rows=await client.query(`PRAGMA table_info(${table})`),actualByName=new Map;for(const row of rows)isPragmaTableInfoRow(row)&&actualByName.set(row.name,row.type.toUpperCase());for(const declaredCol of declared){const actualType=actualByName.get(declaredCol.name);void 0!==actualType?actualType!==declaredCol.type.toUpperCase()&&logger.warn("setup.drift",{field:declaredCol.name,declared:declaredCol.type,actual:actualType}):logger.warn("setup.drift",{field:declaredCol.name,declared:declaredCol.type,actual:"missing"})}}(client,table,options.schema,logger);const indexesCreated=[];for(const idx of options.indexes){const unique=idx.unique?"UNIQUE ":"",cols=idx.columns.join(", ");await client.execute(`CREATE ${unique}INDEX IF NOT EXISTS ${idx.name} ON ${table}(${cols})`),indexesCreated.push(idx.name)}return{tableCreated:!tableExisted,pragmasApplied:pragmasApplied,indexesCreated:indexesCreated}}finally{if(owned)try{await client.close()}catch{}}},types_exports={},examples_exports={};__export(examples_exports,{env:()=>env_exports,step:()=>step_exports});var env_exports={};__export(env_exports,{push:()=>push2,simulation:()=>simulation});var mockClient={execute:()=>Promise.resolve(),prepare:()=>()=>Promise.resolve(),query:()=>Promise.resolve([]),close:()=>Promise.resolve()},push2={SqliteDriver:()=>Promise.resolve(mockClient)},simulation=["call:client.prepare","call:client.execute"],step_exports={};__export(step_exports,{customTable:()=>customTable,defaultInsert:()=>defaultInsert,ignoredEvent:()=>ignoredEvent,orderComplete:()=>orderComplete,tableOverride:()=>tableOverride});var import_core5=require("@walkeros/core"),defaultInsert={title:"Default insert",description:"A walker event is inserted into the default events table with canonical columns and JSON-encoded sections.",in:(0,import_core5.getEvent)("page view",{timestamp:1700000100,id:"evt-1",user:{session:"sess-1",id:"user-42"},data:{title:"Home"},source:{type:"browser",platform:"web",url:"https://example.com/",referrer:"https://example.com/prev"},globals:{env:"prod"},consent:{analytics:!0}}),out:[["client.runInsert",[1700000100,"evt-1","page view","page","view","sess-1","user-42","https://example.com/","Home","https://example.com/prev",JSON.stringify({title:"Home"}),JSON.stringify({env:"prod"}),JSON.stringify({analytics:!0})]]]},customTable={title:"Custom table",description:"A destination-level table setting inserts events into a custom SQLite table with the same column layout.",in:(0,import_core5.getEvent)("form submit",{timestamp:1700000101,id:"evt-2",user:{session:"sess-99",id:""},data:{type:"contact"},source:{type:"browser",platform:"web",url:"https://example.com/contact"},globals:{},consent:{}}),settings:{sqlite:{url:":memory:",table:"siteEvents"}},out:[["client.runInsert",[1700000101,"evt-2","form submit","form","submit","sess-99","","https://example.com/contact","","",JSON.stringify({type:"contact"}),JSON.stringify({}),JSON.stringify({})]]]},orderComplete={title:"Order insert",description:"An order complete is inserted with numeric data serialized as JSON in the data column.",in:(0,import_core5.getEvent)("order complete",{timestamp:1700000102,id:"evt-3",user:{session:"",id:""},data:{id:"ORD-1",total:99},source:{type:"collector",schema:"4"},globals:{},consent:{}}),out:[["client.runInsert",[1700000102,"evt-3","order complete","order","complete","","","","","",JSON.stringify({id:"ORD-1",total:99}),JSON.stringify({}),JSON.stringify({})]]]},tableOverride={title:"Table override",description:"A mapping rule overrides the target table so specific events are inserted into a dedicated SQLite table.",in:(0,import_core5.getEvent)("order complete",{timestamp:1700000103,id:"evt-4",user:{session:"",id:""},data:{id:"ORD-2",total:42},source:{type:"collector",schema:"4"},globals:{},consent:{}}),mapping:{settings:{table:"orders"}},out:[["client.runInsert",[1700000103,"evt-4","order complete","order","complete","","","","","",JSON.stringify({id:"ORD-2",total:42}),JSON.stringify({}),JSON.stringify({})]]]},ignoredEvent={public:!1,in:(0,import_core5.getEvent)("debug noise",{timestamp:1700000104,id:"evt-5",source:{type:"collector",schema:"4"}}),mapping:{ignore:!0},out:[]},destinationSQLite={type:"sqlite",config:{},setup:setup,async init({config:partialConfig,logger:logger,env:env,id:id}){const config=function(partialConfig={},logger){const sqlite=(partialConfig.settings??{}).sqlite??{};sqlite.url||logger.throw("Config settings sqlite.url missing");const sqliteSettings={...sqlite,url:sqlite.url,table:sqlite.table??"events"};void 0!==sqlite.schema?(logger.warn('config.settings.sqlite.schema is deprecated; use config.setup instead. "schema: auto" maps to "setup: true" (run "walkeros setup destination.<id>"); "schema: manual" maps to "setup: false". This field is removed in the next major.'),"manual"!==sqlite.schema||partialConfig.mapping||logger.throw('Config settings sqlite.schema="manual" requires a mapping (no default column mapping will be used).'),sqliteSettings._legacyAutoCreate="auto"===sqlite.schema,sqliteSettings._legacySkipProbe="manual"===sqlite.schema):(sqliteSettings._legacyAutoCreate=!1,sqliteSettings._legacySkipProbe=!1),delete sqliteSettings.schema;const settings={sqlite:sqliteSettings};return{...partialConfig,settings:settings}}(partialConfig,logger),sqlite=config.settings.sqlite;if(sqlite._client)return config;let client;if(isSqliteEnv(env)){const envTyped=env;envTyped.client?client=envTyped.client:envTyped.SqliteDriver&&(client=await envTyped.SqliteDriver(sqlite.url,sqlite.authToken))}if(!client)try{client=await createClientFromSettings(sqlite.url,sqlite.authToken)}catch(err){return logger.throw(`@walkeros/server-destination-sqlite: driver load failed: ${String(err)}`),config}if(sqlite._legacyAutoCreate)try{await client.execute(buildCreateTableSql(sqlite.table??"events"))}catch(err){return logger.throw(`@walkeros/server-destination-sqlite: CREATE TABLE failed: ${String(err)}`),config}else if(!sqlite._legacySkipProbe){let exists=!1;try{exists=await tableExists(client,sqlite.table??"events")}catch(err){return logger.throw(`@walkeros/server-destination-sqlite: table probe failed: ${String(err)}`),config}if(!exists)return logger.throw(`SQLite table "${sqlite.table??"events"}" not found in ${sqlite.url}. Run "walkeros setup destination.${id}" to create it.`),config}return sqlite._client=client,sqlite._runInsert=client.prepare(buildInsertSql(sqlite.table??"events")),sqlite._ownedClient=!(isSqliteEnv(env)&&env.client),config},push:async(event,context)=>await async function(event,{config:config,rule:rule,logger:logger}){const settings=config.settings,sqlite=settings?.sqlite;if(!sqlite)return void logger.warn("SQLite settings missing");const client=sqlite._client;if(!client)return void logger.warn("SQLite client not initialized");const ruleSettings=rule?.settings??{},ruleTable=(0,import_core3.isString)(ruleSettings.table)?ruleSettings.table:"",effectiveTable=ruleTable||sqlite.table||"events",runInsert=ruleTable&&ruleTable!==sqlite.table?client.prepare(buildInsertSql(effectiveTable)):sqlite._runInsert;if(!runInsert)return void logger.warn("SQLite insert statement not prepared");const row=eventToRow(event);logger.debug("SQLite INSERT",{table:effectiveTable,eventName:event.name});try{await runInsert(row),logger.debug("SQLite INSERT complete",{table:effectiveTable})}catch(error){logger.error("SQLite INSERT failed",{table:effectiveTable,error:error instanceof Error?error.message:String(error)})}}(event,context),async destroy({config:config}){const settings=config?.settings,sqlite=settings?.sqlite;if(!sqlite)return;const client=sqlite._client;if(client&&sqlite._ownedClient)try{await client.close()}finally{sqlite._client=void 0,sqlite._runInsert=void 0,sqlite._ownedClient=void 0}}},index_default=destinationSQLite;//# sourceMappingURL=index.js.map
|
|
1
|
+
"use strict";var mod,__defProp=Object.defineProperty,__getOwnPropDesc=Object.getOwnPropertyDescriptor,__getOwnPropNames=Object.getOwnPropertyNames,__hasOwnProp=Object.prototype.hasOwnProperty,index_exports={};((target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})})(index_exports,{CANONICAL_COLUMNS:()=>CANONICAL_COLUMNS,DEFAULT_PRAGMAS:()=>DEFAULT_PRAGMAS,DEFAULT_SCHEMA:()=>DEFAULT_SCHEMA,DEFAULT_SETUP:()=>DEFAULT_SETUP,DestinationSQLite:()=>types_exports,buildCreateTableSql:()=>buildCreateTableSql,buildInsertSql:()=>buildInsertSql,default:()=>index_default,destinationSQLite:()=>destinationSQLite,eventToRow:()=>eventToRow,isLibsqlUrl:()=>isLibsqlUrl,setup:()=>setup}),module.exports=(mod=index_exports,((to,from,except,desc)=>{if(from&&"object"==typeof from||"function"==typeof from)for(let key of __getOwnPropNames(from))__hasOwnProp.call(to,key)||key===except||__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to})(__defProp({},"__esModule",{value:!0}),mod));var import_core=require("@walkeros/core");function isSqliteEnv(env){if(!(0,import_core.isObject)(env))return!1;const maybe=env;return"function"==typeof maybe.SqliteDriver||(0,import_core.isObject)(maybe.client??null)}var LIBSQL_URL_PREFIXES=["libsql://","http://","https://","ws://","wss://"];function isLibsqlUrl(url){return LIBSQL_URL_PREFIXES.some(p=>url.startsWith(p))}var defaultFactory=async(url,authToken)=>isLibsqlUrl(url)?async function(url,authToken){let createClient;try{const loaded=require("@libsql/client"),fn=loaded.createClient??loaded.default?.createClient;if("function"!=typeof fn)throw new Error("createClient export not found on @libsql/client");createClient=fn}catch(err){throw new Error(`@walkeros/server-destination-sqlite: @libsql/client is not installed. Install it with \`npm install @libsql/client\`, or use a local file URL. Original error: ${String(err)}`)}const client=createClient({url:url,authToken:authToken});return{async execute(sql,args=[]){await client.execute({sql:sql,args:args})},prepare:sql=>async args=>{await client.execute({sql:sql,args:args})},async query(sql,args=[]){const result=await client.execute({sql:sql,args:args});return result?.rows??[]},async close(){client.close()}}}(url,authToken):async function(filePath){let Database;try{const loaded=require("better-sqlite3");Database="default"in loaded&&"function"==typeof loaded.default?loaded.default:loaded}catch(err){throw new Error(`@walkeros/server-destination-sqlite: better-sqlite3 is not installed. Install it with \`npm install better-sqlite3\`, or switch settings.sqlite.url to a libSQL URL. Original error: ${String(err)}`)}const db=new Database(filePath);return{async execute(sql,args=[]){db.prepare(sql).run(...args)},prepare(sql){const stmt=db.prepare(sql);return async args=>{stmt.run(...args)}},query:async(sql,args=[])=>db.prepare(sql).all(...args),async close(){db.close()}}}(url);async function createClientFromSettings(url,authToken){return defaultFactory(url,authToken)}var import_core2=require("@walkeros/core"),CANONICAL_COLUMNS=["timestamp","event_id","name","entity","action","session_id","user_id","page_url","page_title","referrer_url","data","globals","consent"];function buildCreateTableSql(table){return`CREATE TABLE IF NOT EXISTS ${table} (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n timestamp INTEGER,\n event_id TEXT,\n name TEXT,\n entity TEXT,\n action TEXT,\n session_id TEXT,\n user_id TEXT,\n page_url TEXT,\n page_title TEXT,\n referrer_url TEXT,\n data TEXT,\n globals TEXT,\n consent TEXT\n)`}function buildInsertSql(table){return`INSERT INTO ${table} (${CANONICAL_COLUMNS.join(", ")}) VALUES (${CANONICAL_COLUMNS.map(()=>"?").join(", ")})`}function eventToRow(event){const data=(0,import_core2.isObject)(event.data)?event.data:{},title=(0,import_core2.isString)(data.title)?data.title:"";return["number"==typeof event.timestamp?event.timestamp:Date.now(),event.id??"",event.name??"",event.entity??"",event.action??"",event.user?.session??"",event.user?.id??"",event.source?.url??"",title,event.source?.referrer??"",JSON.stringify(data),JSON.stringify(event.globals??{}),JSON.stringify(event.consent??{})]}var import_core3=require("@walkeros/core"),import_core4=require("@walkeros/core"),DEFAULT_SCHEMA=[{name:"name",type:"TEXT",notNull:!0},{name:"data",type:"TEXT"},{name:"context",type:"TEXT"},{name:"globals",type:"TEXT"},{name:"custom",type:"TEXT"},{name:"user",type:"TEXT"},{name:"nested",type:"TEXT"},{name:"consent",type:"TEXT"},{name:"id",type:"TEXT"},{name:"trigger",type:"TEXT"},{name:"entity",type:"TEXT"},{name:"action",type:"TEXT"},{name:"timestamp",type:"TEXT"},{name:"timing",type:"INTEGER"},{name:"source",type:"TEXT"}],DEFAULT_PRAGMAS={journal_mode:"WAL",synchronous:"NORMAL",foreign_keys:"ON",temp_store:"MEMORY"},DEFAULT_SETUP={pragmas:DEFAULT_PRAGMAS,schema:DEFAULT_SCHEMA,indexes:[]};function isString3(value){return"string"==typeof value}function isPragmaTableInfoRow(value){if("object"!=typeof value||null===value)return!1;const obj={...value};return isString3(obj.name)&&isString3(obj.type)}function isSqliteMasterRow(value){if("object"!=typeof value||null===value)return!1;return isString3({...value}.name)}async function tableExists(client,table){const rows=await client.query("SELECT name FROM sqlite_master WHERE type='table' AND name=?",[table]);for(const row of rows)if(isSqliteMasterRow(row)&&row.name===table)return!0;return!1}var setup=async ctx=>{const{config:config,env:env,logger:logger}=ctx,merged=(0,import_core4.resolveSetup)(config.setup,DEFAULT_SETUP);if(!merged)return void logger.debug("setup: skipped (config.setup is false or unset)");const options={pragmas:{...DEFAULT_PRAGMAS,...merged.pragmas??{}},schema:merged.schema??DEFAULT_SCHEMA,indexes:merged.indexes??[]},sqlite=config.settings?.sqlite;if(!sqlite||!sqlite.url)return void logger.throw("setup: settings.sqlite.url is missing");const table=sqlite.table??"events";let client,owned=!0;isSqliteEnv(env)&&env.client?(client=env.client,owned=!1):client=isSqliteEnv(env)&&env.SqliteDriver?await env.SqliteDriver(sqlite.url,sqlite.authToken):await createClientFromSettings(sqlite.url,sqlite.authToken);try{const pragmasApplied=[];for(const[key,value]of Object.entries(options.pragmas))await client.execute(`PRAGMA ${key} = ${value}`),pragmasApplied.push(key);const tableExisted=await tableExists(client,table);await client.execute(function(table,columns){return`CREATE TABLE IF NOT EXISTS ${table} (\n ${columns.map(c=>{const parts=[c.name,c.type];return c.primaryKey&&parts.push("PRIMARY KEY"),c.notNull&&parts.push("NOT NULL"),parts.join(" ")}).join(",\n ")}\n)`}(table,options.schema)),tableExisted&&await async function(client,table,declared,logger){const rows=await client.query(`PRAGMA table_info(${table})`),actualByName=new Map;for(const row of rows)isPragmaTableInfoRow(row)&&actualByName.set(row.name,row.type.toUpperCase());for(const declaredCol of declared){const actualType=actualByName.get(declaredCol.name);void 0!==actualType?actualType!==declaredCol.type.toUpperCase()&&logger.warn("setup.drift",{field:declaredCol.name,declared:declaredCol.type,actual:actualType}):logger.warn("setup.drift",{field:declaredCol.name,declared:declaredCol.type,actual:"missing"})}}(client,table,options.schema,logger);const indexesCreated=[];for(const idx of options.indexes){const unique=idx.unique?"UNIQUE ":"",cols=idx.columns.join(", ");await client.execute(`CREATE ${unique}INDEX IF NOT EXISTS ${idx.name} ON ${table}(${cols})`),indexesCreated.push(idx.name)}return{tableCreated:!tableExisted,pragmasApplied:pragmasApplied,indexesCreated:indexesCreated}}finally{if(owned)try{await client.close()}catch{}}},types_exports={},destinationSQLite={type:"sqlite",config:{},setup:setup,async init({config:partialConfig,logger:logger,env:env,id:id}){const config=function(partialConfig={},logger){const sqlite=(partialConfig.settings??{}).sqlite??{};sqlite.url||logger.throw("Config settings sqlite.url missing");const sqliteSettings={...sqlite,url:sqlite.url,table:sqlite.table??"events"};void 0!==sqlite.schema?(logger.warn('config.settings.sqlite.schema is deprecated; use config.setup instead. "schema: auto" maps to "setup: true" (run "walkeros setup destination.<id>"); "schema: manual" maps to "setup: false". This field is removed in the next major.'),"manual"!==sqlite.schema||partialConfig.mapping||logger.throw('Config settings sqlite.schema="manual" requires a mapping (no default column mapping will be used).'),sqliteSettings._legacyAutoCreate="auto"===sqlite.schema,sqliteSettings._legacySkipProbe="manual"===sqlite.schema):(sqliteSettings._legacyAutoCreate=!1,sqliteSettings._legacySkipProbe=!1),delete sqliteSettings.schema;const settings={sqlite:sqliteSettings};return{...partialConfig,settings:settings}}(partialConfig,logger),sqlite=config.settings.sqlite;if(sqlite._client)return config;let client;if(isSqliteEnv(env)){const envTyped=env;envTyped.client?client=envTyped.client:envTyped.SqliteDriver&&(client=await envTyped.SqliteDriver(sqlite.url,sqlite.authToken))}if(!client)try{client=await createClientFromSettings(sqlite.url,sqlite.authToken)}catch(err){return logger.throw(`@walkeros/server-destination-sqlite: driver load failed: ${String(err)}`),config}if(sqlite._legacyAutoCreate)try{await client.execute(buildCreateTableSql(sqlite.table??"events"))}catch(err){return logger.throw(`@walkeros/server-destination-sqlite: CREATE TABLE failed: ${String(err)}`),config}else if(!sqlite._legacySkipProbe){let exists=!1;try{exists=await tableExists(client,sqlite.table??"events")}catch(err){return logger.throw(`@walkeros/server-destination-sqlite: table probe failed: ${String(err)}`),config}if(!exists)return logger.throw(`SQLite table "${sqlite.table??"events"}" not found in ${sqlite.url}. Run "walkeros setup destination.${id}" to create it.`),config}return sqlite._client=client,sqlite._runInsert=client.prepare(buildInsertSql(sqlite.table??"events")),sqlite._ownedClient=!(isSqliteEnv(env)&&env.client),config},push:async(event,context)=>await async function(event,{config:config,rule:rule,logger:logger}){const settings=config.settings,sqlite=settings?.sqlite;if(!sqlite)return void logger.warn("SQLite settings missing");const client=sqlite._client;if(!client)return void logger.warn("SQLite client not initialized");const ruleSettings=rule?.settings??{},ruleTable=(0,import_core3.isString)(ruleSettings.table)?ruleSettings.table:"",effectiveTable=ruleTable||sqlite.table||"events",runInsert=ruleTable&&ruleTable!==sqlite.table?client.prepare(buildInsertSql(effectiveTable)):sqlite._runInsert;if(!runInsert)return void logger.warn("SQLite insert statement not prepared");const row=eventToRow(event);logger.debug("SQLite INSERT",{table:effectiveTable,eventName:event.name});try{await runInsert(row),logger.debug("SQLite INSERT complete",{table:effectiveTable})}catch(error){logger.error("SQLite INSERT failed",{table:effectiveTable,error:error instanceof Error?error.message:String(error)})}}(event,context),async destroy({config:config}){const settings=config?.settings,sqlite=settings?.sqlite;if(!sqlite)return;const client=sqlite._client;if(client&&sqlite._ownedClient)try{await client.close()}finally{sqlite._client=void 0,sqlite._runInsert=void 0,sqlite._ownedClient=void 0}}},index_default=destinationSQLite;//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/config.ts","../src/drivers/betterSqlite3.ts","../src/drivers/libsql.ts","../src/drivers/index.ts","../src/serialize.ts","../src/push.ts","../src/setup.ts","../src/types/index.ts","../src/examples/index.ts","../src/examples/env.ts","../src/examples/step.ts"],"sourcesContent":["import type { Destination, Env, Settings, SqliteClient } from './types';\nimport { getConfig, isSqliteEnv } from './config';\nimport { createClientFromSettings } from './drivers';\nimport { buildCreateTableSql, buildInsertSql } from './serialize';\nimport { push } from './push';\nimport { setup, tableExists } from './setup';\n\n// Types re-export\nexport * as DestinationSQLite from './types';\n\n// Examples\nexport * as examples from './examples';\n\n// Serialization helpers (exported for tests and advanced consumers)\nexport {\n CANONICAL_COLUMNS,\n buildCreateTableSql,\n buildInsertSql,\n eventToRow,\n} from './serialize';\n\n// Driver URL detection (exported for tests and advanced consumers)\nexport { isLibsqlUrl } from './drivers';\n\n// Setup helpers (exported for tests and advanced consumers)\nexport { setup, DEFAULT_SCHEMA, DEFAULT_PRAGMAS, DEFAULT_SETUP } from './setup';\n\nexport const destinationSQLite: Destination = {\n type: 'sqlite',\n\n config: {},\n\n setup,\n\n async init({ config: partialConfig, logger, env, id }) {\n const config = getConfig(partialConfig, logger);\n const settings = config.settings as Settings;\n const sqlite = settings.sqlite;\n\n // Skip creation if a client has already been wired in (testing).\n if (sqlite._client) return config;\n\n let client: SqliteClient | undefined;\n\n // Prefer env-injected client / factory (tests, dependency injection).\n if (isSqliteEnv(env)) {\n const envTyped = env as Env;\n if (envTyped.client) {\n client = envTyped.client;\n } else if (envTyped.SqliteDriver) {\n client = await envTyped.SqliteDriver(sqlite.url, sqlite.authToken);\n }\n }\n\n // Production path: load real driver based on the URL prefix.\n if (!client) {\n try {\n client = await createClientFromSettings(sqlite.url, sqlite.authToken);\n } catch (err) {\n logger.throw(\n `@walkeros/server-destination-sqlite: driver load failed: ${String(err)}`,\n );\n return config;\n }\n }\n\n // Legacy auto-create path. Only runs when the user opted in via the\n // deprecated `settings.sqlite.schema: 'auto'` field. New users run\n // `walkeros setup destination.<id>` (see setup.ts) or set `config.setup`.\n if (sqlite._legacyAutoCreate) {\n try {\n await client.execute(buildCreateTableSql(sqlite.table ?? 'events'));\n } catch (err) {\n logger.throw(\n `@walkeros/server-destination-sqlite: CREATE TABLE failed: ${String(err)}`,\n );\n return config;\n }\n } else if (!sqlite._legacySkipProbe) {\n // Modern path: assume the table exists. Probe once and hard-fail with\n // an actionable message if it does not. Legacy `schema: 'manual'` skips\n // this probe (user supplies their own table shape).\n let exists = false;\n try {\n exists = await tableExists(client, sqlite.table ?? 'events');\n } catch (err) {\n logger.throw(\n `@walkeros/server-destination-sqlite: table probe failed: ${String(err)}`,\n );\n return config;\n }\n if (!exists) {\n logger.throw(\n `SQLite table \"${sqlite.table ?? 'events'}\" not found in ${sqlite.url}. ` +\n `Run \"walkeros setup destination.${id}\" to create it.`,\n );\n return config;\n }\n }\n\n // Cache the prepared insert for per-event calls.\n sqlite._client = client;\n sqlite._runInsert = client.prepare(\n buildInsertSql(sqlite.table ?? 'events'),\n );\n sqlite._ownedClient = !(isSqliteEnv(env) && (env as Env).client);\n\n return config;\n },\n\n async push(event, context) {\n return await push(event, context);\n },\n\n async destroy({ config }) {\n const settings = config?.settings as Settings | undefined;\n const sqlite = settings?.sqlite;\n if (!sqlite) return;\n\n const client = sqlite._client;\n // Only close clients the destination created (not user-provided).\n if (client && sqlite._ownedClient) {\n try {\n await client.close();\n } finally {\n sqlite._client = undefined;\n sqlite._runInsert = undefined;\n sqlite._ownedClient = undefined;\n }\n }\n },\n};\n\nexport default destinationSQLite;\n","import type {\n Config,\n Env,\n PartialConfig,\n Settings,\n SqliteSettings,\n} from './types';\nimport type { Logger } from '@walkeros/core';\nimport { isObject } from '@walkeros/core';\n\nexport function getConfig(\n partialConfig: PartialConfig = {},\n logger: Logger.Instance,\n): Config {\n const raw = (partialConfig.settings ?? {}) as Partial<Settings>;\n const sqlite: Partial<SqliteSettings> =\n raw.sqlite ?? ({} as Partial<SqliteSettings>);\n\n if (!sqlite.url) {\n logger.throw('Config settings sqlite.url missing');\n }\n\n const sqliteSettings: SqliteSettings = {\n ...sqlite,\n url: sqlite.url as string,\n table: sqlite.table ?? 'events',\n };\n\n // Migration shim: settings.sqlite.schema is the predecessor of config.setup.\n // Accept BOTH for one minor cycle, emit a deprecation WARN if `schema` is\n // used. Removed in the next major.\n if (sqlite.schema !== undefined) {\n logger.warn(\n 'config.settings.sqlite.schema is deprecated; use config.setup instead. ' +\n '\"schema: auto\" maps to \"setup: true\" (run \"walkeros setup destination.<id>\"); ' +\n '\"schema: manual\" maps to \"setup: false\". This field is removed in the next major.',\n );\n if (sqlite.schema === 'manual' && !partialConfig.mapping) {\n // Preserve the existing throw for the legacy \"manual without mapping\" case.\n logger.throw(\n 'Config settings sqlite.schema=\"manual\" requires a mapping (no default column mapping will be used).',\n );\n }\n sqliteSettings._legacyAutoCreate = sqlite.schema === 'auto';\n // Legacy `schema: 'manual'` opted out of the auto-create AND opted out of\n // the table-existence probe (user brings their own table). Honor that.\n sqliteSettings._legacySkipProbe = sqlite.schema === 'manual';\n } else {\n // No legacy schema flag. Default behavior: do NOT auto-create in init().\n // The user must run `walkeros setup destination.<id>` or set `config.setup: true`.\n sqliteSettings._legacyAutoCreate = false;\n sqliteSettings._legacySkipProbe = false;\n }\n\n // Strip the deprecated key from the live settings object so downstream code\n // never reads it. The flag is preserved on `_legacyAutoCreate`.\n delete sqliteSettings.schema;\n\n const settings: Settings = { sqlite: sqliteSettings };\n\n return { ...partialConfig, settings };\n}\n\nexport function isSqliteEnv(env: unknown): env is Env {\n if (!isObject(env)) return false;\n const maybe = env as { SqliteDriver?: unknown; client?: unknown };\n return (\n typeof maybe.SqliteDriver === 'function' || isObject(maybe.client ?? null)\n );\n}\n","import type { SqliteClient } from '../types';\n\n/**\n * better-sqlite3 driver -- synchronous native SQLite for local files and ':memory:'.\n * Wrapped in Promise.resolve so the cross-driver interface is always async.\n *\n * The library is loaded via dynamic require to allow tests to mock via\n * jest.mock('better-sqlite3'), and to keep it as an optional peer dependency.\n */\ninterface BetterSqliteStatement {\n run: (...args: unknown[]) => unknown;\n all: (...args: unknown[]) => ReadonlyArray<Record<string, unknown>>;\n}\n\ninterface BetterSqliteDatabase {\n prepare: (sql: string) => BetterSqliteStatement;\n close: () => void;\n}\n\ninterface BetterSqliteConstructor {\n new (filePath: string): BetterSqliteDatabase;\n}\n\nexport async function createBetterSqliteClient(\n filePath: string,\n): Promise<SqliteClient> {\n let Database: BetterSqliteConstructor;\n try {\n // Dynamic require so the module stays an optional peer dep and tests can mock.\n const loaded = require('better-sqlite3') as\n | BetterSqliteConstructor\n | { default: BetterSqliteConstructor };\n Database =\n 'default' in loaded && typeof loaded.default === 'function'\n ? loaded.default\n : (loaded as BetterSqliteConstructor);\n } catch (err) {\n throw new Error(\n '@walkeros/server-destination-sqlite: better-sqlite3 is not installed. ' +\n 'Install it with `npm install better-sqlite3`, or switch settings.sqlite.url to a libSQL URL. ' +\n `Original error: ${String(err)}`,\n );\n }\n\n const db = new Database(filePath);\n\n return {\n async execute(sql, args = []) {\n db.prepare(sql).run(...(args as unknown[]));\n },\n prepare(sql) {\n const stmt = db.prepare(sql);\n return async (args) => {\n stmt.run(...(args as unknown[]));\n };\n },\n async query(sql, args = []) {\n return db.prepare(sql).all(...(args as unknown[]));\n },\n async close() {\n db.close();\n },\n };\n}\n","import type { SqliteClient } from '../types';\n\n/**\n * libSQL / Turso driver -- async HTTP(S)/WSS/local file driver.\n *\n * Loaded via dynamic require to stay an optional peer dep and let tests mock\n * via jest.mock('@libsql/client').\n */\ninterface LibsqlExecuteArgs {\n sql: string;\n args?: ReadonlyArray<unknown>;\n}\n\ninterface LibsqlResultSet {\n rows?: ReadonlyArray<Record<string, unknown>>;\n}\n\ninterface LibsqlClient {\n execute: (input: LibsqlExecuteArgs) => Promise<LibsqlResultSet>;\n close: () => void;\n}\n\ninterface LibsqlCreateClientConfig {\n url: string;\n authToken?: string;\n}\n\ntype CreateClientFn = (config: LibsqlCreateClientConfig) => LibsqlClient;\n\nexport async function createLibsqlClient(\n url: string,\n authToken?: string,\n): Promise<SqliteClient> {\n let createClient: CreateClientFn;\n try {\n const loaded = require('@libsql/client') as {\n createClient?: CreateClientFn;\n default?: { createClient?: CreateClientFn };\n };\n const fn = loaded.createClient ?? loaded.default?.createClient;\n if (typeof fn !== 'function') {\n throw new Error('createClient export not found on @libsql/client');\n }\n createClient = fn;\n } catch (err) {\n throw new Error(\n '@walkeros/server-destination-sqlite: @libsql/client is not installed. ' +\n 'Install it with `npm install @libsql/client`, or use a local file URL. ' +\n `Original error: ${String(err)}`,\n );\n }\n\n const client = createClient({ url, authToken });\n\n return {\n async execute(sql, args = []) {\n await client.execute({ sql, args });\n },\n prepare(sql) {\n // libSQL has no server-side prepared-statement cache for HTTP; we close over\n // the sql string and call execute() per run. Cost is negligible vs network RTT.\n return async (args) => {\n await client.execute({ sql, args });\n };\n },\n async query(sql, args = []) {\n const result = await client.execute({ sql, args });\n return result?.rows ?? [];\n },\n async close() {\n client.close();\n },\n };\n}\n","import type { SqliteClient, SqliteClientFactory } from '../types';\nimport { createBetterSqliteClient } from './betterSqlite3';\nimport { createLibsqlClient } from './libsql';\n\nconst LIBSQL_URL_PREFIXES = [\n 'libsql://',\n 'http://',\n 'https://',\n 'ws://',\n 'wss://',\n];\n\nexport function isLibsqlUrl(url: string): boolean {\n return LIBSQL_URL_PREFIXES.some((p) => url.startsWith(p));\n}\n\n/**\n * Default client factory -- picks better-sqlite3 for local file paths and\n * libSQL for remote URLs. Tests can override via env.SqliteDriver.\n */\nexport const defaultFactory: SqliteClientFactory = async (url, authToken) => {\n if (isLibsqlUrl(url)) return createLibsqlClient(url, authToken);\n return createBetterSqliteClient(url);\n};\n\nexport async function createClientFromSettings(\n url: string,\n authToken: string | undefined,\n): Promise<SqliteClient> {\n return defaultFactory(url, authToken);\n}\n\nexport { createBetterSqliteClient, createLibsqlClient };\n","import type { WalkerOS } from '@walkeros/core';\nimport { isObject, isString } from '@walkeros/core';\n\n/**\n * Canonical columns for the auto-created events table. Order matters -- used\n * to build both CREATE TABLE and the prepared INSERT.\n */\nexport const CANONICAL_COLUMNS = [\n 'timestamp',\n 'event_id',\n 'name',\n 'entity',\n 'action',\n 'session_id',\n 'user_id',\n 'page_url',\n 'page_title',\n 'referrer_url',\n 'data',\n 'globals',\n 'consent',\n] as const;\n\nexport function buildCreateTableSql(table: string): string {\n return `CREATE TABLE IF NOT EXISTS ${table} (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n timestamp INTEGER,\n event_id TEXT,\n name TEXT,\n entity TEXT,\n action TEXT,\n session_id TEXT,\n user_id TEXT,\n page_url TEXT,\n page_title TEXT,\n referrer_url TEXT,\n data TEXT,\n globals TEXT,\n consent TEXT\n)`;\n}\n\nexport function buildInsertSql(table: string): string {\n const cols = CANONICAL_COLUMNS.join(', ');\n const placeholders = CANONICAL_COLUMNS.map(() => '?').join(', ');\n return `INSERT INTO ${table} (${cols}) VALUES (${placeholders})`;\n}\n\n/**\n * Map a walkerOS event onto the canonical column order. JSON blobs are\n * stringified. Missing fields become empty strings / zeros.\n */\nexport function eventToRow(event: WalkerOS.Event): unknown[] {\n const data = isObject(event.data) ? event.data : {};\n const title = isString(data.title) ? data.title : '';\n\n return [\n typeof event.timestamp === 'number' ? event.timestamp : Date.now(),\n event.id ?? '',\n event.name ?? '',\n event.entity ?? '',\n event.action ?? '',\n event.user?.session ?? '',\n event.user?.id ?? '',\n event.source?.url ?? '',\n title,\n event.source?.referrer ?? '',\n JSON.stringify(data),\n JSON.stringify(event.globals ?? {}),\n JSON.stringify(event.consent ?? {}),\n ];\n}\n","import type { PushFn, SqliteSettings } from './types';\nimport { isString } from '@walkeros/core';\nimport { buildInsertSql, eventToRow } from './serialize';\n\nexport const push: PushFn = async function (event, { config, rule, logger }) {\n const settings = config.settings as { sqlite?: SqliteSettings } | undefined;\n const sqlite: SqliteSettings | undefined = settings?.sqlite;\n\n if (!sqlite) {\n logger.warn('SQLite settings missing');\n return;\n }\n\n const client = sqlite._client;\n if (!client) {\n logger.warn('SQLite client not initialized');\n return;\n }\n\n const ruleSettings = rule?.settings ?? {};\n const ruleTable = isString(ruleSettings.table) ? ruleSettings.table : '';\n\n // If the rule overrides the table, we can't reuse the cached prepared statement\n // (which was bound to the default table). Build an ad-hoc prepared run.\n const effectiveTable = ruleTable || sqlite.table || 'events';\n const runInsert =\n ruleTable && ruleTable !== sqlite.table\n ? client.prepare(buildInsertSql(effectiveTable))\n : sqlite._runInsert;\n\n if (!runInsert) {\n logger.warn('SQLite insert statement not prepared');\n return;\n }\n\n const row = eventToRow(event);\n\n logger.debug('SQLite INSERT', {\n table: effectiveTable,\n eventName: event.name,\n });\n\n try {\n await runInsert(row);\n logger.debug('SQLite INSERT complete', { table: effectiveTable });\n } catch (error) {\n logger.error('SQLite INSERT failed', {\n table: effectiveTable,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n};\n","import type { LifecycleContext, Logger } from '@walkeros/core';\nimport { resolveSetup } from '@walkeros/core';\nimport type { DestinationServer } from '@walkeros/server-core';\nimport type { Env, Setup, SetupColumn, SqliteClient, Types } from './types';\nimport { isSqliteEnv } from './config';\nimport { createClientFromSettings } from './drivers';\n\n// Setup is wired to the destination's `setup` slot which uses the broader\n// `DestinationServer.Config<Types>` (settings is optional). We runtime-narrow\n// instead of using the local Config alias so the assignment in index.ts\n// type-checks without contravariance issues.\ntype WideConfig = DestinationServer.Config<Types>;\n\n/**\n * Default 15-column walkerOS Event v4 canonical schema.\n * Mirrors BigQuery (Plan 2). Only `name` is REQUIRED.\n */\nexport const DEFAULT_SCHEMA: SetupColumn[] = [\n { name: 'name', type: 'TEXT', notNull: true },\n { name: 'data', type: 'TEXT' },\n { name: 'context', type: 'TEXT' },\n { name: 'globals', type: 'TEXT' },\n { name: 'custom', type: 'TEXT' },\n { name: 'user', type: 'TEXT' },\n { name: 'nested', type: 'TEXT' },\n { name: 'consent', type: 'TEXT' },\n { name: 'id', type: 'TEXT' },\n { name: 'trigger', type: 'TEXT' },\n { name: 'entity', type: 'TEXT' },\n { name: 'action', type: 'TEXT' },\n { name: 'timestamp', type: 'TEXT' },\n { name: 'timing', type: 'INTEGER' },\n { name: 'source', type: 'TEXT' },\n];\n\nexport const DEFAULT_PRAGMAS: Record<string, string> = {\n journal_mode: 'WAL',\n synchronous: 'NORMAL',\n foreign_keys: 'ON',\n temp_store: 'MEMORY',\n};\n\nexport const DEFAULT_SETUP: Required<Setup> = {\n pragmas: DEFAULT_PRAGMAS,\n schema: DEFAULT_SCHEMA,\n indexes: [],\n};\n\nexport interface SetupResult {\n tableCreated: boolean;\n pragmasApplied: string[];\n indexesCreated: string[];\n}\n\ninterface PragmaTableInfoRow {\n name: string;\n type: string;\n}\n\ninterface SqliteMasterRow {\n name: string;\n}\n\nfunction isString(value: unknown): value is string {\n return typeof value === 'string';\n}\n\nfunction isPragmaTableInfoRow(value: unknown): value is PragmaTableInfoRow {\n if (typeof value !== 'object' || value === null) return false;\n const obj: Record<string, unknown> = { ...value };\n return isString(obj.name) && isString(obj.type);\n}\n\nfunction isSqliteMasterRow(value: unknown): value is SqliteMasterRow {\n if (typeof value !== 'object' || value === null) return false;\n const obj: Record<string, unknown> = { ...value };\n return isString(obj.name);\n}\n\nfunction buildCreateTableSql(table: string, columns: SetupColumn[]): string {\n const lines = columns.map((c) => {\n const parts: string[] = [c.name, c.type];\n if (c.primaryKey) parts.push('PRIMARY KEY');\n if (c.notNull) parts.push('NOT NULL');\n return parts.join(' ');\n });\n return `CREATE TABLE IF NOT EXISTS ${table} (\\n ${lines.join(',\\n ')}\\n)`;\n}\n\nasync function tableExists(\n client: SqliteClient,\n table: string,\n): Promise<boolean> {\n const rows = await client.query(\n \"SELECT name FROM sqlite_master WHERE type='table' AND name=?\",\n [table],\n );\n for (const row of rows) {\n if (isSqliteMasterRow(row) && row.name === table) return true;\n }\n return false;\n}\n\nasync function detectDrift(\n client: SqliteClient,\n table: string,\n declared: SetupColumn[],\n logger: Logger.Instance,\n): Promise<void> {\n const rows = await client.query(`PRAGMA table_info(${table})`);\n const actualByName = new Map<string, string>();\n for (const row of rows) {\n if (isPragmaTableInfoRow(row)) {\n actualByName.set(row.name, row.type.toUpperCase());\n }\n }\n\n for (const declaredCol of declared) {\n const actualType = actualByName.get(declaredCol.name);\n if (actualType === undefined) {\n logger.warn('setup.drift', {\n field: declaredCol.name,\n declared: declaredCol.type,\n actual: 'missing',\n });\n continue;\n }\n if (actualType !== declaredCol.type.toUpperCase()) {\n logger.warn('setup.drift', {\n field: declaredCol.name,\n declared: declaredCol.type,\n actual: actualType,\n });\n }\n }\n}\n\nexport const setup = async (\n ctx: LifecycleContext<WideConfig, Env>,\n): Promise<SetupResult | undefined> => {\n const { config, env, logger } = ctx;\n const merged = resolveSetup(config.setup, DEFAULT_SETUP);\n if (!merged) {\n logger.debug('setup: skipped (config.setup is false or unset)');\n return;\n }\n const options: Required<Setup> = {\n pragmas: { ...DEFAULT_PRAGMAS, ...(merged.pragmas ?? {}) },\n schema: merged.schema ?? DEFAULT_SCHEMA,\n indexes: merged.indexes ?? [],\n };\n\n const sqlite = config.settings?.sqlite;\n if (!sqlite || !sqlite.url) {\n logger.throw('setup: settings.sqlite.url is missing');\n return;\n }\n const table = sqlite.table ?? 'events';\n\n // Open transient client (or reuse env-injected one). We do NOT touch\n // sqlite._client / sqlite._runInsert here, setup is a separate lifecycle.\n let client: SqliteClient;\n let owned = true;\n if (isSqliteEnv(env) && env.client) {\n client = env.client;\n owned = false;\n } else if (isSqliteEnv(env) && env.SqliteDriver) {\n client = await env.SqliteDriver(sqlite.url, sqlite.authToken);\n } else {\n client = await createClientFromSettings(sqlite.url, sqlite.authToken);\n }\n\n try {\n // 1. Pragmas first (some, like journal_mode, must apply before any write).\n const pragmasApplied: string[] = [];\n for (const [key, value] of Object.entries(options.pragmas)) {\n await client.execute(`PRAGMA ${key} = ${value}`);\n pragmasApplied.push(key);\n }\n\n // 2. Probe whether the table already exists, so tableCreated is reported correctly.\n const tableExisted = await tableExists(client, table);\n\n // 3. CREATE TABLE IF NOT EXISTS with the declared schema.\n await client.execute(buildCreateTableSql(table, options.schema));\n\n // 4. Drift detection. Only when the table existed before; on fresh create\n // the schema matches by definition. Setup never auto-mutates: drift is\n // surfaced via WARN logs, never via ALTER TABLE.\n if (tableExisted) {\n await detectDrift(client, table, options.schema, logger);\n }\n\n // 5. Indexes (CREATE INDEX IF NOT EXISTS).\n const indexesCreated: string[] = [];\n for (const idx of options.indexes) {\n const unique = idx.unique ? 'UNIQUE ' : '';\n const cols = idx.columns.join(', ');\n await client.execute(\n `CREATE ${unique}INDEX IF NOT EXISTS ${idx.name} ON ${table}(${cols})`,\n );\n indexesCreated.push(idx.name);\n }\n\n return {\n tableCreated: !tableExisted,\n pragmasApplied,\n indexesCreated,\n };\n } finally {\n if (owned) {\n try {\n await client.close();\n } catch {\n /* swallow close errors during setup */\n }\n }\n }\n};\n\nexport { tableExists };\n","import type {\n Mapping as WalkerOSMapping,\n Destination as CoreDestination,\n SetupFn as CoreSetupFn,\n} from '@walkeros/core';\nimport type { DestinationServer } from '@walkeros/server-core';\n\n/**\n * @deprecated Use `config.setup` instead. Kept for one minor cycle.\n * `'auto'` maps to `setup: true`, `'manual'` maps to `setup: false`.\n * Removed in the next major.\n */\nexport type SchemaMode = 'auto' | 'manual';\n\n/**\n * Thin cross-driver connection interface. Both drivers are adapted to this shape.\n * Production code uses the adapters in src/drivers/*; tests inject a mock client\n * via env.client to capture sql/args without touching a real database.\n */\nexport interface SqliteClient {\n /** Execute a SQL statement. Used for CREATE TABLE and ad-hoc commands. */\n execute: (sql: string, args?: ReadonlyArray<unknown>) => Promise<void>;\n /**\n * Prepare a statement for repeated execution. Returned function binds args and runs.\n */\n prepare: (sql: string) => (args: ReadonlyArray<unknown>) => Promise<void>;\n /** Run a query that returns rows. Used by setup() for sqlite_master and PRAGMA table_info. */\n query: (\n sql: string,\n args?: ReadonlyArray<unknown>,\n ) => Promise<ReadonlyArray<Record<string, unknown>>>;\n /** Close the connection. */\n close: () => Promise<void>;\n}\n\n/**\n * Factory that constructs an SqliteClient from the user's connection settings.\n * Used by env.SqliteDriver so tests can inject a spy without mocking node_modules.\n */\nexport type SqliteClientFactory = (\n url: string,\n authToken?: string,\n) => Promise<SqliteClient>;\n\nexport interface SqliteSettings {\n /**\n * Connection URL. Starts with `libsql://`, `http://`, `https://`, `wss://`, `ws://`\n * → libSQL driver. Anything else → better-sqlite3 (treated as a file path).\n * Special value `:memory:` → in-memory (better-sqlite3).\n */\n url: string;\n /** libSQL / Turso auth token. Ignored for better-sqlite3. */\n authToken?: string;\n /** Target table name. Defaults to `events`. */\n table?: string;\n /**\n * @deprecated Use `config.setup` instead. Kept for one minor cycle.\n * `'auto'` maps to `setup: true` (init runs CREATE TABLE for the legacy schema).\n * `'manual'` maps to `setup: false`. Removed in the next major.\n */\n schema?: SchemaMode;\n\n // Runtime, set during init/setup, not user-facing.\n _client?: SqliteClient;\n _runInsert?: (args: ReadonlyArray<unknown>) => Promise<void>;\n _ownedClient?: boolean;\n /**\n * Internal flag set by the migration shim. When true, init() runs the legacy\n * 13-column CREATE TABLE path for backward compatibility. Otherwise init()\n * assumes the table already exists (created via `walkeros setup destination.<id>`).\n */\n _legacyAutoCreate?: boolean;\n /**\n * Internal flag set by the migration shim. When true (legacy `schema: 'manual'`),\n * init() skips the modern table-existence probe so the user can bring their own\n * table and column shape without hitting the new \"table not found\" hard-fail.\n */\n _legacySkipProbe?: boolean;\n}\n\nexport interface Settings {\n sqlite: SqliteSettings;\n}\n\nexport type InitSettings = Partial<Settings>;\n\nexport interface Mapping {\n /** Override target table for this rule. */\n table?: string;\n}\n\n/**\n * Provisioning options for `walkeros setup destination.<name>`.\n * Triggered only by the explicit CLI command. Idempotent, never auto-run.\n *\n * Connection URL and target table are read from `settings.sqlite.url` and\n * `settings.sqlite.table` (single source of truth, not duplicated here).\n */\nexport interface Setup {\n /** Pragmas to apply at setup time. Defaults: journal_mode=WAL, synchronous=NORMAL, foreign_keys=ON, temp_store=MEMORY. */\n pragmas?: Record<string, string | number>;\n /** Schema columns. Default: 15-column walkerOS Event v4 canonical (only `name` REQUIRED). */\n schema?: SetupColumn[];\n /** Indexes to create after the table. Optional. */\n indexes?: SetupIndex[];\n}\n\n/** Single column in the SQLite schema. */\nexport interface SetupColumn {\n name: string;\n type: 'TEXT' | 'INTEGER' | 'REAL' | 'BLOB' | 'NUMERIC';\n notNull?: boolean;\n primaryKey?: boolean;\n}\n\n/** SQLite index definition. */\nexport interface SetupIndex {\n name: string;\n columns: string[];\n unique?: boolean;\n}\n\n/**\n * Env, optional driver override. Production leaves this undefined and the\n * destination loads better-sqlite3 or @libsql/client dynamically. Tests\n * provide a factory via `SqliteDriver` or a pre-built client via `client`.\n */\nexport interface Env extends DestinationServer.Env {\n SqliteDriver?: SqliteClientFactory;\n client?: SqliteClient;\n}\n\nexport type Types = CoreDestination.Types<\n Settings,\n Mapping,\n Env,\n InitSettings,\n Setup\n>;\n\nexport interface Destination extends DestinationServer.Destination<Types> {\n init: DestinationServer.InitFn<Types>;\n}\n\nexport type Config = {\n settings: Settings;\n} & DestinationServer.Config<Types>;\n\nexport type InitFn = DestinationServer.InitFn<Types>;\nexport type PushFn = DestinationServer.PushFn<Types>;\nexport type SetupFn = CoreSetupFn<Config, Env>;\nexport type PartialConfig = DestinationServer.PartialConfig<Types>;\nexport type PushEvents = DestinationServer.PushEvents<Mapping>;\n\nexport type Rule = WalkerOSMapping.Rule<Mapping>;\nexport type Rules = WalkerOSMapping.Rules<Rule>;\n","export * as env from './env';\nexport * as step from './step';\n","import type { Env, SqliteClient, SqliteClientFactory } from '../types';\n\n// Narrow helper type aliases so mock functions are typed without `any`.\ntype ExecuteFn = (sql: string, args?: ReadonlyArray<unknown>) => Promise<void>;\ntype PrepareFn = (\n sql: string,\n) => (args: ReadonlyArray<unknown>) => Promise<void>;\ntype QueryFn = (\n sql: string,\n args?: ReadonlyArray<unknown>,\n) => Promise<ReadonlyArray<Record<string, unknown>>>;\ntype CloseFn = () => Promise<void>;\n\nconst asyncExecute: ExecuteFn = () => Promise.resolve();\nconst asyncClose: CloseFn = () => Promise.resolve();\nconst asyncPrepare: PrepareFn = () => () => Promise.resolve();\nconst asyncQuery: QueryFn = () => Promise.resolve([]);\n\nconst mockClient: SqliteClient = {\n execute: asyncExecute,\n prepare: asyncPrepare,\n query: asyncQuery,\n close: asyncClose,\n};\n\nconst mockFactory: SqliteClientFactory = () => Promise.resolve(mockClient);\n\nexport const push: Env = {\n SqliteDriver: mockFactory,\n};\n\n/**\n * Simulation tracking paths. Specifies which function calls to record when\n * running step examples through the collector.\n */\nexport const simulation = ['call:client.prepare', 'call:client.execute'];\n","import type { Flow } from '@walkeros/core';\nimport { getEvent } from '@walkeros/core';\nimport type { Settings } from '../types';\n\n/**\n * Extended step example that may carry destination-level settings overrides.\n */\nexport type SqliteStepExample = Flow.StepExample & {\n settings?: Partial<Settings>;\n};\n\n/**\n * Canonical insert into the default `events` table. `out` records the\n * column args the prepared INSERT is bound with.\n */\nexport const defaultInsert: SqliteStepExample = {\n title: 'Default insert',\n description:\n 'A walker event is inserted into the default events table with canonical columns and JSON-encoded sections.',\n in: getEvent('page view', {\n timestamp: 1700000100,\n id: 'evt-1',\n user: { session: 'sess-1', id: 'user-42' },\n data: { title: 'Home' },\n source: {\n type: 'browser',\n platform: 'web',\n url: 'https://example.com/',\n referrer: 'https://example.com/prev',\n },\n globals: { env: 'prod' },\n consent: { analytics: true },\n }),\n out: [\n [\n 'client.runInsert',\n [\n 1700000100,\n 'evt-1',\n 'page view',\n 'page',\n 'view',\n 'sess-1',\n 'user-42',\n 'https://example.com/',\n 'Home',\n 'https://example.com/prev',\n JSON.stringify({ title: 'Home' }),\n JSON.stringify({ env: 'prod' }),\n JSON.stringify({ analytics: true }),\n ],\n ],\n ],\n};\n\n/**\n * Custom table name. Verifies table overrides while using the canonical column set.\n */\nexport const customTable: SqliteStepExample = {\n title: 'Custom table',\n description:\n 'A destination-level table setting inserts events into a custom SQLite table with the same column layout.',\n in: getEvent('form submit', {\n timestamp: 1700000101,\n id: 'evt-2',\n user: { session: 'sess-99', id: '' },\n data: { type: 'contact' },\n source: {\n type: 'browser',\n platform: 'web',\n url: 'https://example.com/contact',\n },\n globals: {},\n consent: {},\n }),\n settings: {\n sqlite: {\n url: ':memory:',\n table: 'siteEvents',\n },\n },\n out: [\n [\n 'client.runInsert',\n [\n 1700000101,\n 'evt-2',\n 'form submit',\n 'form',\n 'submit',\n 'sess-99',\n '',\n 'https://example.com/contact',\n '',\n '',\n JSON.stringify({ type: 'contact' }),\n JSON.stringify({}),\n JSON.stringify({}),\n ],\n ],\n ],\n};\n\n/**\n * Order event with numeric data. Confirms JSON serialization of nested values.\n */\nexport const orderComplete: SqliteStepExample = {\n title: 'Order insert',\n description:\n 'An order complete is inserted with numeric data serialized as JSON in the data column.',\n in: getEvent('order complete', {\n timestamp: 1700000102,\n id: 'evt-3',\n user: { session: '', id: '' },\n data: { id: 'ORD-1', total: 99 },\n source: { type: 'collector', schema: '4' },\n globals: {},\n consent: {},\n }),\n out: [\n [\n 'client.runInsert',\n [\n 1700000102,\n 'evt-3',\n 'order complete',\n 'order',\n 'complete',\n '',\n '',\n '',\n '',\n '',\n JSON.stringify({ id: 'ORD-1', total: 99 }),\n JSON.stringify({}),\n JSON.stringify({}),\n ],\n ],\n ],\n};\n\n/**\n * Table override per rule -- routes this event to a dedicated table.\n */\nexport const tableOverride: SqliteStepExample = {\n title: 'Table override',\n description:\n 'A mapping rule overrides the target table so specific events are inserted into a dedicated SQLite table.',\n in: getEvent('order complete', {\n timestamp: 1700000103,\n id: 'evt-4',\n user: { session: '', id: '' },\n data: { id: 'ORD-2', total: 42 },\n source: { type: 'collector', schema: '4' },\n globals: {},\n consent: {},\n }),\n mapping: {\n settings: {\n table: 'orders',\n },\n },\n out: [\n [\n 'client.runInsert',\n [\n 1700000103,\n 'evt-4',\n 'order complete',\n 'order',\n 'complete',\n '',\n '',\n '',\n '',\n '',\n JSON.stringify({ id: 'ORD-2', total: 42 }),\n JSON.stringify({}),\n JSON.stringify({}),\n ],\n ],\n ],\n};\n\n/**\n * Ignored event -- mapping.ignore: true produces no insert call.\n */\nexport const ignoredEvent: SqliteStepExample = {\n public: false,\n in: getEvent('debug noise', {\n timestamp: 1700000104,\n id: 'evt-5',\n source: { type: 'collector', schema: '4' },\n }),\n mapping: { ignore: true },\n out: [],\n};\n"],"mappings":"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","names":["import_core","import_core","import_core","isString","buildCreateTableSql","push","import_core"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/config.ts","../src/drivers/betterSqlite3.ts","../src/drivers/libsql.ts","../src/drivers/index.ts","../src/serialize.ts","../src/push.ts","../src/setup.ts","../src/types/index.ts"],"sourcesContent":["import type { Destination, Env, Settings, SqliteClient } from './types';\nimport { getConfig, isSqliteEnv } from './config';\nimport { createClientFromSettings } from './drivers';\nimport { buildCreateTableSql, buildInsertSql } from './serialize';\nimport { push } from './push';\nimport { setup, tableExists } from './setup';\n\n// Types re-export\nexport * as DestinationSQLite from './types';\n\n// Serialization helpers (exported for tests and advanced consumers)\nexport {\n CANONICAL_COLUMNS,\n buildCreateTableSql,\n buildInsertSql,\n eventToRow,\n} from './serialize';\n\n// Driver URL detection (exported for tests and advanced consumers)\nexport { isLibsqlUrl } from './drivers';\n\n// Setup helpers (exported for tests and advanced consumers)\nexport { setup, DEFAULT_SCHEMA, DEFAULT_PRAGMAS, DEFAULT_SETUP } from './setup';\n\nexport const destinationSQLite: Destination = {\n type: 'sqlite',\n\n config: {},\n\n setup,\n\n async init({ config: partialConfig, logger, env, id }) {\n const config = getConfig(partialConfig, logger);\n const settings = config.settings as Settings;\n const sqlite = settings.sqlite;\n\n // Skip creation if a client has already been wired in (testing).\n if (sqlite._client) return config;\n\n let client: SqliteClient | undefined;\n\n // Prefer env-injected client / factory (tests, dependency injection).\n if (isSqliteEnv(env)) {\n const envTyped = env as Env;\n if (envTyped.client) {\n client = envTyped.client;\n } else if (envTyped.SqliteDriver) {\n client = await envTyped.SqliteDriver(sqlite.url, sqlite.authToken);\n }\n }\n\n // Production path: load real driver based on the URL prefix.\n if (!client) {\n try {\n client = await createClientFromSettings(sqlite.url, sqlite.authToken);\n } catch (err) {\n logger.throw(\n `@walkeros/server-destination-sqlite: driver load failed: ${String(err)}`,\n );\n return config;\n }\n }\n\n // Legacy auto-create path. Only runs when the user opted in via the\n // deprecated `settings.sqlite.schema: 'auto'` field. New users run\n // `walkeros setup destination.<id>` (see setup.ts) or set `config.setup`.\n if (sqlite._legacyAutoCreate) {\n try {\n await client.execute(buildCreateTableSql(sqlite.table ?? 'events'));\n } catch (err) {\n logger.throw(\n `@walkeros/server-destination-sqlite: CREATE TABLE failed: ${String(err)}`,\n );\n return config;\n }\n } else if (!sqlite._legacySkipProbe) {\n // Modern path: assume the table exists. Probe once and hard-fail with\n // an actionable message if it does not. Legacy `schema: 'manual'` skips\n // this probe (user supplies their own table shape).\n let exists = false;\n try {\n exists = await tableExists(client, sqlite.table ?? 'events');\n } catch (err) {\n logger.throw(\n `@walkeros/server-destination-sqlite: table probe failed: ${String(err)}`,\n );\n return config;\n }\n if (!exists) {\n logger.throw(\n `SQLite table \"${sqlite.table ?? 'events'}\" not found in ${sqlite.url}. ` +\n `Run \"walkeros setup destination.${id}\" to create it.`,\n );\n return config;\n }\n }\n\n // Cache the prepared insert for per-event calls.\n sqlite._client = client;\n sqlite._runInsert = client.prepare(\n buildInsertSql(sqlite.table ?? 'events'),\n );\n sqlite._ownedClient = !(isSqliteEnv(env) && (env as Env).client);\n\n return config;\n },\n\n async push(event, context) {\n return await push(event, context);\n },\n\n async destroy({ config }) {\n const settings = config?.settings as Settings | undefined;\n const sqlite = settings?.sqlite;\n if (!sqlite) return;\n\n const client = sqlite._client;\n // Only close clients the destination created (not user-provided).\n if (client && sqlite._ownedClient) {\n try {\n await client.close();\n } finally {\n sqlite._client = undefined;\n sqlite._runInsert = undefined;\n sqlite._ownedClient = undefined;\n }\n }\n },\n};\n\nexport default destinationSQLite;\n","import type {\n Config,\n Env,\n PartialConfig,\n Settings,\n SqliteSettings,\n} from './types';\nimport type { Logger } from '@walkeros/core';\nimport { isObject } from '@walkeros/core';\n\nexport function getConfig(\n partialConfig: PartialConfig = {},\n logger: Logger.Instance,\n): Config {\n const raw = (partialConfig.settings ?? {}) as Partial<Settings>;\n const sqlite: Partial<SqliteSettings> =\n raw.sqlite ?? ({} as Partial<SqliteSettings>);\n\n if (!sqlite.url) {\n logger.throw('Config settings sqlite.url missing');\n }\n\n const sqliteSettings: SqliteSettings = {\n ...sqlite,\n url: sqlite.url as string,\n table: sqlite.table ?? 'events',\n };\n\n // Migration shim: settings.sqlite.schema is the predecessor of config.setup.\n // Accept BOTH for one minor cycle, emit a deprecation WARN if `schema` is\n // used. Removed in the next major.\n if (sqlite.schema !== undefined) {\n logger.warn(\n 'config.settings.sqlite.schema is deprecated; use config.setup instead. ' +\n '\"schema: auto\" maps to \"setup: true\" (run \"walkeros setup destination.<id>\"); ' +\n '\"schema: manual\" maps to \"setup: false\". This field is removed in the next major.',\n );\n if (sqlite.schema === 'manual' && !partialConfig.mapping) {\n // Preserve the existing throw for the legacy \"manual without mapping\" case.\n logger.throw(\n 'Config settings sqlite.schema=\"manual\" requires a mapping (no default column mapping will be used).',\n );\n }\n sqliteSettings._legacyAutoCreate = sqlite.schema === 'auto';\n // Legacy `schema: 'manual'` opted out of the auto-create AND opted out of\n // the table-existence probe (user brings their own table). Honor that.\n sqliteSettings._legacySkipProbe = sqlite.schema === 'manual';\n } else {\n // No legacy schema flag. Default behavior: do NOT auto-create in init().\n // The user must run `walkeros setup destination.<id>` or set `config.setup: true`.\n sqliteSettings._legacyAutoCreate = false;\n sqliteSettings._legacySkipProbe = false;\n }\n\n // Strip the deprecated key from the live settings object so downstream code\n // never reads it. The flag is preserved on `_legacyAutoCreate`.\n delete sqliteSettings.schema;\n\n const settings: Settings = { sqlite: sqliteSettings };\n\n return { ...partialConfig, settings };\n}\n\nexport function isSqliteEnv(env: unknown): env is Env {\n if (!isObject(env)) return false;\n const maybe = env as { SqliteDriver?: unknown; client?: unknown };\n return (\n typeof maybe.SqliteDriver === 'function' || isObject(maybe.client ?? null)\n );\n}\n","import type { SqliteClient } from '../types';\n\n/**\n * better-sqlite3 driver -- synchronous native SQLite for local files and ':memory:'.\n * Wrapped in Promise.resolve so the cross-driver interface is always async.\n *\n * The library is loaded via dynamic require to allow tests to mock via\n * jest.mock('better-sqlite3'), and to keep it as an optional peer dependency.\n */\ninterface BetterSqliteStatement {\n run: (...args: unknown[]) => unknown;\n all: (...args: unknown[]) => ReadonlyArray<Record<string, unknown>>;\n}\n\ninterface BetterSqliteDatabase {\n prepare: (sql: string) => BetterSqliteStatement;\n close: () => void;\n}\n\ninterface BetterSqliteConstructor {\n new (filePath: string): BetterSqliteDatabase;\n}\n\nexport async function createBetterSqliteClient(\n filePath: string,\n): Promise<SqliteClient> {\n let Database: BetterSqliteConstructor;\n try {\n // Dynamic require so the module stays an optional peer dep and tests can mock.\n const loaded = require('better-sqlite3') as\n | BetterSqliteConstructor\n | { default: BetterSqliteConstructor };\n Database =\n 'default' in loaded && typeof loaded.default === 'function'\n ? loaded.default\n : (loaded as BetterSqliteConstructor);\n } catch (err) {\n throw new Error(\n '@walkeros/server-destination-sqlite: better-sqlite3 is not installed. ' +\n 'Install it with `npm install better-sqlite3`, or switch settings.sqlite.url to a libSQL URL. ' +\n `Original error: ${String(err)}`,\n );\n }\n\n const db = new Database(filePath);\n\n return {\n async execute(sql, args = []) {\n db.prepare(sql).run(...(args as unknown[]));\n },\n prepare(sql) {\n const stmt = db.prepare(sql);\n return async (args) => {\n stmt.run(...(args as unknown[]));\n };\n },\n async query(sql, args = []) {\n return db.prepare(sql).all(...(args as unknown[]));\n },\n async close() {\n db.close();\n },\n };\n}\n","import type { SqliteClient } from '../types';\n\n/**\n * libSQL / Turso driver -- async HTTP(S)/WSS/local file driver.\n *\n * Loaded via dynamic require to stay an optional peer dep and let tests mock\n * via jest.mock('@libsql/client').\n */\ninterface LibsqlExecuteArgs {\n sql: string;\n args?: ReadonlyArray<unknown>;\n}\n\ninterface LibsqlResultSet {\n rows?: ReadonlyArray<Record<string, unknown>>;\n}\n\ninterface LibsqlClient {\n execute: (input: LibsqlExecuteArgs) => Promise<LibsqlResultSet>;\n close: () => void;\n}\n\ninterface LibsqlCreateClientConfig {\n url: string;\n authToken?: string;\n}\n\ntype CreateClientFn = (config: LibsqlCreateClientConfig) => LibsqlClient;\n\nexport async function createLibsqlClient(\n url: string,\n authToken?: string,\n): Promise<SqliteClient> {\n let createClient: CreateClientFn;\n try {\n const loaded = require('@libsql/client') as {\n createClient?: CreateClientFn;\n default?: { createClient?: CreateClientFn };\n };\n const fn = loaded.createClient ?? loaded.default?.createClient;\n if (typeof fn !== 'function') {\n throw new Error('createClient export not found on @libsql/client');\n }\n createClient = fn;\n } catch (err) {\n throw new Error(\n '@walkeros/server-destination-sqlite: @libsql/client is not installed. ' +\n 'Install it with `npm install @libsql/client`, or use a local file URL. ' +\n `Original error: ${String(err)}`,\n );\n }\n\n const client = createClient({ url, authToken });\n\n return {\n async execute(sql, args = []) {\n await client.execute({ sql, args });\n },\n prepare(sql) {\n // libSQL has no server-side prepared-statement cache for HTTP; we close over\n // the sql string and call execute() per run. Cost is negligible vs network RTT.\n return async (args) => {\n await client.execute({ sql, args });\n };\n },\n async query(sql, args = []) {\n const result = await client.execute({ sql, args });\n return result?.rows ?? [];\n },\n async close() {\n client.close();\n },\n };\n}\n","import type { SqliteClient, SqliteClientFactory } from '../types';\nimport { createBetterSqliteClient } from './betterSqlite3';\nimport { createLibsqlClient } from './libsql';\n\nconst LIBSQL_URL_PREFIXES = [\n 'libsql://',\n 'http://',\n 'https://',\n 'ws://',\n 'wss://',\n];\n\nexport function isLibsqlUrl(url: string): boolean {\n return LIBSQL_URL_PREFIXES.some((p) => url.startsWith(p));\n}\n\n/**\n * Default client factory -- picks better-sqlite3 for local file paths and\n * libSQL for remote URLs. Tests can override via env.SqliteDriver.\n */\nexport const defaultFactory: SqliteClientFactory = async (url, authToken) => {\n if (isLibsqlUrl(url)) return createLibsqlClient(url, authToken);\n return createBetterSqliteClient(url);\n};\n\nexport async function createClientFromSettings(\n url: string,\n authToken: string | undefined,\n): Promise<SqliteClient> {\n return defaultFactory(url, authToken);\n}\n\nexport { createBetterSqliteClient, createLibsqlClient };\n","import type { WalkerOS } from '@walkeros/core';\nimport { isObject, isString } from '@walkeros/core';\n\n/**\n * Canonical columns for the auto-created events table. Order matters -- used\n * to build both CREATE TABLE and the prepared INSERT.\n */\nexport const CANONICAL_COLUMNS = [\n 'timestamp',\n 'event_id',\n 'name',\n 'entity',\n 'action',\n 'session_id',\n 'user_id',\n 'page_url',\n 'page_title',\n 'referrer_url',\n 'data',\n 'globals',\n 'consent',\n] as const;\n\nexport function buildCreateTableSql(table: string): string {\n return `CREATE TABLE IF NOT EXISTS ${table} (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n timestamp INTEGER,\n event_id TEXT,\n name TEXT,\n entity TEXT,\n action TEXT,\n session_id TEXT,\n user_id TEXT,\n page_url TEXT,\n page_title TEXT,\n referrer_url TEXT,\n data TEXT,\n globals TEXT,\n consent TEXT\n)`;\n}\n\nexport function buildInsertSql(table: string): string {\n const cols = CANONICAL_COLUMNS.join(', ');\n const placeholders = CANONICAL_COLUMNS.map(() => '?').join(', ');\n return `INSERT INTO ${table} (${cols}) VALUES (${placeholders})`;\n}\n\n/**\n * Map a walkerOS event onto the canonical column order. JSON blobs are\n * stringified. Missing fields become empty strings / zeros.\n */\nexport function eventToRow(event: WalkerOS.Event): unknown[] {\n const data = isObject(event.data) ? event.data : {};\n const title = isString(data.title) ? data.title : '';\n\n return [\n typeof event.timestamp === 'number' ? event.timestamp : Date.now(),\n event.id ?? '',\n event.name ?? '',\n event.entity ?? '',\n event.action ?? '',\n event.user?.session ?? '',\n event.user?.id ?? '',\n event.source?.url ?? '',\n title,\n event.source?.referrer ?? '',\n JSON.stringify(data),\n JSON.stringify(event.globals ?? {}),\n JSON.stringify(event.consent ?? {}),\n ];\n}\n","import type { PushFn, SqliteSettings } from './types';\nimport { isString } from '@walkeros/core';\nimport { buildInsertSql, eventToRow } from './serialize';\n\nexport const push: PushFn = async function (event, { config, rule, logger }) {\n const settings = config.settings as { sqlite?: SqliteSettings } | undefined;\n const sqlite: SqliteSettings | undefined = settings?.sqlite;\n\n if (!sqlite) {\n logger.warn('SQLite settings missing');\n return;\n }\n\n const client = sqlite._client;\n if (!client) {\n logger.warn('SQLite client not initialized');\n return;\n }\n\n const ruleSettings = rule?.settings ?? {};\n const ruleTable = isString(ruleSettings.table) ? ruleSettings.table : '';\n\n // If the rule overrides the table, we can't reuse the cached prepared statement\n // (which was bound to the default table). Build an ad-hoc prepared run.\n const effectiveTable = ruleTable || sqlite.table || 'events';\n const runInsert =\n ruleTable && ruleTable !== sqlite.table\n ? client.prepare(buildInsertSql(effectiveTable))\n : sqlite._runInsert;\n\n if (!runInsert) {\n logger.warn('SQLite insert statement not prepared');\n return;\n }\n\n const row = eventToRow(event);\n\n logger.debug('SQLite INSERT', {\n table: effectiveTable,\n eventName: event.name,\n });\n\n try {\n await runInsert(row);\n logger.debug('SQLite INSERT complete', { table: effectiveTable });\n } catch (error) {\n logger.error('SQLite INSERT failed', {\n table: effectiveTable,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n};\n","import type { LifecycleContext, Logger } from '@walkeros/core';\nimport { resolveSetup } from '@walkeros/core';\nimport type { DestinationServer } from '@walkeros/server-core';\nimport type { Env, Setup, SetupColumn, SqliteClient, Types } from './types';\nimport { isSqliteEnv } from './config';\nimport { createClientFromSettings } from './drivers';\n\n// Setup is wired to the destination's `setup` slot which uses the broader\n// `DestinationServer.Config<Types>` (settings is optional). We runtime-narrow\n// instead of using the local Config alias so the assignment in index.ts\n// type-checks without contravariance issues.\ntype WideConfig = DestinationServer.Config<Types>;\n\n/**\n * Default 15-column walkerOS Event v4 canonical schema.\n * Mirrors BigQuery (Plan 2). Only `name` is REQUIRED.\n */\nexport const DEFAULT_SCHEMA: SetupColumn[] = [\n { name: 'name', type: 'TEXT', notNull: true },\n { name: 'data', type: 'TEXT' },\n { name: 'context', type: 'TEXT' },\n { name: 'globals', type: 'TEXT' },\n { name: 'custom', type: 'TEXT' },\n { name: 'user', type: 'TEXT' },\n { name: 'nested', type: 'TEXT' },\n { name: 'consent', type: 'TEXT' },\n { name: 'id', type: 'TEXT' },\n { name: 'trigger', type: 'TEXT' },\n { name: 'entity', type: 'TEXT' },\n { name: 'action', type: 'TEXT' },\n { name: 'timestamp', type: 'TEXT' },\n { name: 'timing', type: 'INTEGER' },\n { name: 'source', type: 'TEXT' },\n];\n\nexport const DEFAULT_PRAGMAS: Record<string, string> = {\n journal_mode: 'WAL',\n synchronous: 'NORMAL',\n foreign_keys: 'ON',\n temp_store: 'MEMORY',\n};\n\nexport const DEFAULT_SETUP: Required<Setup> = {\n pragmas: DEFAULT_PRAGMAS,\n schema: DEFAULT_SCHEMA,\n indexes: [],\n};\n\nexport interface SetupResult {\n tableCreated: boolean;\n pragmasApplied: string[];\n indexesCreated: string[];\n}\n\ninterface PragmaTableInfoRow {\n name: string;\n type: string;\n}\n\ninterface SqliteMasterRow {\n name: string;\n}\n\nfunction isString(value: unknown): value is string {\n return typeof value === 'string';\n}\n\nfunction isPragmaTableInfoRow(value: unknown): value is PragmaTableInfoRow {\n if (typeof value !== 'object' || value === null) return false;\n const obj: Record<string, unknown> = { ...value };\n return isString(obj.name) && isString(obj.type);\n}\n\nfunction isSqliteMasterRow(value: unknown): value is SqliteMasterRow {\n if (typeof value !== 'object' || value === null) return false;\n const obj: Record<string, unknown> = { ...value };\n return isString(obj.name);\n}\n\nfunction buildCreateTableSql(table: string, columns: SetupColumn[]): string {\n const lines = columns.map((c) => {\n const parts: string[] = [c.name, c.type];\n if (c.primaryKey) parts.push('PRIMARY KEY');\n if (c.notNull) parts.push('NOT NULL');\n return parts.join(' ');\n });\n return `CREATE TABLE IF NOT EXISTS ${table} (\\n ${lines.join(',\\n ')}\\n)`;\n}\n\nasync function tableExists(\n client: SqliteClient,\n table: string,\n): Promise<boolean> {\n const rows = await client.query(\n \"SELECT name FROM sqlite_master WHERE type='table' AND name=?\",\n [table],\n );\n for (const row of rows) {\n if (isSqliteMasterRow(row) && row.name === table) return true;\n }\n return false;\n}\n\nasync function detectDrift(\n client: SqliteClient,\n table: string,\n declared: SetupColumn[],\n logger: Logger.Instance,\n): Promise<void> {\n const rows = await client.query(`PRAGMA table_info(${table})`);\n const actualByName = new Map<string, string>();\n for (const row of rows) {\n if (isPragmaTableInfoRow(row)) {\n actualByName.set(row.name, row.type.toUpperCase());\n }\n }\n\n for (const declaredCol of declared) {\n const actualType = actualByName.get(declaredCol.name);\n if (actualType === undefined) {\n logger.warn('setup.drift', {\n field: declaredCol.name,\n declared: declaredCol.type,\n actual: 'missing',\n });\n continue;\n }\n if (actualType !== declaredCol.type.toUpperCase()) {\n logger.warn('setup.drift', {\n field: declaredCol.name,\n declared: declaredCol.type,\n actual: actualType,\n });\n }\n }\n}\n\nexport const setup = async (\n ctx: LifecycleContext<WideConfig, Env>,\n): Promise<SetupResult | undefined> => {\n const { config, env, logger } = ctx;\n const merged = resolveSetup(config.setup, DEFAULT_SETUP);\n if (!merged) {\n logger.debug('setup: skipped (config.setup is false or unset)');\n return;\n }\n const options: Required<Setup> = {\n pragmas: { ...DEFAULT_PRAGMAS, ...(merged.pragmas ?? {}) },\n schema: merged.schema ?? DEFAULT_SCHEMA,\n indexes: merged.indexes ?? [],\n };\n\n const sqlite = config.settings?.sqlite;\n if (!sqlite || !sqlite.url) {\n logger.throw('setup: settings.sqlite.url is missing');\n return;\n }\n const table = sqlite.table ?? 'events';\n\n // Open transient client (or reuse env-injected one). We do NOT touch\n // sqlite._client / sqlite._runInsert here, setup is a separate lifecycle.\n let client: SqliteClient;\n let owned = true;\n if (isSqliteEnv(env) && env.client) {\n client = env.client;\n owned = false;\n } else if (isSqliteEnv(env) && env.SqliteDriver) {\n client = await env.SqliteDriver(sqlite.url, sqlite.authToken);\n } else {\n client = await createClientFromSettings(sqlite.url, sqlite.authToken);\n }\n\n try {\n // 1. Pragmas first (some, like journal_mode, must apply before any write).\n const pragmasApplied: string[] = [];\n for (const [key, value] of Object.entries(options.pragmas)) {\n await client.execute(`PRAGMA ${key} = ${value}`);\n pragmasApplied.push(key);\n }\n\n // 2. Probe whether the table already exists, so tableCreated is reported correctly.\n const tableExisted = await tableExists(client, table);\n\n // 3. CREATE TABLE IF NOT EXISTS with the declared schema.\n await client.execute(buildCreateTableSql(table, options.schema));\n\n // 4. Drift detection. Only when the table existed before; on fresh create\n // the schema matches by definition. Setup never auto-mutates: drift is\n // surfaced via WARN logs, never via ALTER TABLE.\n if (tableExisted) {\n await detectDrift(client, table, options.schema, logger);\n }\n\n // 5. Indexes (CREATE INDEX IF NOT EXISTS).\n const indexesCreated: string[] = [];\n for (const idx of options.indexes) {\n const unique = idx.unique ? 'UNIQUE ' : '';\n const cols = idx.columns.join(', ');\n await client.execute(\n `CREATE ${unique}INDEX IF NOT EXISTS ${idx.name} ON ${table}(${cols})`,\n );\n indexesCreated.push(idx.name);\n }\n\n return {\n tableCreated: !tableExisted,\n pragmasApplied,\n indexesCreated,\n };\n } finally {\n if (owned) {\n try {\n await client.close();\n } catch {\n /* swallow close errors during setup */\n }\n }\n }\n};\n\nexport { tableExists };\n","import type {\n Mapping as WalkerOSMapping,\n Destination as CoreDestination,\n SetupFn as CoreSetupFn,\n} from '@walkeros/core';\nimport type { DestinationServer } from '@walkeros/server-core';\n\n/**\n * @deprecated Use `config.setup` instead. Kept for one minor cycle.\n * `'auto'` maps to `setup: true`, `'manual'` maps to `setup: false`.\n * Removed in the next major.\n */\nexport type SchemaMode = 'auto' | 'manual';\n\n/**\n * Thin cross-driver connection interface. Both drivers are adapted to this shape.\n * Production code uses the adapters in src/drivers/*; tests inject a mock client\n * via env.client to capture sql/args without touching a real database.\n */\nexport interface SqliteClient {\n /** Execute a SQL statement. Used for CREATE TABLE and ad-hoc commands. */\n execute: (sql: string, args?: ReadonlyArray<unknown>) => Promise<void>;\n /**\n * Prepare a statement for repeated execution. Returned function binds args and runs.\n */\n prepare: (sql: string) => (args: ReadonlyArray<unknown>) => Promise<void>;\n /** Run a query that returns rows. Used by setup() for sqlite_master and PRAGMA table_info. */\n query: (\n sql: string,\n args?: ReadonlyArray<unknown>,\n ) => Promise<ReadonlyArray<Record<string, unknown>>>;\n /** Close the connection. */\n close: () => Promise<void>;\n}\n\n/**\n * Factory that constructs an SqliteClient from the user's connection settings.\n * Used by env.SqliteDriver so tests can inject a spy without mocking node_modules.\n */\nexport type SqliteClientFactory = (\n url: string,\n authToken?: string,\n) => Promise<SqliteClient>;\n\nexport interface SqliteSettings {\n /**\n * Connection URL. Starts with `libsql://`, `http://`, `https://`, `wss://`, `ws://`\n * → libSQL driver. Anything else → better-sqlite3 (treated as a file path).\n * Special value `:memory:` → in-memory (better-sqlite3).\n */\n url: string;\n /** libSQL / Turso auth token. Ignored for better-sqlite3. */\n authToken?: string;\n /** Target table name. Defaults to `events`. */\n table?: string;\n /**\n * @deprecated Use `config.setup` instead. Kept for one minor cycle.\n * `'auto'` maps to `setup: true` (init runs CREATE TABLE for the legacy schema).\n * `'manual'` maps to `setup: false`. Removed in the next major.\n */\n schema?: SchemaMode;\n\n // Runtime, set during init/setup, not user-facing.\n _client?: SqliteClient;\n _runInsert?: (args: ReadonlyArray<unknown>) => Promise<void>;\n _ownedClient?: boolean;\n /**\n * Internal flag set by the migration shim. When true, init() runs the legacy\n * 13-column CREATE TABLE path for backward compatibility. Otherwise init()\n * assumes the table already exists (created via `walkeros setup destination.<id>`).\n */\n _legacyAutoCreate?: boolean;\n /**\n * Internal flag set by the migration shim. When true (legacy `schema: 'manual'`),\n * init() skips the modern table-existence probe so the user can bring their own\n * table and column shape without hitting the new \"table not found\" hard-fail.\n */\n _legacySkipProbe?: boolean;\n}\n\nexport interface Settings {\n sqlite: SqliteSettings;\n}\n\nexport type InitSettings = Partial<Settings>;\n\nexport interface Mapping {\n /** Override target table for this rule. */\n table?: string;\n}\n\n/**\n * Provisioning options for `walkeros setup destination.<name>`.\n * Triggered only by the explicit CLI command. Idempotent, never auto-run.\n *\n * Connection URL and target table are read from `settings.sqlite.url` and\n * `settings.sqlite.table` (single source of truth, not duplicated here).\n */\nexport interface Setup {\n /** Pragmas to apply at setup time. Defaults: journal_mode=WAL, synchronous=NORMAL, foreign_keys=ON, temp_store=MEMORY. */\n pragmas?: Record<string, string | number>;\n /** Schema columns. Default: 15-column walkerOS Event v4 canonical (only `name` REQUIRED). */\n schema?: SetupColumn[];\n /** Indexes to create after the table. Optional. */\n indexes?: SetupIndex[];\n}\n\n/** Single column in the SQLite schema. */\nexport interface SetupColumn {\n name: string;\n type: 'TEXT' | 'INTEGER' | 'REAL' | 'BLOB' | 'NUMERIC';\n notNull?: boolean;\n primaryKey?: boolean;\n}\n\n/** SQLite index definition. */\nexport interface SetupIndex {\n name: string;\n columns: string[];\n unique?: boolean;\n}\n\n/**\n * Env, optional driver override. Production leaves this undefined and the\n * destination loads better-sqlite3 or @libsql/client dynamically. Tests\n * provide a factory via `SqliteDriver` or a pre-built client via `client`.\n */\nexport interface Env extends DestinationServer.Env {\n SqliteDriver?: SqliteClientFactory;\n client?: SqliteClient;\n}\n\nexport type Types = CoreDestination.Types<\n Settings,\n Mapping,\n Env,\n InitSettings,\n Setup\n>;\n\nexport interface Destination extends DestinationServer.Destination<Types> {\n init: DestinationServer.InitFn<Types>;\n}\n\nexport type Config = {\n settings: Settings;\n} & DestinationServer.Config<Types>;\n\nexport type InitFn = DestinationServer.InitFn<Types>;\nexport type PushFn = DestinationServer.PushFn<Types>;\nexport type SetupFn = CoreSetupFn<Config, Env>;\nexport type PartialConfig = DestinationServer.PartialConfig<Types>;\nexport type PushEvents = DestinationServer.PushEvents<Mapping>;\n\nexport type Rule = WalkerOSMapping.Rule<Mapping>;\nexport type Rules = WalkerOSMapping.Rules<Rule>;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACQA,kBAAyB;AAElB,SAAS,UACd,gBAA+B,CAAC,GAChC,QACQ;AACR,QAAM,MAAO,cAAc,YAAY,CAAC;AACxC,QAAM,SACJ,IAAI,UAAW,CAAC;AAElB,MAAI,CAAC,OAAO,KAAK;AACf,WAAO,MAAM,oCAAoC;AAAA,EACnD;AAEA,QAAM,iBAAiC;AAAA,IACrC,GAAG;AAAA,IACH,KAAK,OAAO;AAAA,IACZ,OAAO,OAAO,SAAS;AAAA,EACzB;AAKA,MAAI,OAAO,WAAW,QAAW;AAC/B,WAAO;AAAA,MACL;AAAA,IAGF;AACA,QAAI,OAAO,WAAW,YAAY,CAAC,cAAc,SAAS;AAExD,aAAO;AAAA,QACL;AAAA,MACF;AAAA,IACF;AACA,mBAAe,oBAAoB,OAAO,WAAW;AAGrD,mBAAe,mBAAmB,OAAO,WAAW;AAAA,EACtD,OAAO;AAGL,mBAAe,oBAAoB;AACnC,mBAAe,mBAAmB;AAAA,EACpC;AAIA,SAAO,eAAe;AAEtB,QAAM,WAAqB,EAAE,QAAQ,eAAe;AAEpD,SAAO,EAAE,GAAG,eAAe,SAAS;AACtC;AAEO,SAAS,YAAY,KAA0B;AACpD,MAAI,KAAC,sBAAS,GAAG,EAAG,QAAO;AAC3B,QAAM,QAAQ;AACd,SACE,OAAO,MAAM,iBAAiB,kBAAc,sBAAS,MAAM,UAAU,IAAI;AAE7E;;;AC9CA,eAAsB,yBACpB,UACuB;AACvB,MAAI;AACJ,MAAI;AAEF,UAAM,SAAS,QAAQ,gBAAgB;AAGvC,eACE,aAAa,UAAU,OAAO,OAAO,YAAY,aAC7C,OAAO,UACN;AAAA,EACT,SAAS,KAAK;AACZ,UAAM,IAAI;AAAA,MACR,wLAEqB,OAAO,GAAG,CAAC;AAAA,IAClC;AAAA,EACF;AAEA,QAAM,KAAK,IAAI,SAAS,QAAQ;AAEhC,SAAO;AAAA,IACL,MAAM,QAAQ,KAAK,OAAO,CAAC,GAAG;AAC5B,SAAG,QAAQ,GAAG,EAAE,IAAI,GAAI,IAAkB;AAAA,IAC5C;AAAA,IACA,QAAQ,KAAK;AACX,YAAM,OAAO,GAAG,QAAQ,GAAG;AAC3B,aAAO,OAAO,SAAS;AACrB,aAAK,IAAI,GAAI,IAAkB;AAAA,MACjC;AAAA,IACF;AAAA,IACA,MAAM,MAAM,KAAK,OAAO,CAAC,GAAG;AAC1B,aAAO,GAAG,QAAQ,GAAG,EAAE,IAAI,GAAI,IAAkB;AAAA,IACnD;AAAA,IACA,MAAM,QAAQ;AACZ,SAAG,MAAM;AAAA,IACX;AAAA,EACF;AACF;;;AClCA,eAAsB,mBACpB,KACA,WACuB;AACvB,MAAI;AACJ,MAAI;AACF,UAAM,SAAS,QAAQ,gBAAgB;AAIvC,UAAM,KAAK,OAAO,gBAAgB,OAAO,SAAS;AAClD,QAAI,OAAO,OAAO,YAAY;AAC5B,YAAM,IAAI,MAAM,iDAAiD;AAAA,IACnE;AACA,mBAAe;AAAA,EACjB,SAAS,KAAK;AACZ,UAAM,IAAI;AAAA,MACR,kKAEqB,OAAO,GAAG,CAAC;AAAA,IAClC;AAAA,EACF;AAEA,QAAM,SAAS,aAAa,EAAE,KAAK,UAAU,CAAC;AAE9C,SAAO;AAAA,IACL,MAAM,QAAQ,KAAK,OAAO,CAAC,GAAG;AAC5B,YAAM,OAAO,QAAQ,EAAE,KAAK,KAAK,CAAC;AAAA,IACpC;AAAA,IACA,QAAQ,KAAK;AAGX,aAAO,OAAO,SAAS;AACrB,cAAM,OAAO,QAAQ,EAAE,KAAK,KAAK,CAAC;AAAA,MACpC;AAAA,IACF;AAAA,IACA,MAAM,MAAM,KAAK,OAAO,CAAC,GAAG;AAC1B,YAAM,SAAS,MAAM,OAAO,QAAQ,EAAE,KAAK,KAAK,CAAC;AACjD,aAAO,QAAQ,QAAQ,CAAC;AAAA,IAC1B;AAAA,IACA,MAAM,QAAQ;AACZ,aAAO,MAAM;AAAA,IACf;AAAA,EACF;AACF;;;ACrEA,IAAM,sBAAsB;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEO,SAAS,YAAY,KAAsB;AAChD,SAAO,oBAAoB,KAAK,CAAC,MAAM,IAAI,WAAW,CAAC,CAAC;AAC1D;AAMO,IAAM,iBAAsC,OAAO,KAAK,cAAc;AAC3E,MAAI,YAAY,GAAG,EAAG,QAAO,mBAAmB,KAAK,SAAS;AAC9D,SAAO,yBAAyB,GAAG;AACrC;AAEA,eAAsB,yBACpB,KACA,WACuB;AACvB,SAAO,eAAe,KAAK,SAAS;AACtC;;;AC7BA,IAAAA,eAAmC;AAM5B,IAAM,oBAAoB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEO,SAAS,oBAAoB,OAAuB;AACzD,SAAO,8BAA8B,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAiB5C;AAEO,SAAS,eAAe,OAAuB;AACpD,QAAM,OAAO,kBAAkB,KAAK,IAAI;AACxC,QAAM,eAAe,kBAAkB,IAAI,MAAM,GAAG,EAAE,KAAK,IAAI;AAC/D,SAAO,eAAe,KAAK,KAAK,IAAI,aAAa,YAAY;AAC/D;AAMO,SAAS,WAAW,OAAkC;AAC3D,QAAM,WAAO,uBAAS,MAAM,IAAI,IAAI,MAAM,OAAO,CAAC;AAClD,QAAM,YAAQ,uBAAS,KAAK,KAAK,IAAI,KAAK,QAAQ;AAElD,SAAO;AAAA,IACL,OAAO,MAAM,cAAc,WAAW,MAAM,YAAY,KAAK,IAAI;AAAA,IACjE,MAAM,MAAM;AAAA,IACZ,MAAM,QAAQ;AAAA,IACd,MAAM,UAAU;AAAA,IAChB,MAAM,UAAU;AAAA,IAChB,MAAM,MAAM,WAAW;AAAA,IACvB,MAAM,MAAM,MAAM;AAAA,IAClB,MAAM,QAAQ,OAAO;AAAA,IACrB;AAAA,IACA,MAAM,QAAQ,YAAY;AAAA,IAC1B,KAAK,UAAU,IAAI;AAAA,IACnB,KAAK,UAAU,MAAM,WAAW,CAAC,CAAC;AAAA,IAClC,KAAK,UAAU,MAAM,WAAW,CAAC,CAAC;AAAA,EACpC;AACF;;;ACvEA,IAAAC,eAAyB;AAGlB,IAAM,OAAe,eAAgB,OAAO,EAAE,QAAQ,MAAM,OAAO,GAAG;AAC3E,QAAM,WAAW,OAAO;AACxB,QAAM,SAAqC,UAAU;AAErD,MAAI,CAAC,QAAQ;AACX,WAAO,KAAK,yBAAyB;AACrC;AAAA,EACF;AAEA,QAAM,SAAS,OAAO;AACtB,MAAI,CAAC,QAAQ;AACX,WAAO,KAAK,+BAA+B;AAC3C;AAAA,EACF;AAEA,QAAM,eAAe,MAAM,YAAY,CAAC;AACxC,QAAM,gBAAY,uBAAS,aAAa,KAAK,IAAI,aAAa,QAAQ;AAItE,QAAM,iBAAiB,aAAa,OAAO,SAAS;AACpD,QAAM,YACJ,aAAa,cAAc,OAAO,QAC9B,OAAO,QAAQ,eAAe,cAAc,CAAC,IAC7C,OAAO;AAEb,MAAI,CAAC,WAAW;AACd,WAAO,KAAK,sCAAsC;AAClD;AAAA,EACF;AAEA,QAAM,MAAM,WAAW,KAAK;AAE5B,SAAO,MAAM,iBAAiB;AAAA,IAC5B,OAAO;AAAA,IACP,WAAW,MAAM;AAAA,EACnB,CAAC;AAED,MAAI;AACF,UAAM,UAAU,GAAG;AACnB,WAAO,MAAM,0BAA0B,EAAE,OAAO,eAAe,CAAC;AAAA,EAClE,SAAS,OAAO;AACd,WAAO,MAAM,wBAAwB;AAAA,MACnC,OAAO;AAAA,MACP,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,IAC9D,CAAC;AAAA,EACH;AACF;;;AClDA,IAAAC,eAA6B;AAgBtB,IAAM,iBAAgC;AAAA,EAC3C,EAAE,MAAM,QAAQ,MAAM,QAAQ,SAAS,KAAK;AAAA,EAC5C,EAAE,MAAM,QAAQ,MAAM,OAAO;AAAA,EAC7B,EAAE,MAAM,WAAW,MAAM,OAAO;AAAA,EAChC,EAAE,MAAM,WAAW,MAAM,OAAO;AAAA,EAChC,EAAE,MAAM,UAAU,MAAM,OAAO;AAAA,EAC/B,EAAE,MAAM,QAAQ,MAAM,OAAO;AAAA,EAC7B,EAAE,MAAM,UAAU,MAAM,OAAO;AAAA,EAC/B,EAAE,MAAM,WAAW,MAAM,OAAO;AAAA,EAChC,EAAE,MAAM,MAAM,MAAM,OAAO;AAAA,EAC3B,EAAE,MAAM,WAAW,MAAM,OAAO;AAAA,EAChC,EAAE,MAAM,UAAU,MAAM,OAAO;AAAA,EAC/B,EAAE,MAAM,UAAU,MAAM,OAAO;AAAA,EAC/B,EAAE,MAAM,aAAa,MAAM,OAAO;AAAA,EAClC,EAAE,MAAM,UAAU,MAAM,UAAU;AAAA,EAClC,EAAE,MAAM,UAAU,MAAM,OAAO;AACjC;AAEO,IAAM,kBAA0C;AAAA,EACrD,cAAc;AAAA,EACd,aAAa;AAAA,EACb,cAAc;AAAA,EACd,YAAY;AACd;AAEO,IAAM,gBAAiC;AAAA,EAC5C,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,SAAS,CAAC;AACZ;AAiBA,SAASC,UAAS,OAAiC;AACjD,SAAO,OAAO,UAAU;AAC1B;AAEA,SAAS,qBAAqB,OAA6C;AACzE,MAAI,OAAO,UAAU,YAAY,UAAU,KAAM,QAAO;AACxD,QAAM,MAA+B,EAAE,GAAG,MAAM;AAChD,SAAOA,UAAS,IAAI,IAAI,KAAKA,UAAS,IAAI,IAAI;AAChD;AAEA,SAAS,kBAAkB,OAA0C;AACnE,MAAI,OAAO,UAAU,YAAY,UAAU,KAAM,QAAO;AACxD,QAAM,MAA+B,EAAE,GAAG,MAAM;AAChD,SAAOA,UAAS,IAAI,IAAI;AAC1B;AAEA,SAASC,qBAAoB,OAAe,SAAgC;AAC1E,QAAM,QAAQ,QAAQ,IAAI,CAAC,MAAM;AAC/B,UAAM,QAAkB,CAAC,EAAE,MAAM,EAAE,IAAI;AACvC,QAAI,EAAE,WAAY,OAAM,KAAK,aAAa;AAC1C,QAAI,EAAE,QAAS,OAAM,KAAK,UAAU;AACpC,WAAO,MAAM,KAAK,GAAG;AAAA,EACvB,CAAC;AACD,SAAO,8BAA8B,KAAK;AAAA,IAAS,MAAM,KAAK,OAAO,CAAC;AAAA;AACxE;AAEA,eAAe,YACb,QACA,OACkB;AAClB,QAAM,OAAO,MAAM,OAAO;AAAA,IACxB;AAAA,IACA,CAAC,KAAK;AAAA,EACR;AACA,aAAW,OAAO,MAAM;AACtB,QAAI,kBAAkB,GAAG,KAAK,IAAI,SAAS,MAAO,QAAO;AAAA,EAC3D;AACA,SAAO;AACT;AAEA,eAAe,YACb,QACA,OACA,UACA,QACe;AACf,QAAM,OAAO,MAAM,OAAO,MAAM,qBAAqB,KAAK,GAAG;AAC7D,QAAM,eAAe,oBAAI,IAAoB;AAC7C,aAAW,OAAO,MAAM;AACtB,QAAI,qBAAqB,GAAG,GAAG;AAC7B,mBAAa,IAAI,IAAI,MAAM,IAAI,KAAK,YAAY,CAAC;AAAA,IACnD;AAAA,EACF;AAEA,aAAW,eAAe,UAAU;AAClC,UAAM,aAAa,aAAa,IAAI,YAAY,IAAI;AACpD,QAAI,eAAe,QAAW;AAC5B,aAAO,KAAK,eAAe;AAAA,QACzB,OAAO,YAAY;AAAA,QACnB,UAAU,YAAY;AAAA,QACtB,QAAQ;AAAA,MACV,CAAC;AACD;AAAA,IACF;AACA,QAAI,eAAe,YAAY,KAAK,YAAY,GAAG;AACjD,aAAO,KAAK,eAAe;AAAA,QACzB,OAAO,YAAY;AAAA,QACnB,UAAU,YAAY;AAAA,QACtB,QAAQ;AAAA,MACV,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAEO,IAAM,QAAQ,OACnB,QACqC;AACrC,QAAM,EAAE,QAAQ,KAAK,OAAO,IAAI;AAChC,QAAM,aAAS,2BAAa,OAAO,OAAO,aAAa;AACvD,MAAI,CAAC,QAAQ;AACX,WAAO,MAAM,iDAAiD;AAC9D;AAAA,EACF;AACA,QAAM,UAA2B;AAAA,IAC/B,SAAS,EAAE,GAAG,iBAAiB,GAAI,OAAO,WAAW,CAAC,EAAG;AAAA,IACzD,QAAQ,OAAO,UAAU;AAAA,IACzB,SAAS,OAAO,WAAW,CAAC;AAAA,EAC9B;AAEA,QAAM,SAAS,OAAO,UAAU;AAChC,MAAI,CAAC,UAAU,CAAC,OAAO,KAAK;AAC1B,WAAO,MAAM,uCAAuC;AACpD;AAAA,EACF;AACA,QAAM,QAAQ,OAAO,SAAS;AAI9B,MAAI;AACJ,MAAI,QAAQ;AACZ,MAAI,YAAY,GAAG,KAAK,IAAI,QAAQ;AAClC,aAAS,IAAI;AACb,YAAQ;AAAA,EACV,WAAW,YAAY,GAAG,KAAK,IAAI,cAAc;AAC/C,aAAS,MAAM,IAAI,aAAa,OAAO,KAAK,OAAO,SAAS;AAAA,EAC9D,OAAO;AACL,aAAS,MAAM,yBAAyB,OAAO,KAAK,OAAO,SAAS;AAAA,EACtE;AAEA,MAAI;AAEF,UAAM,iBAA2B,CAAC;AAClC,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,QAAQ,OAAO,GAAG;AAC1D,YAAM,OAAO,QAAQ,UAAU,GAAG,MAAM,KAAK,EAAE;AAC/C,qBAAe,KAAK,GAAG;AAAA,IACzB;AAGA,UAAM,eAAe,MAAM,YAAY,QAAQ,KAAK;AAGpD,UAAM,OAAO,QAAQA,qBAAoB,OAAO,QAAQ,MAAM,CAAC;AAK/D,QAAI,cAAc;AAChB,YAAM,YAAY,QAAQ,OAAO,QAAQ,QAAQ,MAAM;AAAA,IACzD;AAGA,UAAM,iBAA2B,CAAC;AAClC,eAAW,OAAO,QAAQ,SAAS;AACjC,YAAM,SAAS,IAAI,SAAS,YAAY;AACxC,YAAM,OAAO,IAAI,QAAQ,KAAK,IAAI;AAClC,YAAM,OAAO;AAAA,QACX,UAAU,MAAM,uBAAuB,IAAI,IAAI,OAAO,KAAK,IAAI,IAAI;AAAA,MACrE;AACA,qBAAe,KAAK,IAAI,IAAI;AAAA,IAC9B;AAEA,WAAO;AAAA,MACL,cAAc,CAAC;AAAA,MACf;AAAA,MACA;AAAA,IACF;AAAA,EACF,UAAE;AACA,QAAI,OAAO;AACT,UAAI;AACF,cAAM,OAAO,MAAM;AAAA,MACrB,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AACF;;;AC1NA;;;ARwBO,IAAM,oBAAiC;AAAA,EAC5C,MAAM;AAAA,EAEN,QAAQ,CAAC;AAAA,EAET;AAAA,EAEA,MAAM,KAAK,EAAE,QAAQ,eAAe,QAAQ,KAAK,GAAG,GAAG;AACrD,UAAM,SAAS,UAAU,eAAe,MAAM;AAC9C,UAAM,WAAW,OAAO;AACxB,UAAM,SAAS,SAAS;AAGxB,QAAI,OAAO,QAAS,QAAO;AAE3B,QAAI;AAGJ,QAAI,YAAY,GAAG,GAAG;AACpB,YAAM,WAAW;AACjB,UAAI,SAAS,QAAQ;AACnB,iBAAS,SAAS;AAAA,MACpB,WAAW,SAAS,cAAc;AAChC,iBAAS,MAAM,SAAS,aAAa,OAAO,KAAK,OAAO,SAAS;AAAA,MACnE;AAAA,IACF;AAGA,QAAI,CAAC,QAAQ;AACX,UAAI;AACF,iBAAS,MAAM,yBAAyB,OAAO,KAAK,OAAO,SAAS;AAAA,MACtE,SAAS,KAAK;AACZ,eAAO;AAAA,UACL,4DAA4D,OAAO,GAAG,CAAC;AAAA,QACzE;AACA,eAAO;AAAA,MACT;AAAA,IACF;AAKA,QAAI,OAAO,mBAAmB;AAC5B,UAAI;AACF,cAAM,OAAO,QAAQ,oBAAoB,OAAO,SAAS,QAAQ,CAAC;AAAA,MACpE,SAAS,KAAK;AACZ,eAAO;AAAA,UACL,6DAA6D,OAAO,GAAG,CAAC;AAAA,QAC1E;AACA,eAAO;AAAA,MACT;AAAA,IACF,WAAW,CAAC,OAAO,kBAAkB;AAInC,UAAI,SAAS;AACb,UAAI;AACF,iBAAS,MAAM,YAAY,QAAQ,OAAO,SAAS,QAAQ;AAAA,MAC7D,SAAS,KAAK;AACZ,eAAO;AAAA,UACL,4DAA4D,OAAO,GAAG,CAAC;AAAA,QACzE;AACA,eAAO;AAAA,MACT;AACA,UAAI,CAAC,QAAQ;AACX,eAAO;AAAA,UACL,iBAAiB,OAAO,SAAS,QAAQ,kBAAkB,OAAO,GAAG,qCAChC,EAAE;AAAA,QACzC;AACA,eAAO;AAAA,MACT;AAAA,IACF;AAGA,WAAO,UAAU;AACjB,WAAO,aAAa,OAAO;AAAA,MACzB,eAAe,OAAO,SAAS,QAAQ;AAAA,IACzC;AACA,WAAO,eAAe,EAAE,YAAY,GAAG,KAAM,IAAY;AAEzD,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,KAAK,OAAO,SAAS;AACzB,WAAO,MAAM,KAAK,OAAO,OAAO;AAAA,EAClC;AAAA,EAEA,MAAM,QAAQ,EAAE,OAAO,GAAG;AACxB,UAAM,WAAW,QAAQ;AACzB,UAAM,SAAS,UAAU;AACzB,QAAI,CAAC,OAAQ;AAEb,UAAM,SAAS,OAAO;AAEtB,QAAI,UAAU,OAAO,cAAc;AACjC,UAAI;AACF,cAAM,OAAO,MAAM;AAAA,MACrB,UAAE;AACA,eAAO,UAAU;AACjB,eAAO,aAAa;AACpB,eAAO,eAAe;AAAA,MACxB;AAAA,IACF;AAAA,EACF;AACF;AAEA,IAAO,gBAAQ;","names":["import_core","import_core","import_core","isString","buildCreateTableSql"]}
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var __defProp=Object.defineProperty,__require=(x=>"undefined"!=typeof require?require:"undefined"!=typeof Proxy?new Proxy(x,{get:(a,b)=>("undefined"!=typeof require?require:a)[b]}):x)(function(x){if("undefined"!=typeof require)return require.apply(this,arguments);throw Error('Dynamic require of "'+x+'" is not supported')}),__export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})};import{isObject}from"@walkeros/core";function isSqliteEnv(env){if(!isObject(env))return!1;const maybe=env;return"function"==typeof maybe.SqliteDriver||isObject(maybe.client??null)}var LIBSQL_URL_PREFIXES=["libsql://","http://","https://","ws://","wss://"];function isLibsqlUrl(url){return LIBSQL_URL_PREFIXES.some(p=>url.startsWith(p))}var defaultFactory=async(url,authToken)=>isLibsqlUrl(url)?async function(url,authToken){let createClient;try{const loaded=__require("@libsql/client"),fn=loaded.createClient??loaded.default?.createClient;if("function"!=typeof fn)throw new Error("createClient export not found on @libsql/client");createClient=fn}catch(err){throw new Error(`@walkeros/server-destination-sqlite: @libsql/client is not installed. Install it with \`npm install @libsql/client\`, or use a local file URL. Original error: ${String(err)}`)}const client=createClient({url:url,authToken:authToken});return{async execute(sql,args=[]){await client.execute({sql:sql,args:args})},prepare:sql=>async args=>{await client.execute({sql:sql,args:args})},async query(sql,args=[]){const result=await client.execute({sql:sql,args:args});return result?.rows??[]},async close(){client.close()}}}(url,authToken):async function(filePath){let Database;try{const loaded=__require("better-sqlite3");Database="default"in loaded&&"function"==typeof loaded.default?loaded.default:loaded}catch(err){throw new Error(`@walkeros/server-destination-sqlite: better-sqlite3 is not installed. Install it with \`npm install better-sqlite3\`, or switch settings.sqlite.url to a libSQL URL. Original error: ${String(err)}`)}const db=new Database(filePath);return{async execute(sql,args=[]){db.prepare(sql).run(...args)},prepare(sql){const stmt=db.prepare(sql);return async args=>{stmt.run(...args)}},query:async(sql,args=[])=>db.prepare(sql).all(...args),async close(){db.close()}}}(url);async function createClientFromSettings(url,authToken){return defaultFactory(url,authToken)}import{isObject as isObject2,isString}from"@walkeros/core";var CANONICAL_COLUMNS=["timestamp","event_id","name","entity","action","session_id","user_id","page_url","page_title","referrer_url","data","globals","consent"];function buildCreateTableSql(table){return`CREATE TABLE IF NOT EXISTS ${table} (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n timestamp INTEGER,\n event_id TEXT,\n name TEXT,\n entity TEXT,\n action TEXT,\n session_id TEXT,\n user_id TEXT,\n page_url TEXT,\n page_title TEXT,\n referrer_url TEXT,\n data TEXT,\n globals TEXT,\n consent TEXT\n)`}function buildInsertSql(table){return`INSERT INTO ${table} (${CANONICAL_COLUMNS.join(", ")}) VALUES (${CANONICAL_COLUMNS.map(()=>"?").join(", ")})`}function eventToRow(event){const data=isObject2(event.data)?event.data:{},title=isString(data.title)?data.title:"";return["number"==typeof event.timestamp?event.timestamp:Date.now(),event.id??"",event.name??"",event.entity??"",event.action??"",event.user?.session??"",event.user?.id??"",event.source?.url??"",title,event.source?.referrer??"",JSON.stringify(data),JSON.stringify(event.globals??{}),JSON.stringify(event.consent??{})]}import{isString as isString2}from"@walkeros/core";import{resolveSetup}from"@walkeros/core";var DEFAULT_SCHEMA=[{name:"name",type:"TEXT",notNull:!0},{name:"data",type:"TEXT"},{name:"context",type:"TEXT"},{name:"globals",type:"TEXT"},{name:"custom",type:"TEXT"},{name:"user",type:"TEXT"},{name:"nested",type:"TEXT"},{name:"consent",type:"TEXT"},{name:"id",type:"TEXT"},{name:"trigger",type:"TEXT"},{name:"entity",type:"TEXT"},{name:"action",type:"TEXT"},{name:"timestamp",type:"TEXT"},{name:"timing",type:"INTEGER"},{name:"source",type:"TEXT"}],DEFAULT_PRAGMAS={journal_mode:"WAL",synchronous:"NORMAL",foreign_keys:"ON",temp_store:"MEMORY"},DEFAULT_SETUP={pragmas:DEFAULT_PRAGMAS,schema:DEFAULT_SCHEMA,indexes:[]};function isString3(value){return"string"==typeof value}function isPragmaTableInfoRow(value){if("object"!=typeof value||null===value)return!1;const obj={...value};return isString3(obj.name)&&isString3(obj.type)}function isSqliteMasterRow(value){if("object"!=typeof value||null===value)return!1;return isString3({...value}.name)}async function tableExists(client,table){const rows=await client.query("SELECT name FROM sqlite_master WHERE type='table' AND name=?",[table]);for(const row of rows)if(isSqliteMasterRow(row)&&row.name===table)return!0;return!1}var setup=async ctx=>{const{config:config,env:env,logger:logger}=ctx,merged=resolveSetup(config.setup,DEFAULT_SETUP);if(!merged)return void logger.debug("setup: skipped (config.setup is false or unset)");const options={pragmas:{...DEFAULT_PRAGMAS,...merged.pragmas??{}},schema:merged.schema??DEFAULT_SCHEMA,indexes:merged.indexes??[]},sqlite=config.settings?.sqlite;if(!sqlite||!sqlite.url)return void logger.throw("setup: settings.sqlite.url is missing");const table=sqlite.table??"events";let client,owned=!0;isSqliteEnv(env)&&env.client?(client=env.client,owned=!1):client=isSqliteEnv(env)&&env.SqliteDriver?await env.SqliteDriver(sqlite.url,sqlite.authToken):await createClientFromSettings(sqlite.url,sqlite.authToken);try{const pragmasApplied=[];for(const[key,value]of Object.entries(options.pragmas))await client.execute(`PRAGMA ${key} = ${value}`),pragmasApplied.push(key);const tableExisted=await tableExists(client,table);await client.execute(function(table,columns){return`CREATE TABLE IF NOT EXISTS ${table} (\n ${columns.map(c=>{const parts=[c.name,c.type];return c.primaryKey&&parts.push("PRIMARY KEY"),c.notNull&&parts.push("NOT NULL"),parts.join(" ")}).join(",\n ")}\n)`}(table,options.schema)),tableExisted&&await async function(client,table,declared,logger){const rows=await client.query(`PRAGMA table_info(${table})`),actualByName=new Map;for(const row of rows)isPragmaTableInfoRow(row)&&actualByName.set(row.name,row.type.toUpperCase());for(const declaredCol of declared){const actualType=actualByName.get(declaredCol.name);void 0!==actualType?actualType!==declaredCol.type.toUpperCase()&&logger.warn("setup.drift",{field:declaredCol.name,declared:declaredCol.type,actual:actualType}):logger.warn("setup.drift",{field:declaredCol.name,declared:declaredCol.type,actual:"missing"})}}(client,table,options.schema,logger);const indexesCreated=[];for(const idx of options.indexes){const unique=idx.unique?"UNIQUE ":"",cols=idx.columns.join(", ");await client.execute(`CREATE ${unique}INDEX IF NOT EXISTS ${idx.name} ON ${table}(${cols})`),indexesCreated.push(idx.name)}return{tableCreated:!tableExisted,pragmasApplied:pragmasApplied,indexesCreated:indexesCreated}}finally{if(owned)try{await client.close()}catch{}}},types_exports={},examples_exports={};__export(examples_exports,{env:()=>env_exports,step:()=>step_exports});var env_exports={};__export(env_exports,{push:()=>push2,simulation:()=>simulation});var mockClient={execute:()=>Promise.resolve(),prepare:()=>()=>Promise.resolve(),query:()=>Promise.resolve([]),close:()=>Promise.resolve()},push2={SqliteDriver:()=>Promise.resolve(mockClient)},simulation=["call:client.prepare","call:client.execute"],step_exports={};__export(step_exports,{customTable:()=>customTable,defaultInsert:()=>defaultInsert,ignoredEvent:()=>ignoredEvent,orderComplete:()=>orderComplete,tableOverride:()=>tableOverride});import{getEvent}from"@walkeros/core";var defaultInsert={title:"Default insert",description:"A walker event is inserted into the default events table with canonical columns and JSON-encoded sections.",in:getEvent("page view",{timestamp:1700000100,id:"evt-1",user:{session:"sess-1",id:"user-42"},data:{title:"Home"},source:{type:"browser",platform:"web",url:"https://example.com/",referrer:"https://example.com/prev"},globals:{env:"prod"},consent:{analytics:!0}}),out:[["client.runInsert",[1700000100,"evt-1","page view","page","view","sess-1","user-42","https://example.com/","Home","https://example.com/prev",JSON.stringify({title:"Home"}),JSON.stringify({env:"prod"}),JSON.stringify({analytics:!0})]]]},customTable={title:"Custom table",description:"A destination-level table setting inserts events into a custom SQLite table with the same column layout.",in:getEvent("form submit",{timestamp:1700000101,id:"evt-2",user:{session:"sess-99",id:""},data:{type:"contact"},source:{type:"browser",platform:"web",url:"https://example.com/contact"},globals:{},consent:{}}),settings:{sqlite:{url:":memory:",table:"siteEvents"}},out:[["client.runInsert",[1700000101,"evt-2","form submit","form","submit","sess-99","","https://example.com/contact","","",JSON.stringify({type:"contact"}),JSON.stringify({}),JSON.stringify({})]]]},orderComplete={title:"Order insert",description:"An order complete is inserted with numeric data serialized as JSON in the data column.",in:getEvent("order complete",{timestamp:1700000102,id:"evt-3",user:{session:"",id:""},data:{id:"ORD-1",total:99},source:{type:"collector",schema:"4"},globals:{},consent:{}}),out:[["client.runInsert",[1700000102,"evt-3","order complete","order","complete","","","","","",JSON.stringify({id:"ORD-1",total:99}),JSON.stringify({}),JSON.stringify({})]]]},tableOverride={title:"Table override",description:"A mapping rule overrides the target table so specific events are inserted into a dedicated SQLite table.",in:getEvent("order complete",{timestamp:1700000103,id:"evt-4",user:{session:"",id:""},data:{id:"ORD-2",total:42},source:{type:"collector",schema:"4"},globals:{},consent:{}}),mapping:{settings:{table:"orders"}},out:[["client.runInsert",[1700000103,"evt-4","order complete","order","complete","","","","","",JSON.stringify({id:"ORD-2",total:42}),JSON.stringify({}),JSON.stringify({})]]]},ignoredEvent={public:!1,in:getEvent("debug noise",{timestamp:1700000104,id:"evt-5",source:{type:"collector",schema:"4"}}),mapping:{ignore:!0},out:[]},destinationSQLite={type:"sqlite",config:{},setup:setup,async init({config:partialConfig,logger:logger,env:env,id:id}){const config=function(partialConfig={},logger){const sqlite=(partialConfig.settings??{}).sqlite??{};sqlite.url||logger.throw("Config settings sqlite.url missing");const sqliteSettings={...sqlite,url:sqlite.url,table:sqlite.table??"events"};void 0!==sqlite.schema?(logger.warn('config.settings.sqlite.schema is deprecated; use config.setup instead. "schema: auto" maps to "setup: true" (run "walkeros setup destination.<id>"); "schema: manual" maps to "setup: false". This field is removed in the next major.'),"manual"!==sqlite.schema||partialConfig.mapping||logger.throw('Config settings sqlite.schema="manual" requires a mapping (no default column mapping will be used).'),sqliteSettings._legacyAutoCreate="auto"===sqlite.schema,sqliteSettings._legacySkipProbe="manual"===sqlite.schema):(sqliteSettings._legacyAutoCreate=!1,sqliteSettings._legacySkipProbe=!1),delete sqliteSettings.schema;const settings={sqlite:sqliteSettings};return{...partialConfig,settings:settings}}(partialConfig,logger),sqlite=config.settings.sqlite;if(sqlite._client)return config;let client;if(isSqliteEnv(env)){const envTyped=env;envTyped.client?client=envTyped.client:envTyped.SqliteDriver&&(client=await envTyped.SqliteDriver(sqlite.url,sqlite.authToken))}if(!client)try{client=await createClientFromSettings(sqlite.url,sqlite.authToken)}catch(err){return logger.throw(`@walkeros/server-destination-sqlite: driver load failed: ${String(err)}`),config}if(sqlite._legacyAutoCreate)try{await client.execute(buildCreateTableSql(sqlite.table??"events"))}catch(err){return logger.throw(`@walkeros/server-destination-sqlite: CREATE TABLE failed: ${String(err)}`),config}else if(!sqlite._legacySkipProbe){let exists=!1;try{exists=await tableExists(client,sqlite.table??"events")}catch(err){return logger.throw(`@walkeros/server-destination-sqlite: table probe failed: ${String(err)}`),config}if(!exists)return logger.throw(`SQLite table "${sqlite.table??"events"}" not found in ${sqlite.url}. Run "walkeros setup destination.${id}" to create it.`),config}return sqlite._client=client,sqlite._runInsert=client.prepare(buildInsertSql(sqlite.table??"events")),sqlite._ownedClient=!(isSqliteEnv(env)&&env.client),config},push:async(event,context)=>await async function(event,{config:config,rule:rule,logger:logger}){const settings=config.settings,sqlite=settings?.sqlite;if(!sqlite)return void logger.warn("SQLite settings missing");const client=sqlite._client;if(!client)return void logger.warn("SQLite client not initialized");const ruleSettings=rule?.settings??{},ruleTable=isString2(ruleSettings.table)?ruleSettings.table:"",effectiveTable=ruleTable||sqlite.table||"events",runInsert=ruleTable&&ruleTable!==sqlite.table?client.prepare(buildInsertSql(effectiveTable)):sqlite._runInsert;if(!runInsert)return void logger.warn("SQLite insert statement not prepared");const row=eventToRow(event);logger.debug("SQLite INSERT",{table:effectiveTable,eventName:event.name});try{await runInsert(row),logger.debug("SQLite INSERT complete",{table:effectiveTable})}catch(error){logger.error("SQLite INSERT failed",{table:effectiveTable,error:error instanceof Error?error.message:String(error)})}}(event,context),async destroy({config:config}){const settings=config?.settings,sqlite=settings?.sqlite;if(!sqlite)return;const client=sqlite._client;if(client&&sqlite._ownedClient)try{await client.close()}finally{sqlite._client=void 0,sqlite._runInsert=void 0,sqlite._ownedClient=void 0}}},index_default=destinationSQLite;export{CANONICAL_COLUMNS,DEFAULT_PRAGMAS,DEFAULT_SCHEMA,DEFAULT_SETUP,types_exports as DestinationSQLite,buildCreateTableSql,buildInsertSql,index_default as default,destinationSQLite,eventToRow,examples_exports as examples,isLibsqlUrl,setup};//# sourceMappingURL=index.mjs.map
|
|
1
|
+
var __require=(x=>"undefined"!=typeof require?require:"undefined"!=typeof Proxy?new Proxy(x,{get:(a,b)=>("undefined"!=typeof require?require:a)[b]}):x)(function(x){if("undefined"!=typeof require)return require.apply(this,arguments);throw Error('Dynamic require of "'+x+'" is not supported')});import{isObject}from"@walkeros/core";function isSqliteEnv(env){if(!isObject(env))return!1;const maybe=env;return"function"==typeof maybe.SqliteDriver||isObject(maybe.client??null)}var LIBSQL_URL_PREFIXES=["libsql://","http://","https://","ws://","wss://"];function isLibsqlUrl(url){return LIBSQL_URL_PREFIXES.some(p=>url.startsWith(p))}var defaultFactory=async(url,authToken)=>isLibsqlUrl(url)?async function(url,authToken){let createClient;try{const loaded=__require("@libsql/client"),fn=loaded.createClient??loaded.default?.createClient;if("function"!=typeof fn)throw new Error("createClient export not found on @libsql/client");createClient=fn}catch(err){throw new Error(`@walkeros/server-destination-sqlite: @libsql/client is not installed. Install it with \`npm install @libsql/client\`, or use a local file URL. Original error: ${String(err)}`)}const client=createClient({url:url,authToken:authToken});return{async execute(sql,args=[]){await client.execute({sql:sql,args:args})},prepare:sql=>async args=>{await client.execute({sql:sql,args:args})},async query(sql,args=[]){const result=await client.execute({sql:sql,args:args});return result?.rows??[]},async close(){client.close()}}}(url,authToken):async function(filePath){let Database;try{const loaded=__require("better-sqlite3");Database="default"in loaded&&"function"==typeof loaded.default?loaded.default:loaded}catch(err){throw new Error(`@walkeros/server-destination-sqlite: better-sqlite3 is not installed. Install it with \`npm install better-sqlite3\`, or switch settings.sqlite.url to a libSQL URL. Original error: ${String(err)}`)}const db=new Database(filePath);return{async execute(sql,args=[]){db.prepare(sql).run(...args)},prepare(sql){const stmt=db.prepare(sql);return async args=>{stmt.run(...args)}},query:async(sql,args=[])=>db.prepare(sql).all(...args),async close(){db.close()}}}(url);async function createClientFromSettings(url,authToken){return defaultFactory(url,authToken)}import{isObject as isObject2,isString}from"@walkeros/core";var CANONICAL_COLUMNS=["timestamp","event_id","name","entity","action","session_id","user_id","page_url","page_title","referrer_url","data","globals","consent"];function buildCreateTableSql(table){return`CREATE TABLE IF NOT EXISTS ${table} (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n timestamp INTEGER,\n event_id TEXT,\n name TEXT,\n entity TEXT,\n action TEXT,\n session_id TEXT,\n user_id TEXT,\n page_url TEXT,\n page_title TEXT,\n referrer_url TEXT,\n data TEXT,\n globals TEXT,\n consent TEXT\n)`}function buildInsertSql(table){return`INSERT INTO ${table} (${CANONICAL_COLUMNS.join(", ")}) VALUES (${CANONICAL_COLUMNS.map(()=>"?").join(", ")})`}function eventToRow(event){const data=isObject2(event.data)?event.data:{},title=isString(data.title)?data.title:"";return["number"==typeof event.timestamp?event.timestamp:Date.now(),event.id??"",event.name??"",event.entity??"",event.action??"",event.user?.session??"",event.user?.id??"",event.source?.url??"",title,event.source?.referrer??"",JSON.stringify(data),JSON.stringify(event.globals??{}),JSON.stringify(event.consent??{})]}import{isString as isString2}from"@walkeros/core";import{resolveSetup}from"@walkeros/core";var DEFAULT_SCHEMA=[{name:"name",type:"TEXT",notNull:!0},{name:"data",type:"TEXT"},{name:"context",type:"TEXT"},{name:"globals",type:"TEXT"},{name:"custom",type:"TEXT"},{name:"user",type:"TEXT"},{name:"nested",type:"TEXT"},{name:"consent",type:"TEXT"},{name:"id",type:"TEXT"},{name:"trigger",type:"TEXT"},{name:"entity",type:"TEXT"},{name:"action",type:"TEXT"},{name:"timestamp",type:"TEXT"},{name:"timing",type:"INTEGER"},{name:"source",type:"TEXT"}],DEFAULT_PRAGMAS={journal_mode:"WAL",synchronous:"NORMAL",foreign_keys:"ON",temp_store:"MEMORY"},DEFAULT_SETUP={pragmas:DEFAULT_PRAGMAS,schema:DEFAULT_SCHEMA,indexes:[]};function isString3(value){return"string"==typeof value}function isPragmaTableInfoRow(value){if("object"!=typeof value||null===value)return!1;const obj={...value};return isString3(obj.name)&&isString3(obj.type)}function isSqliteMasterRow(value){if("object"!=typeof value||null===value)return!1;return isString3({...value}.name)}async function tableExists(client,table){const rows=await client.query("SELECT name FROM sqlite_master WHERE type='table' AND name=?",[table]);for(const row of rows)if(isSqliteMasterRow(row)&&row.name===table)return!0;return!1}var setup=async ctx=>{const{config:config,env:env,logger:logger}=ctx,merged=resolveSetup(config.setup,DEFAULT_SETUP);if(!merged)return void logger.debug("setup: skipped (config.setup is false or unset)");const options={pragmas:{...DEFAULT_PRAGMAS,...merged.pragmas??{}},schema:merged.schema??DEFAULT_SCHEMA,indexes:merged.indexes??[]},sqlite=config.settings?.sqlite;if(!sqlite||!sqlite.url)return void logger.throw("setup: settings.sqlite.url is missing");const table=sqlite.table??"events";let client,owned=!0;isSqliteEnv(env)&&env.client?(client=env.client,owned=!1):client=isSqliteEnv(env)&&env.SqliteDriver?await env.SqliteDriver(sqlite.url,sqlite.authToken):await createClientFromSettings(sqlite.url,sqlite.authToken);try{const pragmasApplied=[];for(const[key,value]of Object.entries(options.pragmas))await client.execute(`PRAGMA ${key} = ${value}`),pragmasApplied.push(key);const tableExisted=await tableExists(client,table);await client.execute(function(table,columns){return`CREATE TABLE IF NOT EXISTS ${table} (\n ${columns.map(c=>{const parts=[c.name,c.type];return c.primaryKey&&parts.push("PRIMARY KEY"),c.notNull&&parts.push("NOT NULL"),parts.join(" ")}).join(",\n ")}\n)`}(table,options.schema)),tableExisted&&await async function(client,table,declared,logger){const rows=await client.query(`PRAGMA table_info(${table})`),actualByName=new Map;for(const row of rows)isPragmaTableInfoRow(row)&&actualByName.set(row.name,row.type.toUpperCase());for(const declaredCol of declared){const actualType=actualByName.get(declaredCol.name);void 0!==actualType?actualType!==declaredCol.type.toUpperCase()&&logger.warn("setup.drift",{field:declaredCol.name,declared:declaredCol.type,actual:actualType}):logger.warn("setup.drift",{field:declaredCol.name,declared:declaredCol.type,actual:"missing"})}}(client,table,options.schema,logger);const indexesCreated=[];for(const idx of options.indexes){const unique=idx.unique?"UNIQUE ":"",cols=idx.columns.join(", ");await client.execute(`CREATE ${unique}INDEX IF NOT EXISTS ${idx.name} ON ${table}(${cols})`),indexesCreated.push(idx.name)}return{tableCreated:!tableExisted,pragmasApplied:pragmasApplied,indexesCreated:indexesCreated}}finally{if(owned)try{await client.close()}catch{}}},types_exports={},destinationSQLite={type:"sqlite",config:{},setup:setup,async init({config:partialConfig,logger:logger,env:env,id:id}){const config=function(partialConfig={},logger){const sqlite=(partialConfig.settings??{}).sqlite??{};sqlite.url||logger.throw("Config settings sqlite.url missing");const sqliteSettings={...sqlite,url:sqlite.url,table:sqlite.table??"events"};void 0!==sqlite.schema?(logger.warn('config.settings.sqlite.schema is deprecated; use config.setup instead. "schema: auto" maps to "setup: true" (run "walkeros setup destination.<id>"); "schema: manual" maps to "setup: false". This field is removed in the next major.'),"manual"!==sqlite.schema||partialConfig.mapping||logger.throw('Config settings sqlite.schema="manual" requires a mapping (no default column mapping will be used).'),sqliteSettings._legacyAutoCreate="auto"===sqlite.schema,sqliteSettings._legacySkipProbe="manual"===sqlite.schema):(sqliteSettings._legacyAutoCreate=!1,sqliteSettings._legacySkipProbe=!1),delete sqliteSettings.schema;const settings={sqlite:sqliteSettings};return{...partialConfig,settings:settings}}(partialConfig,logger),sqlite=config.settings.sqlite;if(sqlite._client)return config;let client;if(isSqliteEnv(env)){const envTyped=env;envTyped.client?client=envTyped.client:envTyped.SqliteDriver&&(client=await envTyped.SqliteDriver(sqlite.url,sqlite.authToken))}if(!client)try{client=await createClientFromSettings(sqlite.url,sqlite.authToken)}catch(err){return logger.throw(`@walkeros/server-destination-sqlite: driver load failed: ${String(err)}`),config}if(sqlite._legacyAutoCreate)try{await client.execute(buildCreateTableSql(sqlite.table??"events"))}catch(err){return logger.throw(`@walkeros/server-destination-sqlite: CREATE TABLE failed: ${String(err)}`),config}else if(!sqlite._legacySkipProbe){let exists=!1;try{exists=await tableExists(client,sqlite.table??"events")}catch(err){return logger.throw(`@walkeros/server-destination-sqlite: table probe failed: ${String(err)}`),config}if(!exists)return logger.throw(`SQLite table "${sqlite.table??"events"}" not found in ${sqlite.url}. Run "walkeros setup destination.${id}" to create it.`),config}return sqlite._client=client,sqlite._runInsert=client.prepare(buildInsertSql(sqlite.table??"events")),sqlite._ownedClient=!(isSqliteEnv(env)&&env.client),config},push:async(event,context)=>await async function(event,{config:config,rule:rule,logger:logger}){const settings=config.settings,sqlite=settings?.sqlite;if(!sqlite)return void logger.warn("SQLite settings missing");const client=sqlite._client;if(!client)return void logger.warn("SQLite client not initialized");const ruleSettings=rule?.settings??{},ruleTable=isString2(ruleSettings.table)?ruleSettings.table:"",effectiveTable=ruleTable||sqlite.table||"events",runInsert=ruleTable&&ruleTable!==sqlite.table?client.prepare(buildInsertSql(effectiveTable)):sqlite._runInsert;if(!runInsert)return void logger.warn("SQLite insert statement not prepared");const row=eventToRow(event);logger.debug("SQLite INSERT",{table:effectiveTable,eventName:event.name});try{await runInsert(row),logger.debug("SQLite INSERT complete",{table:effectiveTable})}catch(error){logger.error("SQLite INSERT failed",{table:effectiveTable,error:error instanceof Error?error.message:String(error)})}}(event,context),async destroy({config:config}){const settings=config?.settings,sqlite=settings?.sqlite;if(!sqlite)return;const client=sqlite._client;if(client&&sqlite._ownedClient)try{await client.close()}finally{sqlite._client=void 0,sqlite._runInsert=void 0,sqlite._ownedClient=void 0}}},index_default=destinationSQLite;export{CANONICAL_COLUMNS,DEFAULT_PRAGMAS,DEFAULT_SCHEMA,DEFAULT_SETUP,types_exports as DestinationSQLite,buildCreateTableSql,buildInsertSql,index_default as default,destinationSQLite,eventToRow,isLibsqlUrl,setup};//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/config.ts","../src/drivers/betterSqlite3.ts","../src/drivers/libsql.ts","../src/drivers/index.ts","../src/serialize.ts","../src/push.ts","../src/setup.ts","../src/types/index.ts","../src/examples/index.ts","../src/examples/env.ts","../src/examples/step.ts","../src/index.ts"],"sourcesContent":["import type {\n Config,\n Env,\n PartialConfig,\n Settings,\n SqliteSettings,\n} from './types';\nimport type { Logger } from '@walkeros/core';\nimport { isObject } from '@walkeros/core';\n\nexport function getConfig(\n partialConfig: PartialConfig = {},\n logger: Logger.Instance,\n): Config {\n const raw = (partialConfig.settings ?? {}) as Partial<Settings>;\n const sqlite: Partial<SqliteSettings> =\n raw.sqlite ?? ({} as Partial<SqliteSettings>);\n\n if (!sqlite.url) {\n logger.throw('Config settings sqlite.url missing');\n }\n\n const sqliteSettings: SqliteSettings = {\n ...sqlite,\n url: sqlite.url as string,\n table: sqlite.table ?? 'events',\n };\n\n // Migration shim: settings.sqlite.schema is the predecessor of config.setup.\n // Accept BOTH for one minor cycle, emit a deprecation WARN if `schema` is\n // used. Removed in the next major.\n if (sqlite.schema !== undefined) {\n logger.warn(\n 'config.settings.sqlite.schema is deprecated; use config.setup instead. ' +\n '\"schema: auto\" maps to \"setup: true\" (run \"walkeros setup destination.<id>\"); ' +\n '\"schema: manual\" maps to \"setup: false\". This field is removed in the next major.',\n );\n if (sqlite.schema === 'manual' && !partialConfig.mapping) {\n // Preserve the existing throw for the legacy \"manual without mapping\" case.\n logger.throw(\n 'Config settings sqlite.schema=\"manual\" requires a mapping (no default column mapping will be used).',\n );\n }\n sqliteSettings._legacyAutoCreate = sqlite.schema === 'auto';\n // Legacy `schema: 'manual'` opted out of the auto-create AND opted out of\n // the table-existence probe (user brings their own table). Honor that.\n sqliteSettings._legacySkipProbe = sqlite.schema === 'manual';\n } else {\n // No legacy schema flag. Default behavior: do NOT auto-create in init().\n // The user must run `walkeros setup destination.<id>` or set `config.setup: true`.\n sqliteSettings._legacyAutoCreate = false;\n sqliteSettings._legacySkipProbe = false;\n }\n\n // Strip the deprecated key from the live settings object so downstream code\n // never reads it. The flag is preserved on `_legacyAutoCreate`.\n delete sqliteSettings.schema;\n\n const settings: Settings = { sqlite: sqliteSettings };\n\n return { ...partialConfig, settings };\n}\n\nexport function isSqliteEnv(env: unknown): env is Env {\n if (!isObject(env)) return false;\n const maybe = env as { SqliteDriver?: unknown; client?: unknown };\n return (\n typeof maybe.SqliteDriver === 'function' || isObject(maybe.client ?? null)\n );\n}\n","import type { SqliteClient } from '../types';\n\n/**\n * better-sqlite3 driver -- synchronous native SQLite for local files and ':memory:'.\n * Wrapped in Promise.resolve so the cross-driver interface is always async.\n *\n * The library is loaded via dynamic require to allow tests to mock via\n * jest.mock('better-sqlite3'), and to keep it as an optional peer dependency.\n */\ninterface BetterSqliteStatement {\n run: (...args: unknown[]) => unknown;\n all: (...args: unknown[]) => ReadonlyArray<Record<string, unknown>>;\n}\n\ninterface BetterSqliteDatabase {\n prepare: (sql: string) => BetterSqliteStatement;\n close: () => void;\n}\n\ninterface BetterSqliteConstructor {\n new (filePath: string): BetterSqliteDatabase;\n}\n\nexport async function createBetterSqliteClient(\n filePath: string,\n): Promise<SqliteClient> {\n let Database: BetterSqliteConstructor;\n try {\n // Dynamic require so the module stays an optional peer dep and tests can mock.\n const loaded = require('better-sqlite3') as\n | BetterSqliteConstructor\n | { default: BetterSqliteConstructor };\n Database =\n 'default' in loaded && typeof loaded.default === 'function'\n ? loaded.default\n : (loaded as BetterSqliteConstructor);\n } catch (err) {\n throw new Error(\n '@walkeros/server-destination-sqlite: better-sqlite3 is not installed. ' +\n 'Install it with `npm install better-sqlite3`, or switch settings.sqlite.url to a libSQL URL. ' +\n `Original error: ${String(err)}`,\n );\n }\n\n const db = new Database(filePath);\n\n return {\n async execute(sql, args = []) {\n db.prepare(sql).run(...(args as unknown[]));\n },\n prepare(sql) {\n const stmt = db.prepare(sql);\n return async (args) => {\n stmt.run(...(args as unknown[]));\n };\n },\n async query(sql, args = []) {\n return db.prepare(sql).all(...(args as unknown[]));\n },\n async close() {\n db.close();\n },\n };\n}\n","import type { SqliteClient } from '../types';\n\n/**\n * libSQL / Turso driver -- async HTTP(S)/WSS/local file driver.\n *\n * Loaded via dynamic require to stay an optional peer dep and let tests mock\n * via jest.mock('@libsql/client').\n */\ninterface LibsqlExecuteArgs {\n sql: string;\n args?: ReadonlyArray<unknown>;\n}\n\ninterface LibsqlResultSet {\n rows?: ReadonlyArray<Record<string, unknown>>;\n}\n\ninterface LibsqlClient {\n execute: (input: LibsqlExecuteArgs) => Promise<LibsqlResultSet>;\n close: () => void;\n}\n\ninterface LibsqlCreateClientConfig {\n url: string;\n authToken?: string;\n}\n\ntype CreateClientFn = (config: LibsqlCreateClientConfig) => LibsqlClient;\n\nexport async function createLibsqlClient(\n url: string,\n authToken?: string,\n): Promise<SqliteClient> {\n let createClient: CreateClientFn;\n try {\n const loaded = require('@libsql/client') as {\n createClient?: CreateClientFn;\n default?: { createClient?: CreateClientFn };\n };\n const fn = loaded.createClient ?? loaded.default?.createClient;\n if (typeof fn !== 'function') {\n throw new Error('createClient export not found on @libsql/client');\n }\n createClient = fn;\n } catch (err) {\n throw new Error(\n '@walkeros/server-destination-sqlite: @libsql/client is not installed. ' +\n 'Install it with `npm install @libsql/client`, or use a local file URL. ' +\n `Original error: ${String(err)}`,\n );\n }\n\n const client = createClient({ url, authToken });\n\n return {\n async execute(sql, args = []) {\n await client.execute({ sql, args });\n },\n prepare(sql) {\n // libSQL has no server-side prepared-statement cache for HTTP; we close over\n // the sql string and call execute() per run. Cost is negligible vs network RTT.\n return async (args) => {\n await client.execute({ sql, args });\n };\n },\n async query(sql, args = []) {\n const result = await client.execute({ sql, args });\n return result?.rows ?? [];\n },\n async close() {\n client.close();\n },\n };\n}\n","import type { SqliteClient, SqliteClientFactory } from '../types';\nimport { createBetterSqliteClient } from './betterSqlite3';\nimport { createLibsqlClient } from './libsql';\n\nconst LIBSQL_URL_PREFIXES = [\n 'libsql://',\n 'http://',\n 'https://',\n 'ws://',\n 'wss://',\n];\n\nexport function isLibsqlUrl(url: string): boolean {\n return LIBSQL_URL_PREFIXES.some((p) => url.startsWith(p));\n}\n\n/**\n * Default client factory -- picks better-sqlite3 for local file paths and\n * libSQL for remote URLs. Tests can override via env.SqliteDriver.\n */\nexport const defaultFactory: SqliteClientFactory = async (url, authToken) => {\n if (isLibsqlUrl(url)) return createLibsqlClient(url, authToken);\n return createBetterSqliteClient(url);\n};\n\nexport async function createClientFromSettings(\n url: string,\n authToken: string | undefined,\n): Promise<SqliteClient> {\n return defaultFactory(url, authToken);\n}\n\nexport { createBetterSqliteClient, createLibsqlClient };\n","import type { WalkerOS } from '@walkeros/core';\nimport { isObject, isString } from '@walkeros/core';\n\n/**\n * Canonical columns for the auto-created events table. Order matters -- used\n * to build both CREATE TABLE and the prepared INSERT.\n */\nexport const CANONICAL_COLUMNS = [\n 'timestamp',\n 'event_id',\n 'name',\n 'entity',\n 'action',\n 'session_id',\n 'user_id',\n 'page_url',\n 'page_title',\n 'referrer_url',\n 'data',\n 'globals',\n 'consent',\n] as const;\n\nexport function buildCreateTableSql(table: string): string {\n return `CREATE TABLE IF NOT EXISTS ${table} (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n timestamp INTEGER,\n event_id TEXT,\n name TEXT,\n entity TEXT,\n action TEXT,\n session_id TEXT,\n user_id TEXT,\n page_url TEXT,\n page_title TEXT,\n referrer_url TEXT,\n data TEXT,\n globals TEXT,\n consent TEXT\n)`;\n}\n\nexport function buildInsertSql(table: string): string {\n const cols = CANONICAL_COLUMNS.join(', ');\n const placeholders = CANONICAL_COLUMNS.map(() => '?').join(', ');\n return `INSERT INTO ${table} (${cols}) VALUES (${placeholders})`;\n}\n\n/**\n * Map a walkerOS event onto the canonical column order. JSON blobs are\n * stringified. Missing fields become empty strings / zeros.\n */\nexport function eventToRow(event: WalkerOS.Event): unknown[] {\n const data = isObject(event.data) ? event.data : {};\n const title = isString(data.title) ? data.title : '';\n\n return [\n typeof event.timestamp === 'number' ? event.timestamp : Date.now(),\n event.id ?? '',\n event.name ?? '',\n event.entity ?? '',\n event.action ?? '',\n event.user?.session ?? '',\n event.user?.id ?? '',\n event.source?.url ?? '',\n title,\n event.source?.referrer ?? '',\n JSON.stringify(data),\n JSON.stringify(event.globals ?? {}),\n JSON.stringify(event.consent ?? {}),\n ];\n}\n","import type { PushFn, SqliteSettings } from './types';\nimport { isString } from '@walkeros/core';\nimport { buildInsertSql, eventToRow } from './serialize';\n\nexport const push: PushFn = async function (event, { config, rule, logger }) {\n const settings = config.settings as { sqlite?: SqliteSettings } | undefined;\n const sqlite: SqliteSettings | undefined = settings?.sqlite;\n\n if (!sqlite) {\n logger.warn('SQLite settings missing');\n return;\n }\n\n const client = sqlite._client;\n if (!client) {\n logger.warn('SQLite client not initialized');\n return;\n }\n\n const ruleSettings = rule?.settings ?? {};\n const ruleTable = isString(ruleSettings.table) ? ruleSettings.table : '';\n\n // If the rule overrides the table, we can't reuse the cached prepared statement\n // (which was bound to the default table). Build an ad-hoc prepared run.\n const effectiveTable = ruleTable || sqlite.table || 'events';\n const runInsert =\n ruleTable && ruleTable !== sqlite.table\n ? client.prepare(buildInsertSql(effectiveTable))\n : sqlite._runInsert;\n\n if (!runInsert) {\n logger.warn('SQLite insert statement not prepared');\n return;\n }\n\n const row = eventToRow(event);\n\n logger.debug('SQLite INSERT', {\n table: effectiveTable,\n eventName: event.name,\n });\n\n try {\n await runInsert(row);\n logger.debug('SQLite INSERT complete', { table: effectiveTable });\n } catch (error) {\n logger.error('SQLite INSERT failed', {\n table: effectiveTable,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n};\n","import type { LifecycleContext, Logger } from '@walkeros/core';\nimport { resolveSetup } from '@walkeros/core';\nimport type { DestinationServer } from '@walkeros/server-core';\nimport type { Env, Setup, SetupColumn, SqliteClient, Types } from './types';\nimport { isSqliteEnv } from './config';\nimport { createClientFromSettings } from './drivers';\n\n// Setup is wired to the destination's `setup` slot which uses the broader\n// `DestinationServer.Config<Types>` (settings is optional). We runtime-narrow\n// instead of using the local Config alias so the assignment in index.ts\n// type-checks without contravariance issues.\ntype WideConfig = DestinationServer.Config<Types>;\n\n/**\n * Default 15-column walkerOS Event v4 canonical schema.\n * Mirrors BigQuery (Plan 2). Only `name` is REQUIRED.\n */\nexport const DEFAULT_SCHEMA: SetupColumn[] = [\n { name: 'name', type: 'TEXT', notNull: true },\n { name: 'data', type: 'TEXT' },\n { name: 'context', type: 'TEXT' },\n { name: 'globals', type: 'TEXT' },\n { name: 'custom', type: 'TEXT' },\n { name: 'user', type: 'TEXT' },\n { name: 'nested', type: 'TEXT' },\n { name: 'consent', type: 'TEXT' },\n { name: 'id', type: 'TEXT' },\n { name: 'trigger', type: 'TEXT' },\n { name: 'entity', type: 'TEXT' },\n { name: 'action', type: 'TEXT' },\n { name: 'timestamp', type: 'TEXT' },\n { name: 'timing', type: 'INTEGER' },\n { name: 'source', type: 'TEXT' },\n];\n\nexport const DEFAULT_PRAGMAS: Record<string, string> = {\n journal_mode: 'WAL',\n synchronous: 'NORMAL',\n foreign_keys: 'ON',\n temp_store: 'MEMORY',\n};\n\nexport const DEFAULT_SETUP: Required<Setup> = {\n pragmas: DEFAULT_PRAGMAS,\n schema: DEFAULT_SCHEMA,\n indexes: [],\n};\n\nexport interface SetupResult {\n tableCreated: boolean;\n pragmasApplied: string[];\n indexesCreated: string[];\n}\n\ninterface PragmaTableInfoRow {\n name: string;\n type: string;\n}\n\ninterface SqliteMasterRow {\n name: string;\n}\n\nfunction isString(value: unknown): value is string {\n return typeof value === 'string';\n}\n\nfunction isPragmaTableInfoRow(value: unknown): value is PragmaTableInfoRow {\n if (typeof value !== 'object' || value === null) return false;\n const obj: Record<string, unknown> = { ...value };\n return isString(obj.name) && isString(obj.type);\n}\n\nfunction isSqliteMasterRow(value: unknown): value is SqliteMasterRow {\n if (typeof value !== 'object' || value === null) return false;\n const obj: Record<string, unknown> = { ...value };\n return isString(obj.name);\n}\n\nfunction buildCreateTableSql(table: string, columns: SetupColumn[]): string {\n const lines = columns.map((c) => {\n const parts: string[] = [c.name, c.type];\n if (c.primaryKey) parts.push('PRIMARY KEY');\n if (c.notNull) parts.push('NOT NULL');\n return parts.join(' ');\n });\n return `CREATE TABLE IF NOT EXISTS ${table} (\\n ${lines.join(',\\n ')}\\n)`;\n}\n\nasync function tableExists(\n client: SqliteClient,\n table: string,\n): Promise<boolean> {\n const rows = await client.query(\n \"SELECT name FROM sqlite_master WHERE type='table' AND name=?\",\n [table],\n );\n for (const row of rows) {\n if (isSqliteMasterRow(row) && row.name === table) return true;\n }\n return false;\n}\n\nasync function detectDrift(\n client: SqliteClient,\n table: string,\n declared: SetupColumn[],\n logger: Logger.Instance,\n): Promise<void> {\n const rows = await client.query(`PRAGMA table_info(${table})`);\n const actualByName = new Map<string, string>();\n for (const row of rows) {\n if (isPragmaTableInfoRow(row)) {\n actualByName.set(row.name, row.type.toUpperCase());\n }\n }\n\n for (const declaredCol of declared) {\n const actualType = actualByName.get(declaredCol.name);\n if (actualType === undefined) {\n logger.warn('setup.drift', {\n field: declaredCol.name,\n declared: declaredCol.type,\n actual: 'missing',\n });\n continue;\n }\n if (actualType !== declaredCol.type.toUpperCase()) {\n logger.warn('setup.drift', {\n field: declaredCol.name,\n declared: declaredCol.type,\n actual: actualType,\n });\n }\n }\n}\n\nexport const setup = async (\n ctx: LifecycleContext<WideConfig, Env>,\n): Promise<SetupResult | undefined> => {\n const { config, env, logger } = ctx;\n const merged = resolveSetup(config.setup, DEFAULT_SETUP);\n if (!merged) {\n logger.debug('setup: skipped (config.setup is false or unset)');\n return;\n }\n const options: Required<Setup> = {\n pragmas: { ...DEFAULT_PRAGMAS, ...(merged.pragmas ?? {}) },\n schema: merged.schema ?? DEFAULT_SCHEMA,\n indexes: merged.indexes ?? [],\n };\n\n const sqlite = config.settings?.sqlite;\n if (!sqlite || !sqlite.url) {\n logger.throw('setup: settings.sqlite.url is missing');\n return;\n }\n const table = sqlite.table ?? 'events';\n\n // Open transient client (or reuse env-injected one). We do NOT touch\n // sqlite._client / sqlite._runInsert here, setup is a separate lifecycle.\n let client: SqliteClient;\n let owned = true;\n if (isSqliteEnv(env) && env.client) {\n client = env.client;\n owned = false;\n } else if (isSqliteEnv(env) && env.SqliteDriver) {\n client = await env.SqliteDriver(sqlite.url, sqlite.authToken);\n } else {\n client = await createClientFromSettings(sqlite.url, sqlite.authToken);\n }\n\n try {\n // 1. Pragmas first (some, like journal_mode, must apply before any write).\n const pragmasApplied: string[] = [];\n for (const [key, value] of Object.entries(options.pragmas)) {\n await client.execute(`PRAGMA ${key} = ${value}`);\n pragmasApplied.push(key);\n }\n\n // 2. Probe whether the table already exists, so tableCreated is reported correctly.\n const tableExisted = await tableExists(client, table);\n\n // 3. CREATE TABLE IF NOT EXISTS with the declared schema.\n await client.execute(buildCreateTableSql(table, options.schema));\n\n // 4. Drift detection. Only when the table existed before; on fresh create\n // the schema matches by definition. Setup never auto-mutates: drift is\n // surfaced via WARN logs, never via ALTER TABLE.\n if (tableExisted) {\n await detectDrift(client, table, options.schema, logger);\n }\n\n // 5. Indexes (CREATE INDEX IF NOT EXISTS).\n const indexesCreated: string[] = [];\n for (const idx of options.indexes) {\n const unique = idx.unique ? 'UNIQUE ' : '';\n const cols = idx.columns.join(', ');\n await client.execute(\n `CREATE ${unique}INDEX IF NOT EXISTS ${idx.name} ON ${table}(${cols})`,\n );\n indexesCreated.push(idx.name);\n }\n\n return {\n tableCreated: !tableExisted,\n pragmasApplied,\n indexesCreated,\n };\n } finally {\n if (owned) {\n try {\n await client.close();\n } catch {\n /* swallow close errors during setup */\n }\n }\n }\n};\n\nexport { tableExists };\n","import type {\n Mapping as WalkerOSMapping,\n Destination as CoreDestination,\n SetupFn as CoreSetupFn,\n} from '@walkeros/core';\nimport type { DestinationServer } from '@walkeros/server-core';\n\n/**\n * @deprecated Use `config.setup` instead. Kept for one minor cycle.\n * `'auto'` maps to `setup: true`, `'manual'` maps to `setup: false`.\n * Removed in the next major.\n */\nexport type SchemaMode = 'auto' | 'manual';\n\n/**\n * Thin cross-driver connection interface. Both drivers are adapted to this shape.\n * Production code uses the adapters in src/drivers/*; tests inject a mock client\n * via env.client to capture sql/args without touching a real database.\n */\nexport interface SqliteClient {\n /** Execute a SQL statement. Used for CREATE TABLE and ad-hoc commands. */\n execute: (sql: string, args?: ReadonlyArray<unknown>) => Promise<void>;\n /**\n * Prepare a statement for repeated execution. Returned function binds args and runs.\n */\n prepare: (sql: string) => (args: ReadonlyArray<unknown>) => Promise<void>;\n /** Run a query that returns rows. Used by setup() for sqlite_master and PRAGMA table_info. */\n query: (\n sql: string,\n args?: ReadonlyArray<unknown>,\n ) => Promise<ReadonlyArray<Record<string, unknown>>>;\n /** Close the connection. */\n close: () => Promise<void>;\n}\n\n/**\n * Factory that constructs an SqliteClient from the user's connection settings.\n * Used by env.SqliteDriver so tests can inject a spy without mocking node_modules.\n */\nexport type SqliteClientFactory = (\n url: string,\n authToken?: string,\n) => Promise<SqliteClient>;\n\nexport interface SqliteSettings {\n /**\n * Connection URL. Starts with `libsql://`, `http://`, `https://`, `wss://`, `ws://`\n * → libSQL driver. Anything else → better-sqlite3 (treated as a file path).\n * Special value `:memory:` → in-memory (better-sqlite3).\n */\n url: string;\n /** libSQL / Turso auth token. Ignored for better-sqlite3. */\n authToken?: string;\n /** Target table name. Defaults to `events`. */\n table?: string;\n /**\n * @deprecated Use `config.setup` instead. Kept for one minor cycle.\n * `'auto'` maps to `setup: true` (init runs CREATE TABLE for the legacy schema).\n * `'manual'` maps to `setup: false`. Removed in the next major.\n */\n schema?: SchemaMode;\n\n // Runtime, set during init/setup, not user-facing.\n _client?: SqliteClient;\n _runInsert?: (args: ReadonlyArray<unknown>) => Promise<void>;\n _ownedClient?: boolean;\n /**\n * Internal flag set by the migration shim. When true, init() runs the legacy\n * 13-column CREATE TABLE path for backward compatibility. Otherwise init()\n * assumes the table already exists (created via `walkeros setup destination.<id>`).\n */\n _legacyAutoCreate?: boolean;\n /**\n * Internal flag set by the migration shim. When true (legacy `schema: 'manual'`),\n * init() skips the modern table-existence probe so the user can bring their own\n * table and column shape without hitting the new \"table not found\" hard-fail.\n */\n _legacySkipProbe?: boolean;\n}\n\nexport interface Settings {\n sqlite: SqliteSettings;\n}\n\nexport type InitSettings = Partial<Settings>;\n\nexport interface Mapping {\n /** Override target table for this rule. */\n table?: string;\n}\n\n/**\n * Provisioning options for `walkeros setup destination.<name>`.\n * Triggered only by the explicit CLI command. Idempotent, never auto-run.\n *\n * Connection URL and target table are read from `settings.sqlite.url` and\n * `settings.sqlite.table` (single source of truth, not duplicated here).\n */\nexport interface Setup {\n /** Pragmas to apply at setup time. Defaults: journal_mode=WAL, synchronous=NORMAL, foreign_keys=ON, temp_store=MEMORY. */\n pragmas?: Record<string, string | number>;\n /** Schema columns. Default: 15-column walkerOS Event v4 canonical (only `name` REQUIRED). */\n schema?: SetupColumn[];\n /** Indexes to create after the table. Optional. */\n indexes?: SetupIndex[];\n}\n\n/** Single column in the SQLite schema. */\nexport interface SetupColumn {\n name: string;\n type: 'TEXT' | 'INTEGER' | 'REAL' | 'BLOB' | 'NUMERIC';\n notNull?: boolean;\n primaryKey?: boolean;\n}\n\n/** SQLite index definition. */\nexport interface SetupIndex {\n name: string;\n columns: string[];\n unique?: boolean;\n}\n\n/**\n * Env, optional driver override. Production leaves this undefined and the\n * destination loads better-sqlite3 or @libsql/client dynamically. Tests\n * provide a factory via `SqliteDriver` or a pre-built client via `client`.\n */\nexport interface Env extends DestinationServer.Env {\n SqliteDriver?: SqliteClientFactory;\n client?: SqliteClient;\n}\n\nexport type Types = CoreDestination.Types<\n Settings,\n Mapping,\n Env,\n InitSettings,\n Setup\n>;\n\nexport interface Destination extends DestinationServer.Destination<Types> {\n init: DestinationServer.InitFn<Types>;\n}\n\nexport type Config = {\n settings: Settings;\n} & DestinationServer.Config<Types>;\n\nexport type InitFn = DestinationServer.InitFn<Types>;\nexport type PushFn = DestinationServer.PushFn<Types>;\nexport type SetupFn = CoreSetupFn<Config, Env>;\nexport type PartialConfig = DestinationServer.PartialConfig<Types>;\nexport type PushEvents = DestinationServer.PushEvents<Mapping>;\n\nexport type Rule = WalkerOSMapping.Rule<Mapping>;\nexport type Rules = WalkerOSMapping.Rules<Rule>;\n","export * as env from './env';\nexport * as step from './step';\n","import type { Env, SqliteClient, SqliteClientFactory } from '../types';\n\n// Narrow helper type aliases so mock functions are typed without `any`.\ntype ExecuteFn = (sql: string, args?: ReadonlyArray<unknown>) => Promise<void>;\ntype PrepareFn = (\n sql: string,\n) => (args: ReadonlyArray<unknown>) => Promise<void>;\ntype QueryFn = (\n sql: string,\n args?: ReadonlyArray<unknown>,\n) => Promise<ReadonlyArray<Record<string, unknown>>>;\ntype CloseFn = () => Promise<void>;\n\nconst asyncExecute: ExecuteFn = () => Promise.resolve();\nconst asyncClose: CloseFn = () => Promise.resolve();\nconst asyncPrepare: PrepareFn = () => () => Promise.resolve();\nconst asyncQuery: QueryFn = () => Promise.resolve([]);\n\nconst mockClient: SqliteClient = {\n execute: asyncExecute,\n prepare: asyncPrepare,\n query: asyncQuery,\n close: asyncClose,\n};\n\nconst mockFactory: SqliteClientFactory = () => Promise.resolve(mockClient);\n\nexport const push: Env = {\n SqliteDriver: mockFactory,\n};\n\n/**\n * Simulation tracking paths. Specifies which function calls to record when\n * running step examples through the collector.\n */\nexport const simulation = ['call:client.prepare', 'call:client.execute'];\n","import type { Flow } from '@walkeros/core';\nimport { getEvent } from '@walkeros/core';\nimport type { Settings } from '../types';\n\n/**\n * Extended step example that may carry destination-level settings overrides.\n */\nexport type SqliteStepExample = Flow.StepExample & {\n settings?: Partial<Settings>;\n};\n\n/**\n * Canonical insert into the default `events` table. `out` records the\n * column args the prepared INSERT is bound with.\n */\nexport const defaultInsert: SqliteStepExample = {\n title: 'Default insert',\n description:\n 'A walker event is inserted into the default events table with canonical columns and JSON-encoded sections.',\n in: getEvent('page view', {\n timestamp: 1700000100,\n id: 'evt-1',\n user: { session: 'sess-1', id: 'user-42' },\n data: { title: 'Home' },\n source: {\n type: 'browser',\n platform: 'web',\n url: 'https://example.com/',\n referrer: 'https://example.com/prev',\n },\n globals: { env: 'prod' },\n consent: { analytics: true },\n }),\n out: [\n [\n 'client.runInsert',\n [\n 1700000100,\n 'evt-1',\n 'page view',\n 'page',\n 'view',\n 'sess-1',\n 'user-42',\n 'https://example.com/',\n 'Home',\n 'https://example.com/prev',\n JSON.stringify({ title: 'Home' }),\n JSON.stringify({ env: 'prod' }),\n JSON.stringify({ analytics: true }),\n ],\n ],\n ],\n};\n\n/**\n * Custom table name. Verifies table overrides while using the canonical column set.\n */\nexport const customTable: SqliteStepExample = {\n title: 'Custom table',\n description:\n 'A destination-level table setting inserts events into a custom SQLite table with the same column layout.',\n in: getEvent('form submit', {\n timestamp: 1700000101,\n id: 'evt-2',\n user: { session: 'sess-99', id: '' },\n data: { type: 'contact' },\n source: {\n type: 'browser',\n platform: 'web',\n url: 'https://example.com/contact',\n },\n globals: {},\n consent: {},\n }),\n settings: {\n sqlite: {\n url: ':memory:',\n table: 'siteEvents',\n },\n },\n out: [\n [\n 'client.runInsert',\n [\n 1700000101,\n 'evt-2',\n 'form submit',\n 'form',\n 'submit',\n 'sess-99',\n '',\n 'https://example.com/contact',\n '',\n '',\n JSON.stringify({ type: 'contact' }),\n JSON.stringify({}),\n JSON.stringify({}),\n ],\n ],\n ],\n};\n\n/**\n * Order event with numeric data. Confirms JSON serialization of nested values.\n */\nexport const orderComplete: SqliteStepExample = {\n title: 'Order insert',\n description:\n 'An order complete is inserted with numeric data serialized as JSON in the data column.',\n in: getEvent('order complete', {\n timestamp: 1700000102,\n id: 'evt-3',\n user: { session: '', id: '' },\n data: { id: 'ORD-1', total: 99 },\n source: { type: 'collector', schema: '4' },\n globals: {},\n consent: {},\n }),\n out: [\n [\n 'client.runInsert',\n [\n 1700000102,\n 'evt-3',\n 'order complete',\n 'order',\n 'complete',\n '',\n '',\n '',\n '',\n '',\n JSON.stringify({ id: 'ORD-1', total: 99 }),\n JSON.stringify({}),\n JSON.stringify({}),\n ],\n ],\n ],\n};\n\n/**\n * Table override per rule -- routes this event to a dedicated table.\n */\nexport const tableOverride: SqliteStepExample = {\n title: 'Table override',\n description:\n 'A mapping rule overrides the target table so specific events are inserted into a dedicated SQLite table.',\n in: getEvent('order complete', {\n timestamp: 1700000103,\n id: 'evt-4',\n user: { session: '', id: '' },\n data: { id: 'ORD-2', total: 42 },\n source: { type: 'collector', schema: '4' },\n globals: {},\n consent: {},\n }),\n mapping: {\n settings: {\n table: 'orders',\n },\n },\n out: [\n [\n 'client.runInsert',\n [\n 1700000103,\n 'evt-4',\n 'order complete',\n 'order',\n 'complete',\n '',\n '',\n '',\n '',\n '',\n JSON.stringify({ id: 'ORD-2', total: 42 }),\n JSON.stringify({}),\n JSON.stringify({}),\n ],\n ],\n ],\n};\n\n/**\n * Ignored event -- mapping.ignore: true produces no insert call.\n */\nexport const ignoredEvent: SqliteStepExample = {\n public: false,\n in: getEvent('debug noise', {\n timestamp: 1700000104,\n id: 'evt-5',\n source: { type: 'collector', schema: '4' },\n }),\n mapping: { ignore: true },\n out: [],\n};\n","import type { Destination, Env, Settings, SqliteClient } from './types';\nimport { getConfig, isSqliteEnv } from './config';\nimport { createClientFromSettings } from './drivers';\nimport { buildCreateTableSql, buildInsertSql } from './serialize';\nimport { push } from './push';\nimport { setup, tableExists } from './setup';\n\n// Types re-export\nexport * as DestinationSQLite from './types';\n\n// Examples\nexport * as examples from './examples';\n\n// Serialization helpers (exported for tests and advanced consumers)\nexport {\n CANONICAL_COLUMNS,\n buildCreateTableSql,\n buildInsertSql,\n eventToRow,\n} from './serialize';\n\n// Driver URL detection (exported for tests and advanced consumers)\nexport { isLibsqlUrl } from './drivers';\n\n// Setup helpers (exported for tests and advanced consumers)\nexport { setup, DEFAULT_SCHEMA, DEFAULT_PRAGMAS, DEFAULT_SETUP } from './setup';\n\nexport const destinationSQLite: Destination = {\n type: 'sqlite',\n\n config: {},\n\n setup,\n\n async init({ config: partialConfig, logger, env, id }) {\n const config = getConfig(partialConfig, logger);\n const settings = config.settings as Settings;\n const sqlite = settings.sqlite;\n\n // Skip creation if a client has already been wired in (testing).\n if (sqlite._client) return config;\n\n let client: SqliteClient | undefined;\n\n // Prefer env-injected client / factory (tests, dependency injection).\n if (isSqliteEnv(env)) {\n const envTyped = env as Env;\n if (envTyped.client) {\n client = envTyped.client;\n } else if (envTyped.SqliteDriver) {\n client = await envTyped.SqliteDriver(sqlite.url, sqlite.authToken);\n }\n }\n\n // Production path: load real driver based on the URL prefix.\n if (!client) {\n try {\n client = await createClientFromSettings(sqlite.url, sqlite.authToken);\n } catch (err) {\n logger.throw(\n `@walkeros/server-destination-sqlite: driver load failed: ${String(err)}`,\n );\n return config;\n }\n }\n\n // Legacy auto-create path. Only runs when the user opted in via the\n // deprecated `settings.sqlite.schema: 'auto'` field. New users run\n // `walkeros setup destination.<id>` (see setup.ts) or set `config.setup`.\n if (sqlite._legacyAutoCreate) {\n try {\n await client.execute(buildCreateTableSql(sqlite.table ?? 'events'));\n } catch (err) {\n logger.throw(\n `@walkeros/server-destination-sqlite: CREATE TABLE failed: ${String(err)}`,\n );\n return config;\n }\n } else if (!sqlite._legacySkipProbe) {\n // Modern path: assume the table exists. Probe once and hard-fail with\n // an actionable message if it does not. Legacy `schema: 'manual'` skips\n // this probe (user supplies their own table shape).\n let exists = false;\n try {\n exists = await tableExists(client, sqlite.table ?? 'events');\n } catch (err) {\n logger.throw(\n `@walkeros/server-destination-sqlite: table probe failed: ${String(err)}`,\n );\n return config;\n }\n if (!exists) {\n logger.throw(\n `SQLite table \"${sqlite.table ?? 'events'}\" not found in ${sqlite.url}. ` +\n `Run \"walkeros setup destination.${id}\" to create it.`,\n );\n return config;\n }\n }\n\n // Cache the prepared insert for per-event calls.\n sqlite._client = client;\n sqlite._runInsert = client.prepare(\n buildInsertSql(sqlite.table ?? 'events'),\n );\n sqlite._ownedClient = !(isSqliteEnv(env) && (env as Env).client);\n\n return config;\n },\n\n async push(event, context) {\n return await push(event, context);\n },\n\n async destroy({ config }) {\n const settings = config?.settings as Settings | undefined;\n const sqlite = settings?.sqlite;\n if (!sqlite) return;\n\n const client = sqlite._client;\n // Only close clients the destination created (not user-provided).\n if (client && sqlite._ownedClient) {\n try {\n await client.close();\n } finally {\n sqlite._client = undefined;\n sqlite._runInsert = undefined;\n sqlite._ownedClient = undefined;\n }\n }\n },\n};\n\nexport default destinationSQLite;\n"],"mappings":"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","names":["isObject","isString","isString","isString","buildCreateTableSql","push"]}
|
|
1
|
+
{"version":3,"sources":["../src/config.ts","../src/drivers/betterSqlite3.ts","../src/drivers/libsql.ts","../src/drivers/index.ts","../src/serialize.ts","../src/push.ts","../src/setup.ts","../src/types/index.ts","../src/index.ts"],"sourcesContent":["import type {\n Config,\n Env,\n PartialConfig,\n Settings,\n SqliteSettings,\n} from './types';\nimport type { Logger } from '@walkeros/core';\nimport { isObject } from '@walkeros/core';\n\nexport function getConfig(\n partialConfig: PartialConfig = {},\n logger: Logger.Instance,\n): Config {\n const raw = (partialConfig.settings ?? {}) as Partial<Settings>;\n const sqlite: Partial<SqliteSettings> =\n raw.sqlite ?? ({} as Partial<SqliteSettings>);\n\n if (!sqlite.url) {\n logger.throw('Config settings sqlite.url missing');\n }\n\n const sqliteSettings: SqliteSettings = {\n ...sqlite,\n url: sqlite.url as string,\n table: sqlite.table ?? 'events',\n };\n\n // Migration shim: settings.sqlite.schema is the predecessor of config.setup.\n // Accept BOTH for one minor cycle, emit a deprecation WARN if `schema` is\n // used. Removed in the next major.\n if (sqlite.schema !== undefined) {\n logger.warn(\n 'config.settings.sqlite.schema is deprecated; use config.setup instead. ' +\n '\"schema: auto\" maps to \"setup: true\" (run \"walkeros setup destination.<id>\"); ' +\n '\"schema: manual\" maps to \"setup: false\". This field is removed in the next major.',\n );\n if (sqlite.schema === 'manual' && !partialConfig.mapping) {\n // Preserve the existing throw for the legacy \"manual without mapping\" case.\n logger.throw(\n 'Config settings sqlite.schema=\"manual\" requires a mapping (no default column mapping will be used).',\n );\n }\n sqliteSettings._legacyAutoCreate = sqlite.schema === 'auto';\n // Legacy `schema: 'manual'` opted out of the auto-create AND opted out of\n // the table-existence probe (user brings their own table). Honor that.\n sqliteSettings._legacySkipProbe = sqlite.schema === 'manual';\n } else {\n // No legacy schema flag. Default behavior: do NOT auto-create in init().\n // The user must run `walkeros setup destination.<id>` or set `config.setup: true`.\n sqliteSettings._legacyAutoCreate = false;\n sqliteSettings._legacySkipProbe = false;\n }\n\n // Strip the deprecated key from the live settings object so downstream code\n // never reads it. The flag is preserved on `_legacyAutoCreate`.\n delete sqliteSettings.schema;\n\n const settings: Settings = { sqlite: sqliteSettings };\n\n return { ...partialConfig, settings };\n}\n\nexport function isSqliteEnv(env: unknown): env is Env {\n if (!isObject(env)) return false;\n const maybe = env as { SqliteDriver?: unknown; client?: unknown };\n return (\n typeof maybe.SqliteDriver === 'function' || isObject(maybe.client ?? null)\n );\n}\n","import type { SqliteClient } from '../types';\n\n/**\n * better-sqlite3 driver -- synchronous native SQLite for local files and ':memory:'.\n * Wrapped in Promise.resolve so the cross-driver interface is always async.\n *\n * The library is loaded via dynamic require to allow tests to mock via\n * jest.mock('better-sqlite3'), and to keep it as an optional peer dependency.\n */\ninterface BetterSqliteStatement {\n run: (...args: unknown[]) => unknown;\n all: (...args: unknown[]) => ReadonlyArray<Record<string, unknown>>;\n}\n\ninterface BetterSqliteDatabase {\n prepare: (sql: string) => BetterSqliteStatement;\n close: () => void;\n}\n\ninterface BetterSqliteConstructor {\n new (filePath: string): BetterSqliteDatabase;\n}\n\nexport async function createBetterSqliteClient(\n filePath: string,\n): Promise<SqliteClient> {\n let Database: BetterSqliteConstructor;\n try {\n // Dynamic require so the module stays an optional peer dep and tests can mock.\n const loaded = require('better-sqlite3') as\n | BetterSqliteConstructor\n | { default: BetterSqliteConstructor };\n Database =\n 'default' in loaded && typeof loaded.default === 'function'\n ? loaded.default\n : (loaded as BetterSqliteConstructor);\n } catch (err) {\n throw new Error(\n '@walkeros/server-destination-sqlite: better-sqlite3 is not installed. ' +\n 'Install it with `npm install better-sqlite3`, or switch settings.sqlite.url to a libSQL URL. ' +\n `Original error: ${String(err)}`,\n );\n }\n\n const db = new Database(filePath);\n\n return {\n async execute(sql, args = []) {\n db.prepare(sql).run(...(args as unknown[]));\n },\n prepare(sql) {\n const stmt = db.prepare(sql);\n return async (args) => {\n stmt.run(...(args as unknown[]));\n };\n },\n async query(sql, args = []) {\n return db.prepare(sql).all(...(args as unknown[]));\n },\n async close() {\n db.close();\n },\n };\n}\n","import type { SqliteClient } from '../types';\n\n/**\n * libSQL / Turso driver -- async HTTP(S)/WSS/local file driver.\n *\n * Loaded via dynamic require to stay an optional peer dep and let tests mock\n * via jest.mock('@libsql/client').\n */\ninterface LibsqlExecuteArgs {\n sql: string;\n args?: ReadonlyArray<unknown>;\n}\n\ninterface LibsqlResultSet {\n rows?: ReadonlyArray<Record<string, unknown>>;\n}\n\ninterface LibsqlClient {\n execute: (input: LibsqlExecuteArgs) => Promise<LibsqlResultSet>;\n close: () => void;\n}\n\ninterface LibsqlCreateClientConfig {\n url: string;\n authToken?: string;\n}\n\ntype CreateClientFn = (config: LibsqlCreateClientConfig) => LibsqlClient;\n\nexport async function createLibsqlClient(\n url: string,\n authToken?: string,\n): Promise<SqliteClient> {\n let createClient: CreateClientFn;\n try {\n const loaded = require('@libsql/client') as {\n createClient?: CreateClientFn;\n default?: { createClient?: CreateClientFn };\n };\n const fn = loaded.createClient ?? loaded.default?.createClient;\n if (typeof fn !== 'function') {\n throw new Error('createClient export not found on @libsql/client');\n }\n createClient = fn;\n } catch (err) {\n throw new Error(\n '@walkeros/server-destination-sqlite: @libsql/client is not installed. ' +\n 'Install it with `npm install @libsql/client`, or use a local file URL. ' +\n `Original error: ${String(err)}`,\n );\n }\n\n const client = createClient({ url, authToken });\n\n return {\n async execute(sql, args = []) {\n await client.execute({ sql, args });\n },\n prepare(sql) {\n // libSQL has no server-side prepared-statement cache for HTTP; we close over\n // the sql string and call execute() per run. Cost is negligible vs network RTT.\n return async (args) => {\n await client.execute({ sql, args });\n };\n },\n async query(sql, args = []) {\n const result = await client.execute({ sql, args });\n return result?.rows ?? [];\n },\n async close() {\n client.close();\n },\n };\n}\n","import type { SqliteClient, SqliteClientFactory } from '../types';\nimport { createBetterSqliteClient } from './betterSqlite3';\nimport { createLibsqlClient } from './libsql';\n\nconst LIBSQL_URL_PREFIXES = [\n 'libsql://',\n 'http://',\n 'https://',\n 'ws://',\n 'wss://',\n];\n\nexport function isLibsqlUrl(url: string): boolean {\n return LIBSQL_URL_PREFIXES.some((p) => url.startsWith(p));\n}\n\n/**\n * Default client factory -- picks better-sqlite3 for local file paths and\n * libSQL for remote URLs. Tests can override via env.SqliteDriver.\n */\nexport const defaultFactory: SqliteClientFactory = async (url, authToken) => {\n if (isLibsqlUrl(url)) return createLibsqlClient(url, authToken);\n return createBetterSqliteClient(url);\n};\n\nexport async function createClientFromSettings(\n url: string,\n authToken: string | undefined,\n): Promise<SqliteClient> {\n return defaultFactory(url, authToken);\n}\n\nexport { createBetterSqliteClient, createLibsqlClient };\n","import type { WalkerOS } from '@walkeros/core';\nimport { isObject, isString } from '@walkeros/core';\n\n/**\n * Canonical columns for the auto-created events table. Order matters -- used\n * to build both CREATE TABLE and the prepared INSERT.\n */\nexport const CANONICAL_COLUMNS = [\n 'timestamp',\n 'event_id',\n 'name',\n 'entity',\n 'action',\n 'session_id',\n 'user_id',\n 'page_url',\n 'page_title',\n 'referrer_url',\n 'data',\n 'globals',\n 'consent',\n] as const;\n\nexport function buildCreateTableSql(table: string): string {\n return `CREATE TABLE IF NOT EXISTS ${table} (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n timestamp INTEGER,\n event_id TEXT,\n name TEXT,\n entity TEXT,\n action TEXT,\n session_id TEXT,\n user_id TEXT,\n page_url TEXT,\n page_title TEXT,\n referrer_url TEXT,\n data TEXT,\n globals TEXT,\n consent TEXT\n)`;\n}\n\nexport function buildInsertSql(table: string): string {\n const cols = CANONICAL_COLUMNS.join(', ');\n const placeholders = CANONICAL_COLUMNS.map(() => '?').join(', ');\n return `INSERT INTO ${table} (${cols}) VALUES (${placeholders})`;\n}\n\n/**\n * Map a walkerOS event onto the canonical column order. JSON blobs are\n * stringified. Missing fields become empty strings / zeros.\n */\nexport function eventToRow(event: WalkerOS.Event): unknown[] {\n const data = isObject(event.data) ? event.data : {};\n const title = isString(data.title) ? data.title : '';\n\n return [\n typeof event.timestamp === 'number' ? event.timestamp : Date.now(),\n event.id ?? '',\n event.name ?? '',\n event.entity ?? '',\n event.action ?? '',\n event.user?.session ?? '',\n event.user?.id ?? '',\n event.source?.url ?? '',\n title,\n event.source?.referrer ?? '',\n JSON.stringify(data),\n JSON.stringify(event.globals ?? {}),\n JSON.stringify(event.consent ?? {}),\n ];\n}\n","import type { PushFn, SqliteSettings } from './types';\nimport { isString } from '@walkeros/core';\nimport { buildInsertSql, eventToRow } from './serialize';\n\nexport const push: PushFn = async function (event, { config, rule, logger }) {\n const settings = config.settings as { sqlite?: SqliteSettings } | undefined;\n const sqlite: SqliteSettings | undefined = settings?.sqlite;\n\n if (!sqlite) {\n logger.warn('SQLite settings missing');\n return;\n }\n\n const client = sqlite._client;\n if (!client) {\n logger.warn('SQLite client not initialized');\n return;\n }\n\n const ruleSettings = rule?.settings ?? {};\n const ruleTable = isString(ruleSettings.table) ? ruleSettings.table : '';\n\n // If the rule overrides the table, we can't reuse the cached prepared statement\n // (which was bound to the default table). Build an ad-hoc prepared run.\n const effectiveTable = ruleTable || sqlite.table || 'events';\n const runInsert =\n ruleTable && ruleTable !== sqlite.table\n ? client.prepare(buildInsertSql(effectiveTable))\n : sqlite._runInsert;\n\n if (!runInsert) {\n logger.warn('SQLite insert statement not prepared');\n return;\n }\n\n const row = eventToRow(event);\n\n logger.debug('SQLite INSERT', {\n table: effectiveTable,\n eventName: event.name,\n });\n\n try {\n await runInsert(row);\n logger.debug('SQLite INSERT complete', { table: effectiveTable });\n } catch (error) {\n logger.error('SQLite INSERT failed', {\n table: effectiveTable,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n};\n","import type { LifecycleContext, Logger } from '@walkeros/core';\nimport { resolveSetup } from '@walkeros/core';\nimport type { DestinationServer } from '@walkeros/server-core';\nimport type { Env, Setup, SetupColumn, SqliteClient, Types } from './types';\nimport { isSqliteEnv } from './config';\nimport { createClientFromSettings } from './drivers';\n\n// Setup is wired to the destination's `setup` slot which uses the broader\n// `DestinationServer.Config<Types>` (settings is optional). We runtime-narrow\n// instead of using the local Config alias so the assignment in index.ts\n// type-checks without contravariance issues.\ntype WideConfig = DestinationServer.Config<Types>;\n\n/**\n * Default 15-column walkerOS Event v4 canonical schema.\n * Mirrors BigQuery (Plan 2). Only `name` is REQUIRED.\n */\nexport const DEFAULT_SCHEMA: SetupColumn[] = [\n { name: 'name', type: 'TEXT', notNull: true },\n { name: 'data', type: 'TEXT' },\n { name: 'context', type: 'TEXT' },\n { name: 'globals', type: 'TEXT' },\n { name: 'custom', type: 'TEXT' },\n { name: 'user', type: 'TEXT' },\n { name: 'nested', type: 'TEXT' },\n { name: 'consent', type: 'TEXT' },\n { name: 'id', type: 'TEXT' },\n { name: 'trigger', type: 'TEXT' },\n { name: 'entity', type: 'TEXT' },\n { name: 'action', type: 'TEXT' },\n { name: 'timestamp', type: 'TEXT' },\n { name: 'timing', type: 'INTEGER' },\n { name: 'source', type: 'TEXT' },\n];\n\nexport const DEFAULT_PRAGMAS: Record<string, string> = {\n journal_mode: 'WAL',\n synchronous: 'NORMAL',\n foreign_keys: 'ON',\n temp_store: 'MEMORY',\n};\n\nexport const DEFAULT_SETUP: Required<Setup> = {\n pragmas: DEFAULT_PRAGMAS,\n schema: DEFAULT_SCHEMA,\n indexes: [],\n};\n\nexport interface SetupResult {\n tableCreated: boolean;\n pragmasApplied: string[];\n indexesCreated: string[];\n}\n\ninterface PragmaTableInfoRow {\n name: string;\n type: string;\n}\n\ninterface SqliteMasterRow {\n name: string;\n}\n\nfunction isString(value: unknown): value is string {\n return typeof value === 'string';\n}\n\nfunction isPragmaTableInfoRow(value: unknown): value is PragmaTableInfoRow {\n if (typeof value !== 'object' || value === null) return false;\n const obj: Record<string, unknown> = { ...value };\n return isString(obj.name) && isString(obj.type);\n}\n\nfunction isSqliteMasterRow(value: unknown): value is SqliteMasterRow {\n if (typeof value !== 'object' || value === null) return false;\n const obj: Record<string, unknown> = { ...value };\n return isString(obj.name);\n}\n\nfunction buildCreateTableSql(table: string, columns: SetupColumn[]): string {\n const lines = columns.map((c) => {\n const parts: string[] = [c.name, c.type];\n if (c.primaryKey) parts.push('PRIMARY KEY');\n if (c.notNull) parts.push('NOT NULL');\n return parts.join(' ');\n });\n return `CREATE TABLE IF NOT EXISTS ${table} (\\n ${lines.join(',\\n ')}\\n)`;\n}\n\nasync function tableExists(\n client: SqliteClient,\n table: string,\n): Promise<boolean> {\n const rows = await client.query(\n \"SELECT name FROM sqlite_master WHERE type='table' AND name=?\",\n [table],\n );\n for (const row of rows) {\n if (isSqliteMasterRow(row) && row.name === table) return true;\n }\n return false;\n}\n\nasync function detectDrift(\n client: SqliteClient,\n table: string,\n declared: SetupColumn[],\n logger: Logger.Instance,\n): Promise<void> {\n const rows = await client.query(`PRAGMA table_info(${table})`);\n const actualByName = new Map<string, string>();\n for (const row of rows) {\n if (isPragmaTableInfoRow(row)) {\n actualByName.set(row.name, row.type.toUpperCase());\n }\n }\n\n for (const declaredCol of declared) {\n const actualType = actualByName.get(declaredCol.name);\n if (actualType === undefined) {\n logger.warn('setup.drift', {\n field: declaredCol.name,\n declared: declaredCol.type,\n actual: 'missing',\n });\n continue;\n }\n if (actualType !== declaredCol.type.toUpperCase()) {\n logger.warn('setup.drift', {\n field: declaredCol.name,\n declared: declaredCol.type,\n actual: actualType,\n });\n }\n }\n}\n\nexport const setup = async (\n ctx: LifecycleContext<WideConfig, Env>,\n): Promise<SetupResult | undefined> => {\n const { config, env, logger } = ctx;\n const merged = resolveSetup(config.setup, DEFAULT_SETUP);\n if (!merged) {\n logger.debug('setup: skipped (config.setup is false or unset)');\n return;\n }\n const options: Required<Setup> = {\n pragmas: { ...DEFAULT_PRAGMAS, ...(merged.pragmas ?? {}) },\n schema: merged.schema ?? DEFAULT_SCHEMA,\n indexes: merged.indexes ?? [],\n };\n\n const sqlite = config.settings?.sqlite;\n if (!sqlite || !sqlite.url) {\n logger.throw('setup: settings.sqlite.url is missing');\n return;\n }\n const table = sqlite.table ?? 'events';\n\n // Open transient client (or reuse env-injected one). We do NOT touch\n // sqlite._client / sqlite._runInsert here, setup is a separate lifecycle.\n let client: SqliteClient;\n let owned = true;\n if (isSqliteEnv(env) && env.client) {\n client = env.client;\n owned = false;\n } else if (isSqliteEnv(env) && env.SqliteDriver) {\n client = await env.SqliteDriver(sqlite.url, sqlite.authToken);\n } else {\n client = await createClientFromSettings(sqlite.url, sqlite.authToken);\n }\n\n try {\n // 1. Pragmas first (some, like journal_mode, must apply before any write).\n const pragmasApplied: string[] = [];\n for (const [key, value] of Object.entries(options.pragmas)) {\n await client.execute(`PRAGMA ${key} = ${value}`);\n pragmasApplied.push(key);\n }\n\n // 2. Probe whether the table already exists, so tableCreated is reported correctly.\n const tableExisted = await tableExists(client, table);\n\n // 3. CREATE TABLE IF NOT EXISTS with the declared schema.\n await client.execute(buildCreateTableSql(table, options.schema));\n\n // 4. Drift detection. Only when the table existed before; on fresh create\n // the schema matches by definition. Setup never auto-mutates: drift is\n // surfaced via WARN logs, never via ALTER TABLE.\n if (tableExisted) {\n await detectDrift(client, table, options.schema, logger);\n }\n\n // 5. Indexes (CREATE INDEX IF NOT EXISTS).\n const indexesCreated: string[] = [];\n for (const idx of options.indexes) {\n const unique = idx.unique ? 'UNIQUE ' : '';\n const cols = idx.columns.join(', ');\n await client.execute(\n `CREATE ${unique}INDEX IF NOT EXISTS ${idx.name} ON ${table}(${cols})`,\n );\n indexesCreated.push(idx.name);\n }\n\n return {\n tableCreated: !tableExisted,\n pragmasApplied,\n indexesCreated,\n };\n } finally {\n if (owned) {\n try {\n await client.close();\n } catch {\n /* swallow close errors during setup */\n }\n }\n }\n};\n\nexport { tableExists };\n","import type {\n Mapping as WalkerOSMapping,\n Destination as CoreDestination,\n SetupFn as CoreSetupFn,\n} from '@walkeros/core';\nimport type { DestinationServer } from '@walkeros/server-core';\n\n/**\n * @deprecated Use `config.setup` instead. Kept for one minor cycle.\n * `'auto'` maps to `setup: true`, `'manual'` maps to `setup: false`.\n * Removed in the next major.\n */\nexport type SchemaMode = 'auto' | 'manual';\n\n/**\n * Thin cross-driver connection interface. Both drivers are adapted to this shape.\n * Production code uses the adapters in src/drivers/*; tests inject a mock client\n * via env.client to capture sql/args without touching a real database.\n */\nexport interface SqliteClient {\n /** Execute a SQL statement. Used for CREATE TABLE and ad-hoc commands. */\n execute: (sql: string, args?: ReadonlyArray<unknown>) => Promise<void>;\n /**\n * Prepare a statement for repeated execution. Returned function binds args and runs.\n */\n prepare: (sql: string) => (args: ReadonlyArray<unknown>) => Promise<void>;\n /** Run a query that returns rows. Used by setup() for sqlite_master and PRAGMA table_info. */\n query: (\n sql: string,\n args?: ReadonlyArray<unknown>,\n ) => Promise<ReadonlyArray<Record<string, unknown>>>;\n /** Close the connection. */\n close: () => Promise<void>;\n}\n\n/**\n * Factory that constructs an SqliteClient from the user's connection settings.\n * Used by env.SqliteDriver so tests can inject a spy without mocking node_modules.\n */\nexport type SqliteClientFactory = (\n url: string,\n authToken?: string,\n) => Promise<SqliteClient>;\n\nexport interface SqliteSettings {\n /**\n * Connection URL. Starts with `libsql://`, `http://`, `https://`, `wss://`, `ws://`\n * → libSQL driver. Anything else → better-sqlite3 (treated as a file path).\n * Special value `:memory:` → in-memory (better-sqlite3).\n */\n url: string;\n /** libSQL / Turso auth token. Ignored for better-sqlite3. */\n authToken?: string;\n /** Target table name. Defaults to `events`. */\n table?: string;\n /**\n * @deprecated Use `config.setup` instead. Kept for one minor cycle.\n * `'auto'` maps to `setup: true` (init runs CREATE TABLE for the legacy schema).\n * `'manual'` maps to `setup: false`. Removed in the next major.\n */\n schema?: SchemaMode;\n\n // Runtime, set during init/setup, not user-facing.\n _client?: SqliteClient;\n _runInsert?: (args: ReadonlyArray<unknown>) => Promise<void>;\n _ownedClient?: boolean;\n /**\n * Internal flag set by the migration shim. When true, init() runs the legacy\n * 13-column CREATE TABLE path for backward compatibility. Otherwise init()\n * assumes the table already exists (created via `walkeros setup destination.<id>`).\n */\n _legacyAutoCreate?: boolean;\n /**\n * Internal flag set by the migration shim. When true (legacy `schema: 'manual'`),\n * init() skips the modern table-existence probe so the user can bring their own\n * table and column shape without hitting the new \"table not found\" hard-fail.\n */\n _legacySkipProbe?: boolean;\n}\n\nexport interface Settings {\n sqlite: SqliteSettings;\n}\n\nexport type InitSettings = Partial<Settings>;\n\nexport interface Mapping {\n /** Override target table for this rule. */\n table?: string;\n}\n\n/**\n * Provisioning options for `walkeros setup destination.<name>`.\n * Triggered only by the explicit CLI command. Idempotent, never auto-run.\n *\n * Connection URL and target table are read from `settings.sqlite.url` and\n * `settings.sqlite.table` (single source of truth, not duplicated here).\n */\nexport interface Setup {\n /** Pragmas to apply at setup time. Defaults: journal_mode=WAL, synchronous=NORMAL, foreign_keys=ON, temp_store=MEMORY. */\n pragmas?: Record<string, string | number>;\n /** Schema columns. Default: 15-column walkerOS Event v4 canonical (only `name` REQUIRED). */\n schema?: SetupColumn[];\n /** Indexes to create after the table. Optional. */\n indexes?: SetupIndex[];\n}\n\n/** Single column in the SQLite schema. */\nexport interface SetupColumn {\n name: string;\n type: 'TEXT' | 'INTEGER' | 'REAL' | 'BLOB' | 'NUMERIC';\n notNull?: boolean;\n primaryKey?: boolean;\n}\n\n/** SQLite index definition. */\nexport interface SetupIndex {\n name: string;\n columns: string[];\n unique?: boolean;\n}\n\n/**\n * Env, optional driver override. Production leaves this undefined and the\n * destination loads better-sqlite3 or @libsql/client dynamically. Tests\n * provide a factory via `SqliteDriver` or a pre-built client via `client`.\n */\nexport interface Env extends DestinationServer.Env {\n SqliteDriver?: SqliteClientFactory;\n client?: SqliteClient;\n}\n\nexport type Types = CoreDestination.Types<\n Settings,\n Mapping,\n Env,\n InitSettings,\n Setup\n>;\n\nexport interface Destination extends DestinationServer.Destination<Types> {\n init: DestinationServer.InitFn<Types>;\n}\n\nexport type Config = {\n settings: Settings;\n} & DestinationServer.Config<Types>;\n\nexport type InitFn = DestinationServer.InitFn<Types>;\nexport type PushFn = DestinationServer.PushFn<Types>;\nexport type SetupFn = CoreSetupFn<Config, Env>;\nexport type PartialConfig = DestinationServer.PartialConfig<Types>;\nexport type PushEvents = DestinationServer.PushEvents<Mapping>;\n\nexport type Rule = WalkerOSMapping.Rule<Mapping>;\nexport type Rules = WalkerOSMapping.Rules<Rule>;\n","import type { Destination, Env, Settings, SqliteClient } from './types';\nimport { getConfig, isSqliteEnv } from './config';\nimport { createClientFromSettings } from './drivers';\nimport { buildCreateTableSql, buildInsertSql } from './serialize';\nimport { push } from './push';\nimport { setup, tableExists } from './setup';\n\n// Types re-export\nexport * as DestinationSQLite from './types';\n\n// Serialization helpers (exported for tests and advanced consumers)\nexport {\n CANONICAL_COLUMNS,\n buildCreateTableSql,\n buildInsertSql,\n eventToRow,\n} from './serialize';\n\n// Driver URL detection (exported for tests and advanced consumers)\nexport { isLibsqlUrl } from './drivers';\n\n// Setup helpers (exported for tests and advanced consumers)\nexport { setup, DEFAULT_SCHEMA, DEFAULT_PRAGMAS, DEFAULT_SETUP } from './setup';\n\nexport const destinationSQLite: Destination = {\n type: 'sqlite',\n\n config: {},\n\n setup,\n\n async init({ config: partialConfig, logger, env, id }) {\n const config = getConfig(partialConfig, logger);\n const settings = config.settings as Settings;\n const sqlite = settings.sqlite;\n\n // Skip creation if a client has already been wired in (testing).\n if (sqlite._client) return config;\n\n let client: SqliteClient | undefined;\n\n // Prefer env-injected client / factory (tests, dependency injection).\n if (isSqliteEnv(env)) {\n const envTyped = env as Env;\n if (envTyped.client) {\n client = envTyped.client;\n } else if (envTyped.SqliteDriver) {\n client = await envTyped.SqliteDriver(sqlite.url, sqlite.authToken);\n }\n }\n\n // Production path: load real driver based on the URL prefix.\n if (!client) {\n try {\n client = await createClientFromSettings(sqlite.url, sqlite.authToken);\n } catch (err) {\n logger.throw(\n `@walkeros/server-destination-sqlite: driver load failed: ${String(err)}`,\n );\n return config;\n }\n }\n\n // Legacy auto-create path. Only runs when the user opted in via the\n // deprecated `settings.sqlite.schema: 'auto'` field. New users run\n // `walkeros setup destination.<id>` (see setup.ts) or set `config.setup`.\n if (sqlite._legacyAutoCreate) {\n try {\n await client.execute(buildCreateTableSql(sqlite.table ?? 'events'));\n } catch (err) {\n logger.throw(\n `@walkeros/server-destination-sqlite: CREATE TABLE failed: ${String(err)}`,\n );\n return config;\n }\n } else if (!sqlite._legacySkipProbe) {\n // Modern path: assume the table exists. Probe once and hard-fail with\n // an actionable message if it does not. Legacy `schema: 'manual'` skips\n // this probe (user supplies their own table shape).\n let exists = false;\n try {\n exists = await tableExists(client, sqlite.table ?? 'events');\n } catch (err) {\n logger.throw(\n `@walkeros/server-destination-sqlite: table probe failed: ${String(err)}`,\n );\n return config;\n }\n if (!exists) {\n logger.throw(\n `SQLite table \"${sqlite.table ?? 'events'}\" not found in ${sqlite.url}. ` +\n `Run \"walkeros setup destination.${id}\" to create it.`,\n );\n return config;\n }\n }\n\n // Cache the prepared insert for per-event calls.\n sqlite._client = client;\n sqlite._runInsert = client.prepare(\n buildInsertSql(sqlite.table ?? 'events'),\n );\n sqlite._ownedClient = !(isSqliteEnv(env) && (env as Env).client);\n\n return config;\n },\n\n async push(event, context) {\n return await push(event, context);\n },\n\n async destroy({ config }) {\n const settings = config?.settings as Settings | undefined;\n const sqlite = settings?.sqlite;\n if (!sqlite) return;\n\n const client = sqlite._client;\n // Only close clients the destination created (not user-provided).\n if (client && sqlite._ownedClient) {\n try {\n await client.close();\n } finally {\n sqlite._client = undefined;\n sqlite._runInsert = undefined;\n sqlite._ownedClient = undefined;\n }\n }\n },\n};\n\nexport default destinationSQLite;\n"],"mappings":"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","names":["isObject","isString","isString","isString","buildCreateTableSql"]}
|
package/dist/walkerOS.json
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@walkeros/server-destination-sqlite",
|
|
3
3
|
"description": "SQLite server destination for walkerOS (local via better-sqlite3, remote via libSQL/Turso)",
|
|
4
|
-
"version": "4.
|
|
4
|
+
"version": "4.2.0-next-1780420352914",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"module": "./dist/index.mjs",
|
|
@@ -38,12 +38,12 @@
|
|
|
38
38
|
"update": "npx npm-check-updates -u && npm update"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@walkeros/core": "4.
|
|
42
|
-
"@walkeros/server-core": "4.
|
|
41
|
+
"@walkeros/core": "4.2.0-next-1780420352914",
|
|
42
|
+
"@walkeros/server-core": "4.2.0-next-1780420352914"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
|
-
"better-sqlite3": "^
|
|
46
|
-
"@libsql/client": "^0.
|
|
45
|
+
"better-sqlite3": "^12.0.0",
|
|
46
|
+
"@libsql/client": "^0.17.0"
|
|
47
47
|
},
|
|
48
48
|
"peerDependenciesMeta": {
|
|
49
49
|
"better-sqlite3": {
|
|
@@ -54,10 +54,10 @@
|
|
|
54
54
|
}
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
|
-
"@walkeros/collector": "4.
|
|
58
|
-
"better-sqlite3": "^
|
|
59
|
-
"@libsql/client": "^0.
|
|
60
|
-
"@types/better-sqlite3": "^7.6.
|
|
57
|
+
"@walkeros/collector": "4.2.0-next-1780420352914",
|
|
58
|
+
"better-sqlite3": "^12.0.0",
|
|
59
|
+
"@libsql/client": "^0.17.0",
|
|
60
|
+
"@types/better-sqlite3": "^7.6.13"
|
|
61
61
|
},
|
|
62
62
|
"repository": {
|
|
63
63
|
"url": "git+https://github.com/elbwalker/walkerOS.git",
|