awaitly-libsql 1.0.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -0
- package/dist/index.cjs +37 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +51 -6
- package/dist/index.d.ts +51 -6
- package/dist/index.js +38 -10
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -52,6 +52,19 @@ const store = await createLibSqlPersistence({
|
|
|
52
52
|
});
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
+
## Cross-Process Locking
|
|
56
|
+
|
|
57
|
+
To ensure only one process runs a given workflow ID at a time (when `durable.run` is used without `allowConcurrent: true`), pass the `lock` option. The store will implement `WorkflowLock` (lease + owner token):
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
const store = await createLibSqlPersistence({
|
|
61
|
+
url: "file:./awaitly.db",
|
|
62
|
+
lock: { lockTableName: "awaitly_workflow_lock" }, // optional; default table name
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// durable.run(..., { id, store }) will tryAcquire before running and release in finally
|
|
66
|
+
```
|
|
67
|
+
|
|
55
68
|
## Tenant-Aware Keying (Recommended)
|
|
56
69
|
|
|
57
70
|
To make it easier to avoid cross-tenant leaks in multi-tenant setups, use a
|
package/dist/index.cjs
CHANGED
|
@@ -1,23 +1,28 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var y=Object.defineProperty;var N=Object.getOwnPropertyDescriptor;var I=Object.getOwnPropertyNames;var _=Object.prototype.hasOwnProperty;var R=(n,e)=>{for(var t in e)y(n,t,{get:e[t],enumerable:!0})},q=(n,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of I(e))!_.call(n,s)&&s!==t&&y(n,s,{get:()=>e[s],enumerable:!(i=N(e,s))||i.enumerable});return n};var A=n=>q(y({},"__esModule",{value:!0}),n);var D={};R(D,{LibSqlKeyValueStore:()=>S,createLibSqlPersistence:()=>$});module.exports=A(D);var O=require("@libsql/client");var k=require("@libsql/client"),S=class{client;tableName;initialized=!1;initPromise=null;constructor(e={}){if(e.client)this.client=e.client;else{let i=e.url??"file:./awaitly.db";this.client=(0,k.createClient)({url:i,authToken:e.authToken})}let t=e.tableName??"awaitly_workflow_state";if(!/^[A-Za-z0-9_]+$/.test(t))throw new Error(`Invalid table name '${t}'. Only alphanumeric and underscore characters are allowed.`);this.tableName=t}async ensureInitialized(){if(!this.initialized)return this.initPromise?this.initPromise:(this.initPromise=(async()=>{try{await this.createTable(),this.initialized=!0}finally{this.initialized||(this.initPromise=null)}})(),this.initPromise)}async createTable(){await this.client.execute({sql:`
|
|
2
2
|
CREATE TABLE IF NOT EXISTS ${this.tableName} (
|
|
3
3
|
key TEXT PRIMARY KEY,
|
|
4
4
|
value TEXT NOT NULL,
|
|
5
|
-
expires_at TEXT
|
|
5
|
+
expires_at TEXT,
|
|
6
|
+
updated_at TEXT
|
|
6
7
|
);
|
|
7
8
|
`,args:[]}),await this.client.execute({sql:`
|
|
8
9
|
CREATE INDEX IF NOT EXISTS idx_${this.tableName}_expires_at
|
|
9
10
|
ON ${this.tableName}(expires_at);
|
|
10
|
-
`,args:[]})
|
|
11
|
+
`,args:[]}),await this.client.execute({sql:`
|
|
12
|
+
CREATE INDEX IF NOT EXISTS idx_${this.tableName}_updated_at
|
|
13
|
+
ON ${this.tableName}(updated_at);
|
|
14
|
+
`,args:[]});try{await this.client.execute({sql:`ALTER TABLE ${this.tableName} ADD COLUMN updated_at TEXT`,args:[]})}catch{}}patternToLike(e){return e.replace(/%/g,"\\%").replace(/_/g,"\\_").replace(/\*/g,"%")}async get(e){await this.ensureInitialized();let t=new Date().toISOString(),i=await this.client.execute({sql:`
|
|
11
15
|
SELECT value, expires_at
|
|
12
16
|
FROM ${this.tableName}
|
|
13
17
|
WHERE key = ?
|
|
14
|
-
`,args:[e]});if(i.rows.length===0)return null;let s=i.rows[0],
|
|
15
|
-
INSERT INTO ${this.tableName} (key, value, expires_at)
|
|
16
|
-
VALUES (?, ?, ?)
|
|
18
|
+
`,args:[e]});if(i.rows.length===0)return null;let s=i.rows[0],o=s.expires_at;return o&&o<=t?null:s.value??null}async set(e,t,i){await this.ensureInitialized();let s=i?.ttl&&i.ttl>0?new Date(Date.now()+i.ttl*1e3).toISOString():null,o=new Date().toISOString();await this.client.execute({sql:`
|
|
19
|
+
INSERT INTO ${this.tableName} (key, value, expires_at, updated_at)
|
|
20
|
+
VALUES (?, ?, ?, ?)
|
|
17
21
|
ON CONFLICT(key) DO UPDATE SET
|
|
18
22
|
value = excluded.value,
|
|
19
|
-
expires_at = excluded.expires_at
|
|
20
|
-
|
|
23
|
+
expires_at = excluded.expires_at,
|
|
24
|
+
updated_at = excluded.updated_at
|
|
25
|
+
`,args:[e,t,s,o]})}async delete(e){await this.ensureInitialized();let i=(await this.client.execute({sql:`DELETE FROM ${this.tableName} WHERE key = ?`,args:[e]})).rowsAffected;return typeof i=="number"?i>0:await this.get(e)===null}async exists(e){await this.ensureInitialized();let t=new Date().toISOString();return(await this.client.execute({sql:`
|
|
21
26
|
SELECT 1
|
|
22
27
|
FROM ${this.tableName}
|
|
23
28
|
WHERE key = ?
|
|
@@ -28,5 +33,28 @@
|
|
|
28
33
|
FROM ${this.tableName}
|
|
29
34
|
WHERE key LIKE ? ESCAPE '\\'
|
|
30
35
|
AND (expires_at IS NULL OR expires_at > ?)
|
|
31
|
-
`,args:[t,i]})).rows.map(
|
|
36
|
+
`,args:[t,i]})).rows.map(o=>o.key)}async listKeys(e,t={}){await this.ensureInitialized();let i=Math.min(Math.max(0,t.limit??100),1e4),s=Math.max(0,t.offset??0),o=t.orderBy==="key"?"key":"updated_at",w=t.orderDir==="asc"?"ASC":"DESC",u=this.patternToLike(e),d=new Date().toISOString(),m=["key LIKE ? ESCAPE '\\'","(expires_at IS NULL OR expires_at > ?)"],r=[u,d],l=3;t.updatedBefore!=null&&(m.push("updated_at < ?"),r.push(t.updatedBefore.toISOString()),l++),t.updatedAfter!=null&&(m.push("updated_at > ?"),r.push(t.updatedAfter.toISOString()),l++);let a=m.join(" AND "),c=o==="updated_at"?" NULLS LAST":"",p=[...r,i,s],g=`
|
|
37
|
+
SELECT key
|
|
38
|
+
FROM ${this.tableName}
|
|
39
|
+
WHERE ${a}
|
|
40
|
+
ORDER BY ${o} ${w}${c}
|
|
41
|
+
LIMIT ? OFFSET ?
|
|
42
|
+
`,E=(await this.client.execute({sql:g,args:p})).rows.map(b=>b.key),h;if(t.includeTotal===!0||s>0){let f=(await this.client.execute({sql:`SELECT COUNT(*) AS count FROM ${this.tableName} WHERE ${a}`,args:r})).rows[0];h=typeof f?.count=="number"?f.count:parseInt(String(f?.count??"0"),10)}return{keys:E,total:h}}async deleteMany(e){if(e.length===0)return 0;await this.ensureInitialized();let t=e.map(()=>"?").join(", ");return(await this.client.execute({sql:`DELETE FROM ${this.tableName} WHERE key IN (${t})`,args:e})).rowsAffected??0}async clear(){await this.ensureInitialized(),await this.client.execute({sql:`DELETE FROM ${this.tableName}`,args:[]})}async close(){}};var x=require("crypto");function P(n,e={}){let t=e.lockTableName??"awaitly_workflow_lock",i=t.replace(/[^a-zA-Z0-9_]/g,"_");async function s(){await n.execute({sql:`
|
|
43
|
+
CREATE TABLE IF NOT EXISTS ${t} (
|
|
44
|
+
workflow_id TEXT PRIMARY KEY,
|
|
45
|
+
owner_token TEXT NOT NULL,
|
|
46
|
+
expires_at TEXT NOT NULL
|
|
47
|
+
)
|
|
48
|
+
`,args:[]}),await n.execute({sql:`
|
|
49
|
+
CREATE INDEX IF NOT EXISTS idx_${i}_expires_at
|
|
50
|
+
ON ${t}(expires_at)
|
|
51
|
+
`,args:[]})}async function o(u,d){let m=d?.ttlMs??6e4,r=(0,x.randomUUID)(),l=new Date(Date.now()+m).toISOString();await s();let a=await n.execute({sql:`
|
|
52
|
+
INSERT INTO ${t} (workflow_id, owner_token, expires_at)
|
|
53
|
+
VALUES (?, ?, ?)
|
|
54
|
+
ON CONFLICT(workflow_id) DO UPDATE SET
|
|
55
|
+
owner_token = excluded.owner_token,
|
|
56
|
+
expires_at = excluded.expires_at
|
|
57
|
+
WHERE ${t}.expires_at < datetime('now')
|
|
58
|
+
RETURNING owner_token
|
|
59
|
+
`,args:[u,r,l]}),c=a.rows[0];return a.rows.length===1&&c?.owner_token===r?{ownerToken:r}:null}async function w(u,d){await n.execute({sql:`DELETE FROM ${t} WHERE workflow_id = ? AND owner_token = ?`,args:[u,d]})}return{tryAcquire:o,release:w,ensureLockTable:s}}var L=require("awaitly/persistence");async function $(n={}){let{prefix:e,lock:t,...i}=n,s=e??"workflow:state:",o=r=>r.slice(s.length),w=r=>`${s}${r}`,u=(r,l)=>Object.assign(r,{async listPage(a={}){let{keys:c,total:p}=await l.listKeys(`${s}*`,a),g=c.map(o),T=Math.min(Math.max(0,a.limit??100),1e4),E=g.length===T?(a.offset??0)+g.length:void 0;return{ids:g,total:p,nextOffset:E}},async deleteMany(a){if(a.length===0)return 0;let c=a.map(w);return l.deleteMany(c)},async clear(){return l.clear()}});if(t!==void 0){let r=i.client??(0,O.createClient)({url:i.url??"file:./awaitly.db",authToken:i.authToken}),l=new S({...i,client:r}),a=(0,L.createStatePersistence)(l,e),c=u(a,l),p=P(r,t);return Object.assign(c,{tryAcquire:p.tryAcquire.bind(p),release:p.release.bind(p)})}let d=new S(i),m=(0,L.createStatePersistence)(d,e);return u(m,d)}0&&(module.exports={LibSqlKeyValueStore,createLibSqlPersistence});
|
|
32
60
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/libsql-store.ts"],"sourcesContent":["/**\n * awaitly-libsql\n *\n * libSQL / SQLite persistence adapter for awaitly workflows.\n * Provides ready-to-use StatePersistence backed by libSQL.\n */\n\nimport { LibSqlKeyValueStore, type LibSqlKeyValueStoreOptions } from \"./libsql-store\";\nimport { createStatePersistence, type StatePersistence } from \"awaitly/persistence\";\n\n/**\n * Options for creating libSQL persistence.\n */\nexport interface LibSqlPersistenceOptions extends LibSqlKeyValueStoreOptions {\n /**\n * Key prefix for state entries.\n * @default \"workflow:state:\"\n */\n prefix?: string;\n}\n\n/**\n * Create a StatePersistence instance backed by libSQL / SQLite.\n *\n * The table is automatically created on first use.\n *\n * @param options - libSQL connection and configuration options\n * @returns StatePersistence instance ready to use with durable.run()\n *\n * @example\n * ```typescript\n * import { createLibSqlPersistence } from \"awaitly-libsql\";\n * import { durable } from \"awaitly/durable\";\n *\n * const store = await createLibSqlPersistence({\n * url: \"file:./awaitly.db\",\n * });\n *\n * const result = await durable.run(\n * { fetchUser, createOrder },\n * async (step, { fetchUser, createOrder }) => {\n * const user = await step(() => fetchUser(\"123\"), { key: \"fetch-user\" });\n * const order = await step(() => createOrder(user), { key: \"create-order\" });\n * return order;\n * },\n * {\n * id: \"checkout-123\",\n * store,\n * }\n * );\n * ```\n *\n * @example\n * ```typescript\n * // Using remote Turso (libSQL) instance\n * const store = await createLibSqlPersistence({\n * url: process.env.LIBSQL_URL!,\n * authToken: process.env.LIBSQL_AUTH_TOKEN,\n * tableName: \"awaitly_workflow_state\",\n * });\n * ```\n */\nexport async function createLibSqlPersistence(\n options: LibSqlPersistenceOptions = {}\n): Promise<\n StatePersistence & {\n loadRaw(runId: string): Promise<import(\"awaitly/persistence\").SerializedState | undefined>;\n }\n> {\n const { prefix, ...storeOptions } = options;\n\n const store = new LibSqlKeyValueStore(storeOptions);\n return createStatePersistence(store, prefix);\n}\n\n/**\n * libSQL KeyValueStore implementation.\n * Use this directly if you need more control over the store.\n *\n * @example\n * ```typescript\n * import { LibSqlKeyValueStore } from \"awaitly-libsql\";\n * import { createStatePersistence } from \"awaitly/persistence\";\n *\n * const store = new LibSqlKeyValueStore({\n * url: \"file:./awaitly.db\",\n * });\n *\n * const persistence = createStatePersistence(store, \"custom:prefix:\");\n * ```\n */\nexport { LibSqlKeyValueStore, type LibSqlKeyValueStoreOptions };\n\n","/**\n * awaitly-libsql\n *\n * libSQL / SQLite KeyValueStore implementation for awaitly persistence.\n */\n\nimport { createClient, type Client } from \"@libsql/client\";\nimport type { KeyValueStore } from \"awaitly/persistence\";\n\n/**\n * Options for libSQL / SQLite KeyValueStore.\n */\nexport interface LibSqlKeyValueStoreOptions {\n /**\n * libSQL database URL.\n *\n * Examples:\n * - \"file:./awaitly.db\" (local file)\n * - \":memory:\" (in-memory, for tests)\n * - \"libsql://your-db.turso.io\" (remote)\n *\n * @default \"file:./awaitly.db\"\n */\n url?: string;\n\n /**\n * Authentication token for remote libSQL databases (e.g. Turso).\n */\n authToken?: string;\n\n /**\n * Table name for storing key-value pairs.\n * @default \"awaitly_workflow_state\"\n */\n tableName?: string;\n\n /**\n * Existing libSQL client to use.\n * If provided, url/authToken options are ignored.\n */\n client?: Client;\n}\n\n/**\n * libSQL / SQLite implementation of KeyValueStore.\n *\n * Automatically creates the required table on first use.\n * Supports TTL via ISO 8601 `expires_at` column.\n */\nexport class LibSqlKeyValueStore implements KeyValueStore {\n private client: Client;\n private tableName: string;\n private initialized = false;\n private initPromise: Promise<void> | null = null;\n\n constructor(options: LibSqlKeyValueStoreOptions = {}) {\n if (options.client) {\n this.client = options.client;\n } else {\n const url = options.url ?? \"file:./awaitly.db\";\n this.client = createClient({\n url,\n authToken: options.authToken,\n });\n }\n\n const tableName = options.tableName ?? \"awaitly_workflow_state\";\n if (!/^[A-Za-z0-9_]+$/.test(tableName)) {\n throw new Error(\n `Invalid table name '${tableName}'. Only alphanumeric and underscore characters are allowed.`\n );\n }\n this.tableName = tableName;\n }\n\n /**\n * Initialize the store by creating the table and index if they don't exist.\n * This is called automatically on first use.\n */\n private async ensureInitialized(): Promise<void> {\n if (this.initialized) {\n return;\n }\n\n if (this.initPromise) {\n return this.initPromise;\n }\n\n this.initPromise = (async () => {\n try {\n await this.createTable();\n this.initialized = true;\n } finally {\n if (!this.initialized) {\n this.initPromise = null;\n }\n }\n })();\n\n return this.initPromise;\n }\n\n /**\n * Create the table and index if they don't exist.\n */\n private async createTable(): Promise<void> {\n // SQLite / libSQL: execute schema changes as separate statements\n await this.client.execute({\n sql: `\n CREATE TABLE IF NOT EXISTS ${this.tableName} (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL,\n expires_at TEXT\n );\n `,\n args: [],\n });\n\n await this.client.execute({\n sql: `\n CREATE INDEX IF NOT EXISTS idx_${this.tableName}_expires_at\n ON ${this.tableName}(expires_at);\n `,\n args: [],\n });\n }\n\n /**\n * Convert glob pattern to SQL LIKE pattern.\n * Supports * wildcard (matches any characters).\n */\n private patternToLike(pattern: string): string {\n // Escape LIKE special characters and convert * to %\n return pattern.replace(/%/g, \"\\\\%\").replace(/_/g, \"\\\\_\").replace(/\\*/g, \"%\");\n }\n\n async get(key: string): Promise<string | null> {\n await this.ensureInitialized();\n\n const nowIso = new Date().toISOString();\n const result = await this.client.execute({\n sql: `\n SELECT value, expires_at\n FROM ${this.tableName}\n WHERE key = ?\n `,\n args: [key],\n });\n\n if (result.rows.length === 0) {\n return null;\n }\n\n const row = result.rows[0] as Record<string, unknown>;\n const expiresAt = row[\"expires_at\"] as string | null | undefined;\n\n if (expiresAt && expiresAt <= nowIso) {\n // Expired - behave as if key doesn't exist\n return null;\n }\n\n return (row[\"value\"] as string) ?? null;\n }\n\n async set(key: string, value: string, options?: { ttl?: number }): Promise<void> {\n await this.ensureInitialized();\n\n const expiresAt =\n options?.ttl && options.ttl > 0\n ? new Date(Date.now() + options.ttl * 1000).toISOString()\n : null;\n\n await this.client.execute({\n sql: `\n INSERT INTO ${this.tableName} (key, value, expires_at)\n VALUES (?, ?, ?)\n ON CONFLICT(key) DO UPDATE SET\n value = excluded.value,\n expires_at = excluded.expires_at\n `,\n args: [key, value, expiresAt],\n });\n }\n\n async delete(key: string): Promise<boolean> {\n await this.ensureInitialized();\n\n const result = await this.client.execute({\n sql: `DELETE FROM ${this.tableName} WHERE key = ?`,\n args: [key],\n });\n\n // libSQL .rowsAffected is available on hrana responses; fall back to > 0 check\n const affected: number | undefined = result.rowsAffected;\n if (typeof affected === \"number\") {\n return affected > 0;\n }\n\n // If rowsAffected is not available, perform an existence check as a fallback\n const after = await this.get(key);\n return after === null;\n }\n\n async exists(key: string): Promise<boolean> {\n await this.ensureInitialized();\n\n const nowIso = new Date().toISOString();\n const result = await this.client.execute({\n sql: `\n SELECT 1\n FROM ${this.tableName}\n WHERE key = ?\n AND (expires_at IS NULL OR expires_at > ?)\n LIMIT 1\n `,\n args: [key, nowIso],\n });\n\n return result.rows.length > 0;\n }\n\n async keys(pattern: string): Promise<string[]> {\n await this.ensureInitialized();\n\n const likePattern = this.patternToLike(pattern);\n const nowIso = new Date().toISOString();\n\n const result = await this.client.execute({\n sql: `\n SELECT key\n FROM ${this.tableName}\n WHERE key LIKE ? ESCAPE '\\\\'\n AND (expires_at IS NULL OR expires_at > ?)\n `,\n args: [likePattern, nowIso],\n });\n\n return result.rows.map((row) => (row as Record<string, unknown>)[\"key\"] as string);\n }\n\n /**\n * Close the underlying client if it supports close().\n */\n async close(): Promise<void> {\n // libSQL client doesn't expose a close API in all runtimes; no-op for now.\n // If a future version adds client.close(), it can be wired here.\n }\n}\n\n"],"mappings":"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","names":["index_exports","__export","LibSqlKeyValueStore","createLibSqlPersistence","__toCommonJS","import_client","LibSqlKeyValueStore","options","url","tableName","pattern","key","nowIso","result","row","expiresAt","value","affected","likePattern","import_persistence","createLibSqlPersistence","options","prefix","storeOptions","store","LibSqlKeyValueStore"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/libsql-store.ts","../src/libsql-lock.ts"],"sourcesContent":["/**\n * awaitly-libsql\n *\n * libSQL / SQLite persistence adapter for awaitly workflows.\n * Provides ready-to-use StatePersistence backed by libSQL.\n */\n\nimport { createClient } from \"@libsql/client\";\nimport { LibSqlKeyValueStore, type LibSqlKeyValueStoreOptions } from \"./libsql-store\";\nimport { createLibSqlLock, type LibSqlLockOptions } from \"./libsql-lock\";\nimport {\n createStatePersistence,\n type StatePersistence,\n type SerializedState,\n type ListPageOptions,\n type ListPageResult,\n} from \"awaitly/persistence\";\nimport type { WorkflowLock } from \"awaitly/durable\";\n\n/**\n * Options for cross-process locking (lease + owner token).\n * When set, the returned store implements WorkflowLock so only one process\n * runs a given workflow ID at a time (when durable.run allowConcurrent is false).\n */\nexport type { LibSqlLockOptions } from \"./libsql-lock\";\n\n/**\n * Options for creating libSQL persistence.\n */\nexport interface LibSqlPersistenceOptions extends LibSqlKeyValueStoreOptions {\n /**\n * Key prefix for state entries.\n * @default \"workflow:state:\"\n */\n prefix?: string;\n\n /**\n * When set, the store implements WorkflowLock for cross-process concurrency control.\n * Uses a lease (TTL) + owner token; release verifies the token.\n */\n lock?: LibSqlLockOptions;\n}\n\n/**\n * Create a StatePersistence instance backed by libSQL / SQLite.\n *\n * The table is automatically created on first use.\n *\n * @param options - libSQL connection and configuration options\n * @returns StatePersistence instance ready to use with durable.run()\n *\n * @example\n * ```typescript\n * import { createLibSqlPersistence } from \"awaitly-libsql\";\n * import { durable } from \"awaitly/durable\";\n *\n * const store = await createLibSqlPersistence({\n * url: \"file:./awaitly.db\",\n * });\n *\n * const result = await durable.run(\n * { fetchUser, createOrder },\n * async (step, { fetchUser, createOrder }) => {\n * const user = await step(() => fetchUser(\"123\"), { key: \"fetch-user\" });\n * const order = await step(() => createOrder(user), { key: \"create-order\" });\n * return order;\n * },\n * {\n * id: \"checkout-123\",\n * store,\n * }\n * );\n * ```\n *\n * @example\n * ```typescript\n * // Using remote Turso (libSQL) instance\n * const store = await createLibSqlPersistence({\n * url: process.env.LIBSQL_URL!,\n * authToken: process.env.LIBSQL_AUTH_TOKEN,\n * tableName: \"awaitly_workflow_state\",\n * });\n * ```\n */\nexport type LibSqlStatePersistence = StatePersistence & {\n loadRaw(runId: string): Promise<SerializedState | undefined>;\n listPage(options?: ListPageOptions): Promise<ListPageResult>;\n deleteMany(ids: string[]): Promise<number>;\n clear(): Promise<void>;\n};\n\nexport type LibSqlStatePersistenceWithLock = LibSqlStatePersistence & WorkflowLock;\n\nexport async function createLibSqlPersistence(\n options: LibSqlPersistenceOptions = {}\n): Promise<LibSqlStatePersistence | LibSqlStatePersistenceWithLock> {\n const { prefix, lock: lockOptions, ...storeOptions } = options;\n\n const effectivePrefix = prefix ?? \"workflow:state:\";\n const stripPrefix = (key: string): string => key.slice(effectivePrefix.length);\n const prefixKey = (runId: string): string => `${effectivePrefix}${runId}`;\n\n const addExtensions = (\n base: StatePersistence & { loadRaw(runId: string): Promise<SerializedState | undefined> },\n store: LibSqlKeyValueStore\n ): LibSqlStatePersistence =>\n Object.assign(base, {\n async listPage(options: ListPageOptions = {}): Promise<ListPageResult> {\n const { keys, total } = await store.listKeys(`${effectivePrefix}*`, options);\n const ids = keys.map(stripPrefix);\n const limit = Math.min(Math.max(0, options.limit ?? 100), 10_000);\n const nextOffset =\n ids.length === limit ? (options.offset ?? 0) + ids.length : undefined;\n return { ids, total, nextOffset };\n },\n async deleteMany(ids: string[]): Promise<number> {\n if (ids.length === 0) return 0;\n const keys = ids.map(prefixKey);\n return store.deleteMany(keys);\n },\n async clear(): Promise<void> {\n return store.clear();\n },\n });\n\n if (lockOptions !== undefined) {\n const client =\n storeOptions.client ??\n createClient({\n url: storeOptions.url ?? \"file:./awaitly.db\",\n authToken: storeOptions.authToken,\n });\n const store = new LibSqlKeyValueStore({ ...storeOptions, client });\n const base = createStatePersistence(store, prefix) as LibSqlStatePersistence;\n const persistence = addExtensions(\n base as StatePersistence & { loadRaw(runId: string): Promise<SerializedState | undefined> },\n store\n );\n const lock = createLibSqlLock(client, lockOptions);\n return Object.assign(persistence, {\n tryAcquire: lock.tryAcquire.bind(lock),\n release: lock.release.bind(lock),\n });\n }\n\n const store = new LibSqlKeyValueStore(storeOptions);\n const base = createStatePersistence(store, prefix);\n return addExtensions(\n base as StatePersistence & { loadRaw(runId: string): Promise<SerializedState | undefined> },\n store\n );\n}\n\n/**\n * libSQL KeyValueStore implementation.\n * Use this directly if you need more control over the store.\n *\n * @example\n * ```typescript\n * import { LibSqlKeyValueStore } from \"awaitly-libsql\";\n * import { createStatePersistence } from \"awaitly/persistence\";\n *\n * const store = new LibSqlKeyValueStore({\n * url: \"file:./awaitly.db\",\n * });\n *\n * const persistence = createStatePersistence(store, \"custom:prefix:\");\n * ```\n */\nexport { LibSqlKeyValueStore, type LibSqlKeyValueStoreOptions };\n\n","/**\n * awaitly-libsql\n *\n * libSQL / SQLite KeyValueStore implementation for awaitly persistence.\n */\n\nimport { createClient, type Client } from \"@libsql/client\";\nimport type { KeyValueStore, ListPageOptions } from \"awaitly/persistence\";\n\n/**\n * Options for libSQL / SQLite KeyValueStore.\n */\nexport interface LibSqlKeyValueStoreOptions {\n /**\n * libSQL database URL.\n *\n * Examples:\n * - \"file:./awaitly.db\" (local file)\n * - \":memory:\" (in-memory, for tests)\n * - \"libsql://your-db.turso.io\" (remote)\n *\n * @default \"file:./awaitly.db\"\n */\n url?: string;\n\n /**\n * Authentication token for remote libSQL databases (e.g. Turso).\n */\n authToken?: string;\n\n /**\n * Table name for storing key-value pairs.\n * @default \"awaitly_workflow_state\"\n */\n tableName?: string;\n\n /**\n * Existing libSQL client to use.\n * If provided, url/authToken options are ignored.\n */\n client?: Client;\n}\n\n/**\n * libSQL / SQLite implementation of KeyValueStore.\n *\n * Automatically creates the required table on first use.\n * Supports TTL via ISO 8601 `expires_at` column.\n */\nexport class LibSqlKeyValueStore implements KeyValueStore {\n private client: Client;\n private tableName: string;\n private initialized = false;\n private initPromise: Promise<void> | null = null;\n\n constructor(options: LibSqlKeyValueStoreOptions = {}) {\n if (options.client) {\n this.client = options.client;\n } else {\n const url = options.url ?? \"file:./awaitly.db\";\n this.client = createClient({\n url,\n authToken: options.authToken,\n });\n }\n\n const tableName = options.tableName ?? \"awaitly_workflow_state\";\n if (!/^[A-Za-z0-9_]+$/.test(tableName)) {\n throw new Error(\n `Invalid table name '${tableName}'. Only alphanumeric and underscore characters are allowed.`\n );\n }\n this.tableName = tableName;\n }\n\n /**\n * Initialize the store by creating the table and index if they don't exist.\n * This is called automatically on first use.\n */\n private async ensureInitialized(): Promise<void> {\n if (this.initialized) {\n return;\n }\n\n if (this.initPromise) {\n return this.initPromise;\n }\n\n this.initPromise = (async () => {\n try {\n await this.createTable();\n this.initialized = true;\n } finally {\n if (!this.initialized) {\n this.initPromise = null;\n }\n }\n })();\n\n return this.initPromise;\n }\n\n /**\n * Create the table and index if they don't exist.\n */\n private async createTable(): Promise<void> {\n // SQLite / libSQL: execute schema changes as separate statements\n await this.client.execute({\n sql: `\n CREATE TABLE IF NOT EXISTS ${this.tableName} (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL,\n expires_at TEXT,\n updated_at TEXT\n );\n `,\n args: [],\n });\n\n await this.client.execute({\n sql: `\n CREATE INDEX IF NOT EXISTS idx_${this.tableName}_expires_at\n ON ${this.tableName}(expires_at);\n `,\n args: [],\n });\n\n await this.client.execute({\n sql: `\n CREATE INDEX IF NOT EXISTS idx_${this.tableName}_updated_at\n ON ${this.tableName}(updated_at);\n `,\n args: [],\n });\n\n // Add updated_at to existing tables (ignore if column already exists)\n try {\n await this.client.execute({\n sql: `ALTER TABLE ${this.tableName} ADD COLUMN updated_at TEXT`,\n args: [],\n });\n } catch {\n // Column may already exist\n }\n }\n\n /**\n * Convert glob pattern to SQL LIKE pattern.\n * Supports * wildcard (matches any characters).\n */\n private patternToLike(pattern: string): string {\n // Escape LIKE special characters and convert * to %\n return pattern.replace(/%/g, \"\\\\%\").replace(/_/g, \"\\\\_\").replace(/\\*/g, \"%\");\n }\n\n async get(key: string): Promise<string | null> {\n await this.ensureInitialized();\n\n const nowIso = new Date().toISOString();\n const result = await this.client.execute({\n sql: `\n SELECT value, expires_at\n FROM ${this.tableName}\n WHERE key = ?\n `,\n args: [key],\n });\n\n if (result.rows.length === 0) {\n return null;\n }\n\n const row = result.rows[0] as Record<string, unknown>;\n const expiresAt = row[\"expires_at\"] as string | null | undefined;\n\n if (expiresAt && expiresAt <= nowIso) {\n // Expired - behave as if key doesn't exist\n return null;\n }\n\n return (row[\"value\"] as string) ?? null;\n }\n\n async set(key: string, value: string, options?: { ttl?: number }): Promise<void> {\n await this.ensureInitialized();\n\n const expiresAt =\n options?.ttl && options.ttl > 0\n ? new Date(Date.now() + options.ttl * 1000).toISOString()\n : null;\n const updatedAt = new Date().toISOString();\n\n await this.client.execute({\n sql: `\n INSERT INTO ${this.tableName} (key, value, expires_at, updated_at)\n VALUES (?, ?, ?, ?)\n ON CONFLICT(key) DO UPDATE SET\n value = excluded.value,\n expires_at = excluded.expires_at,\n updated_at = excluded.updated_at\n `,\n args: [key, value, expiresAt, updatedAt],\n });\n }\n\n async delete(key: string): Promise<boolean> {\n await this.ensureInitialized();\n\n const result = await this.client.execute({\n sql: `DELETE FROM ${this.tableName} WHERE key = ?`,\n args: [key],\n });\n\n // libSQL .rowsAffected is available on hrana responses; fall back to > 0 check\n const affected: number | undefined = result.rowsAffected;\n if (typeof affected === \"number\") {\n return affected > 0;\n }\n\n // If rowsAffected is not available, perform an existence check as a fallback\n const after = await this.get(key);\n return after === null;\n }\n\n async exists(key: string): Promise<boolean> {\n await this.ensureInitialized();\n\n const nowIso = new Date().toISOString();\n const result = await this.client.execute({\n sql: `\n SELECT 1\n FROM ${this.tableName}\n WHERE key = ?\n AND (expires_at IS NULL OR expires_at > ?)\n LIMIT 1\n `,\n args: [key, nowIso],\n });\n\n return result.rows.length > 0;\n }\n\n async keys(pattern: string): Promise<string[]> {\n await this.ensureInitialized();\n\n const likePattern = this.patternToLike(pattern);\n const nowIso = new Date().toISOString();\n\n const result = await this.client.execute({\n sql: `\n SELECT key\n FROM ${this.tableName}\n WHERE key LIKE ? ESCAPE '\\\\'\n AND (expires_at IS NULL OR expires_at > ?)\n `,\n args: [likePattern, nowIso],\n });\n\n return result.rows.map((row) => (row as Record<string, unknown>)[\"key\"] as string);\n }\n\n /**\n * List keys with pagination, filtering, and ordering.\n */\n async listKeys(\n pattern: string,\n options: ListPageOptions = {}\n ): Promise<{ keys: string[]; total?: number }> {\n await this.ensureInitialized();\n\n const limit = Math.min(Math.max(0, options.limit ?? 100), 10_000);\n const offset = Math.max(0, options.offset ?? 0);\n const orderBy = options.orderBy === \"key\" ? \"key\" : \"updated_at\";\n const orderDir = options.orderDir === \"asc\" ? \"ASC\" : \"DESC\";\n const likePattern = this.patternToLike(pattern);\n const nowIso = new Date().toISOString();\n\n const conditions: string[] = [\n \"key LIKE ? ESCAPE '\\\\'\",\n \"(expires_at IS NULL OR expires_at > ?)\",\n ];\n const args: (string | number)[] = [likePattern, nowIso];\n let paramIndex = 3;\n\n if (options.updatedBefore != null) {\n conditions.push(`updated_at < ?`);\n args.push(options.updatedBefore.toISOString());\n paramIndex++;\n }\n if (options.updatedAfter != null) {\n conditions.push(`updated_at > ?`);\n args.push(options.updatedAfter.toISOString());\n paramIndex++;\n }\n\n const whereClause = conditions.join(\" AND \");\n const orderNulls = orderBy === \"updated_at\" ? \" NULLS LAST\" : \"\";\n\n const listArgs = [...args, limit, offset];\n const listQuery = `\n SELECT key\n FROM ${this.tableName}\n WHERE ${whereClause}\n ORDER BY ${orderBy} ${orderDir}${orderNulls}\n LIMIT ? OFFSET ?\n `;\n\n const result = await this.client.execute({\n sql: listQuery,\n args: listArgs,\n });\n const keys = result.rows.map((row) => (row as Record<string, unknown>)[\"key\"] as string);\n\n let total: number | undefined;\n if (options.includeTotal === true || offset > 0) {\n const countResult = await this.client.execute({\n sql: `SELECT COUNT(*) AS count FROM ${this.tableName} WHERE ${whereClause}`,\n args,\n });\n const countRow = countResult.rows[0] as Record<string, unknown>;\n total = typeof countRow?.count === \"number\" ? countRow.count : parseInt(String(countRow?.count ?? \"0\"), 10);\n }\n\n return { keys, total };\n }\n\n /**\n * Delete multiple keys in one round-trip.\n */\n async deleteMany(keys: string[]): Promise<number> {\n if (keys.length === 0) return 0;\n await this.ensureInitialized();\n const placeholders = keys.map(() => \"?\").join(\", \");\n const result = await this.client.execute({\n sql: `DELETE FROM ${this.tableName} WHERE key IN (${placeholders})`,\n args: keys,\n });\n return result.rowsAffected ?? 0;\n }\n\n /**\n * Remove all entries from the table (clear all workflow state).\n */\n async clear(): Promise<void> {\n await this.ensureInitialized();\n await this.client.execute({\n sql: `DELETE FROM ${this.tableName}`,\n args: [],\n });\n }\n\n /**\n * Close the underlying client if it supports close().\n */\n async close(): Promise<void> {\n // libSQL client doesn't expose a close API in all runtimes; no-op for now.\n // If a future version adds client.close(), it can be wired here.\n }\n}\n\n","/**\n * libSQL workflow lock (lease) for cross-process concurrency control.\n * Uses a lease (TTL) + owner token; release verifies the token.\n */\n\nimport type { Client } from \"@libsql/client\";\nimport { randomUUID } from \"node:crypto\";\n\nexport interface LibSqlLockOptions {\n /**\n * Table name for workflow locks.\n * @default 'awaitly_workflow_lock'\n */\n lockTableName?: string;\n}\n\n/**\n * Create tryAcquire and release functions that use a libSQL lock table.\n * Caller must pass the same client used for state (so one connection).\n */\nexport function createLibSqlLock(\n client: Client,\n options: LibSqlLockOptions = {}\n): {\n tryAcquire(\n id: string,\n opts?: { ttlMs?: number }\n ): Promise<{ ownerToken: string } | null>;\n release(id: string, ownerToken: string): Promise<void>;\n ensureLockTable(): Promise<void>;\n} {\n const lockTableName = options.lockTableName ?? \"awaitly_workflow_lock\";\n\n const safeTableName = lockTableName.replace(/[^a-zA-Z0-9_]/g, \"_\");\n\n async function ensureLockTable(): Promise<void> {\n await client.execute({\n sql: `\n CREATE TABLE IF NOT EXISTS ${lockTableName} (\n workflow_id TEXT PRIMARY KEY,\n owner_token TEXT NOT NULL,\n expires_at TEXT NOT NULL\n )\n `,\n args: [],\n });\n await client.execute({\n sql: `\n CREATE INDEX IF NOT EXISTS idx_${safeTableName}_expires_at\n ON ${lockTableName}(expires_at)\n `,\n args: [],\n });\n }\n\n async function tryAcquire(\n id: string,\n opts?: { ttlMs?: number }\n ): Promise<{ ownerToken: string } | null> {\n const ttlMs = opts?.ttlMs ?? 60_000;\n const ownerToken = randomUUID();\n const expiresAt = new Date(Date.now() + ttlMs).toISOString();\n\n await ensureLockTable();\n\n // Insert new row or update only if current row is expired (or missing).\n // SQLite 3.35+ / libSQL support RETURNING.\n const result = await client.execute({\n sql: `\n INSERT INTO ${lockTableName} (workflow_id, owner_token, expires_at)\n VALUES (?, ?, ?)\n ON CONFLICT(workflow_id) DO UPDATE SET\n owner_token = excluded.owner_token,\n expires_at = excluded.expires_at\n WHERE ${lockTableName}.expires_at < datetime('now')\n RETURNING owner_token\n `,\n args: [id, ownerToken, expiresAt],\n });\n\n const row = result.rows[0] as Record<string, unknown> | undefined;\n if (result.rows.length === 1 && row?.owner_token === ownerToken) {\n return { ownerToken };\n }\n return null;\n }\n\n async function release(id: string, ownerToken: string): Promise<void> {\n await client.execute({\n sql: `DELETE FROM ${lockTableName} WHERE workflow_id = ? AND owner_token = ?`,\n args: [id, ownerToken],\n });\n }\n\n return { tryAcquire, release, ensureLockTable };\n}\n"],"mappings":"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","names":["index_exports","__export","LibSqlKeyValueStore","createLibSqlPersistence","__toCommonJS","import_client","import_client","LibSqlKeyValueStore","options","url","tableName","pattern","key","nowIso","result","row","expiresAt","value","updatedAt","affected","likePattern","limit","offset","orderBy","orderDir","conditions","args","paramIndex","whereClause","orderNulls","listArgs","listQuery","keys","total","countRow","placeholders","import_node_crypto","createLibSqlLock","client","options","lockTableName","safeTableName","ensureLockTable","tryAcquire","id","opts","ttlMs","ownerToken","expiresAt","result","row","release","import_persistence","createLibSqlPersistence","options","prefix","lockOptions","storeOptions","effectivePrefix","stripPrefix","key","prefixKey","runId","addExtensions","base","store","keys","total","ids","limit","nextOffset","client","LibSqlKeyValueStore","persistence","lock","createLibSqlLock"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import * as awaitly_persistence from 'awaitly/persistence';
|
|
2
|
-
import { KeyValueStore, StatePersistence } from 'awaitly/persistence';
|
|
3
1
|
import { Client } from '@libsql/client';
|
|
2
|
+
import { KeyValueStore, ListPageOptions, StatePersistence, SerializedState, ListPageResult } from 'awaitly/persistence';
|
|
3
|
+
import { WorkflowLock } from 'awaitly/durable';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* awaitly-libsql
|
|
@@ -71,12 +71,47 @@ declare class LibSqlKeyValueStore implements KeyValueStore {
|
|
|
71
71
|
delete(key: string): Promise<boolean>;
|
|
72
72
|
exists(key: string): Promise<boolean>;
|
|
73
73
|
keys(pattern: string): Promise<string[]>;
|
|
74
|
+
/**
|
|
75
|
+
* List keys with pagination, filtering, and ordering.
|
|
76
|
+
*/
|
|
77
|
+
listKeys(pattern: string, options?: ListPageOptions): Promise<{
|
|
78
|
+
keys: string[];
|
|
79
|
+
total?: number;
|
|
80
|
+
}>;
|
|
81
|
+
/**
|
|
82
|
+
* Delete multiple keys in one round-trip.
|
|
83
|
+
*/
|
|
84
|
+
deleteMany(keys: string[]): Promise<number>;
|
|
85
|
+
/**
|
|
86
|
+
* Remove all entries from the table (clear all workflow state).
|
|
87
|
+
*/
|
|
88
|
+
clear(): Promise<void>;
|
|
74
89
|
/**
|
|
75
90
|
* Close the underlying client if it supports close().
|
|
76
91
|
*/
|
|
77
92
|
close(): Promise<void>;
|
|
78
93
|
}
|
|
79
94
|
|
|
95
|
+
/**
|
|
96
|
+
* libSQL workflow lock (lease) for cross-process concurrency control.
|
|
97
|
+
* Uses a lease (TTL) + owner token; release verifies the token.
|
|
98
|
+
*/
|
|
99
|
+
|
|
100
|
+
interface LibSqlLockOptions {
|
|
101
|
+
/**
|
|
102
|
+
* Table name for workflow locks.
|
|
103
|
+
* @default 'awaitly_workflow_lock'
|
|
104
|
+
*/
|
|
105
|
+
lockTableName?: string;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* awaitly-libsql
|
|
110
|
+
*
|
|
111
|
+
* libSQL / SQLite persistence adapter for awaitly workflows.
|
|
112
|
+
* Provides ready-to-use StatePersistence backed by libSQL.
|
|
113
|
+
*/
|
|
114
|
+
|
|
80
115
|
/**
|
|
81
116
|
* Options for creating libSQL persistence.
|
|
82
117
|
*/
|
|
@@ -86,6 +121,11 @@ interface LibSqlPersistenceOptions extends LibSqlKeyValueStoreOptions {
|
|
|
86
121
|
* @default "workflow:state:"
|
|
87
122
|
*/
|
|
88
123
|
prefix?: string;
|
|
124
|
+
/**
|
|
125
|
+
* When set, the store implements WorkflowLock for cross-process concurrency control.
|
|
126
|
+
* Uses a lease (TTL) + owner token; release verifies the token.
|
|
127
|
+
*/
|
|
128
|
+
lock?: LibSqlLockOptions;
|
|
89
129
|
}
|
|
90
130
|
/**
|
|
91
131
|
* Create a StatePersistence instance backed by libSQL / SQLite.
|
|
@@ -128,8 +168,13 @@ interface LibSqlPersistenceOptions extends LibSqlKeyValueStoreOptions {
|
|
|
128
168
|
* });
|
|
129
169
|
* ```
|
|
130
170
|
*/
|
|
131
|
-
|
|
132
|
-
loadRaw(runId: string): Promise<
|
|
133
|
-
|
|
171
|
+
type LibSqlStatePersistence = StatePersistence & {
|
|
172
|
+
loadRaw(runId: string): Promise<SerializedState | undefined>;
|
|
173
|
+
listPage(options?: ListPageOptions): Promise<ListPageResult>;
|
|
174
|
+
deleteMany(ids: string[]): Promise<number>;
|
|
175
|
+
clear(): Promise<void>;
|
|
176
|
+
};
|
|
177
|
+
type LibSqlStatePersistenceWithLock = LibSqlStatePersistence & WorkflowLock;
|
|
178
|
+
declare function createLibSqlPersistence(options?: LibSqlPersistenceOptions): Promise<LibSqlStatePersistence | LibSqlStatePersistenceWithLock>;
|
|
134
179
|
|
|
135
|
-
export { LibSqlKeyValueStore, type LibSqlKeyValueStoreOptions, type LibSqlPersistenceOptions, createLibSqlPersistence };
|
|
180
|
+
export { LibSqlKeyValueStore, type LibSqlKeyValueStoreOptions, type LibSqlLockOptions, type LibSqlPersistenceOptions, type LibSqlStatePersistence, type LibSqlStatePersistenceWithLock, createLibSqlPersistence };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import * as awaitly_persistence from 'awaitly/persistence';
|
|
2
|
-
import { KeyValueStore, StatePersistence } from 'awaitly/persistence';
|
|
3
1
|
import { Client } from '@libsql/client';
|
|
2
|
+
import { KeyValueStore, ListPageOptions, StatePersistence, SerializedState, ListPageResult } from 'awaitly/persistence';
|
|
3
|
+
import { WorkflowLock } from 'awaitly/durable';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* awaitly-libsql
|
|
@@ -71,12 +71,47 @@ declare class LibSqlKeyValueStore implements KeyValueStore {
|
|
|
71
71
|
delete(key: string): Promise<boolean>;
|
|
72
72
|
exists(key: string): Promise<boolean>;
|
|
73
73
|
keys(pattern: string): Promise<string[]>;
|
|
74
|
+
/**
|
|
75
|
+
* List keys with pagination, filtering, and ordering.
|
|
76
|
+
*/
|
|
77
|
+
listKeys(pattern: string, options?: ListPageOptions): Promise<{
|
|
78
|
+
keys: string[];
|
|
79
|
+
total?: number;
|
|
80
|
+
}>;
|
|
81
|
+
/**
|
|
82
|
+
* Delete multiple keys in one round-trip.
|
|
83
|
+
*/
|
|
84
|
+
deleteMany(keys: string[]): Promise<number>;
|
|
85
|
+
/**
|
|
86
|
+
* Remove all entries from the table (clear all workflow state).
|
|
87
|
+
*/
|
|
88
|
+
clear(): Promise<void>;
|
|
74
89
|
/**
|
|
75
90
|
* Close the underlying client if it supports close().
|
|
76
91
|
*/
|
|
77
92
|
close(): Promise<void>;
|
|
78
93
|
}
|
|
79
94
|
|
|
95
|
+
/**
|
|
96
|
+
* libSQL workflow lock (lease) for cross-process concurrency control.
|
|
97
|
+
* Uses a lease (TTL) + owner token; release verifies the token.
|
|
98
|
+
*/
|
|
99
|
+
|
|
100
|
+
interface LibSqlLockOptions {
|
|
101
|
+
/**
|
|
102
|
+
* Table name for workflow locks.
|
|
103
|
+
* @default 'awaitly_workflow_lock'
|
|
104
|
+
*/
|
|
105
|
+
lockTableName?: string;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* awaitly-libsql
|
|
110
|
+
*
|
|
111
|
+
* libSQL / SQLite persistence adapter for awaitly workflows.
|
|
112
|
+
* Provides ready-to-use StatePersistence backed by libSQL.
|
|
113
|
+
*/
|
|
114
|
+
|
|
80
115
|
/**
|
|
81
116
|
* Options for creating libSQL persistence.
|
|
82
117
|
*/
|
|
@@ -86,6 +121,11 @@ interface LibSqlPersistenceOptions extends LibSqlKeyValueStoreOptions {
|
|
|
86
121
|
* @default "workflow:state:"
|
|
87
122
|
*/
|
|
88
123
|
prefix?: string;
|
|
124
|
+
/**
|
|
125
|
+
* When set, the store implements WorkflowLock for cross-process concurrency control.
|
|
126
|
+
* Uses a lease (TTL) + owner token; release verifies the token.
|
|
127
|
+
*/
|
|
128
|
+
lock?: LibSqlLockOptions;
|
|
89
129
|
}
|
|
90
130
|
/**
|
|
91
131
|
* Create a StatePersistence instance backed by libSQL / SQLite.
|
|
@@ -128,8 +168,13 @@ interface LibSqlPersistenceOptions extends LibSqlKeyValueStoreOptions {
|
|
|
128
168
|
* });
|
|
129
169
|
* ```
|
|
130
170
|
*/
|
|
131
|
-
|
|
132
|
-
loadRaw(runId: string): Promise<
|
|
133
|
-
|
|
171
|
+
type LibSqlStatePersistence = StatePersistence & {
|
|
172
|
+
loadRaw(runId: string): Promise<SerializedState | undefined>;
|
|
173
|
+
listPage(options?: ListPageOptions): Promise<ListPageResult>;
|
|
174
|
+
deleteMany(ids: string[]): Promise<number>;
|
|
175
|
+
clear(): Promise<void>;
|
|
176
|
+
};
|
|
177
|
+
type LibSqlStatePersistenceWithLock = LibSqlStatePersistence & WorkflowLock;
|
|
178
|
+
declare function createLibSqlPersistence(options?: LibSqlPersistenceOptions): Promise<LibSqlStatePersistence | LibSqlStatePersistenceWithLock>;
|
|
134
179
|
|
|
135
|
-
export { LibSqlKeyValueStore, type LibSqlKeyValueStoreOptions, type LibSqlPersistenceOptions, createLibSqlPersistence };
|
|
180
|
+
export { LibSqlKeyValueStore, type LibSqlKeyValueStoreOptions, type LibSqlLockOptions, type LibSqlPersistenceOptions, type LibSqlStatePersistence, type LibSqlStatePersistenceWithLock, createLibSqlPersistence };
|
package/dist/index.js
CHANGED
|
@@ -1,32 +1,60 @@
|
|
|
1
|
-
import{createClient as
|
|
1
|
+
import{createClient as P}from"@libsql/client";import{createClient as k}from"@libsql/client";var g=class{client;tableName;initialized=!1;initPromise=null;constructor(t={}){if(t.client)this.client=t.client;else{let i=t.url??"file:./awaitly.db";this.client=k({url:i,authToken:t.authToken})}let e=t.tableName??"awaitly_workflow_state";if(!/^[A-Za-z0-9_]+$/.test(e))throw new Error(`Invalid table name '${e}'. Only alphanumeric and underscore characters are allowed.`);this.tableName=e}async ensureInitialized(){if(!this.initialized)return this.initPromise?this.initPromise:(this.initPromise=(async()=>{try{await this.createTable(),this.initialized=!0}finally{this.initialized||(this.initPromise=null)}})(),this.initPromise)}async createTable(){await this.client.execute({sql:`
|
|
2
2
|
CREATE TABLE IF NOT EXISTS ${this.tableName} (
|
|
3
3
|
key TEXT PRIMARY KEY,
|
|
4
4
|
value TEXT NOT NULL,
|
|
5
|
-
expires_at TEXT
|
|
5
|
+
expires_at TEXT,
|
|
6
|
+
updated_at TEXT
|
|
6
7
|
);
|
|
7
8
|
`,args:[]}),await this.client.execute({sql:`
|
|
8
9
|
CREATE INDEX IF NOT EXISTS idx_${this.tableName}_expires_at
|
|
9
10
|
ON ${this.tableName}(expires_at);
|
|
10
|
-
`,args:[]})
|
|
11
|
+
`,args:[]}),await this.client.execute({sql:`
|
|
12
|
+
CREATE INDEX IF NOT EXISTS idx_${this.tableName}_updated_at
|
|
13
|
+
ON ${this.tableName}(updated_at);
|
|
14
|
+
`,args:[]});try{await this.client.execute({sql:`ALTER TABLE ${this.tableName} ADD COLUMN updated_at TEXT`,args:[]})}catch{}}patternToLike(t){return t.replace(/%/g,"\\%").replace(/_/g,"\\_").replace(/\*/g,"%")}async get(t){await this.ensureInitialized();let e=new Date().toISOString(),i=await this.client.execute({sql:`
|
|
11
15
|
SELECT value, expires_at
|
|
12
16
|
FROM ${this.tableName}
|
|
13
17
|
WHERE key = ?
|
|
14
|
-
`,args:[
|
|
15
|
-
INSERT INTO ${this.tableName} (key, value, expires_at)
|
|
16
|
-
VALUES (?, ?, ?)
|
|
18
|
+
`,args:[t]});if(i.rows.length===0)return null;let r=i.rows[0],a=r.expires_at;return a&&a<=e?null:r.value??null}async set(t,e,i){await this.ensureInitialized();let r=i?.ttl&&i.ttl>0?new Date(Date.now()+i.ttl*1e3).toISOString():null,a=new Date().toISOString();await this.client.execute({sql:`
|
|
19
|
+
INSERT INTO ${this.tableName} (key, value, expires_at, updated_at)
|
|
20
|
+
VALUES (?, ?, ?, ?)
|
|
17
21
|
ON CONFLICT(key) DO UPDATE SET
|
|
18
22
|
value = excluded.value,
|
|
19
|
-
expires_at = excluded.expires_at
|
|
20
|
-
|
|
23
|
+
expires_at = excluded.expires_at,
|
|
24
|
+
updated_at = excluded.updated_at
|
|
25
|
+
`,args:[t,e,r,a]})}async delete(t){await this.ensureInitialized();let i=(await this.client.execute({sql:`DELETE FROM ${this.tableName} WHERE key = ?`,args:[t]})).rowsAffected;return typeof i=="number"?i>0:await this.get(t)===null}async exists(t){await this.ensureInitialized();let e=new Date().toISOString();return(await this.client.execute({sql:`
|
|
21
26
|
SELECT 1
|
|
22
27
|
FROM ${this.tableName}
|
|
23
28
|
WHERE key = ?
|
|
24
29
|
AND (expires_at IS NULL OR expires_at > ?)
|
|
25
30
|
LIMIT 1
|
|
26
|
-
`,args:[e
|
|
31
|
+
`,args:[t,e]})).rows.length>0}async keys(t){await this.ensureInitialized();let e=this.patternToLike(t),i=new Date().toISOString();return(await this.client.execute({sql:`
|
|
27
32
|
SELECT key
|
|
28
33
|
FROM ${this.tableName}
|
|
29
34
|
WHERE key LIKE ? ESCAPE '\\'
|
|
30
35
|
AND (expires_at IS NULL OR expires_at > ?)
|
|
31
|
-
`,args:[i
|
|
36
|
+
`,args:[e,i]})).rows.map(a=>a.key)}async listKeys(t,e={}){await this.ensureInitialized();let i=Math.min(Math.max(0,e.limit??100),1e4),r=Math.max(0,e.offset??0),a=e.orderBy==="key"?"key":"updated_at",S=e.orderDir==="asc"?"ASC":"DESC",c=this.patternToLike(t),u=new Date().toISOString(),m=["key LIKE ? ESCAPE '\\'","(expires_at IS NULL OR expires_at > ?)"],s=[c,u],o=3;e.updatedBefore!=null&&(m.push("updated_at < ?"),s.push(e.updatedBefore.toISOString()),o++),e.updatedAfter!=null&&(m.push("updated_at > ?"),s.push(e.updatedAfter.toISOString()),o++);let n=m.join(" AND "),l=a==="updated_at"?" NULLS LAST":"",d=[...s,i,r],w=`
|
|
37
|
+
SELECT key
|
|
38
|
+
FROM ${this.tableName}
|
|
39
|
+
WHERE ${n}
|
|
40
|
+
ORDER BY ${a} ${S}${l}
|
|
41
|
+
LIMIT ? OFFSET ?
|
|
42
|
+
`,E=(await this.client.execute({sql:w,args:d})).rows.map(T=>T.key),L;if(e.includeTotal===!0||r>0){let f=(await this.client.execute({sql:`SELECT COUNT(*) AS count FROM ${this.tableName} WHERE ${n}`,args:s})).rows[0];L=typeof f?.count=="number"?f.count:parseInt(String(f?.count??"0"),10)}return{keys:E,total:L}}async deleteMany(t){if(t.length===0)return 0;await this.ensureInitialized();let e=t.map(()=>"?").join(", ");return(await this.client.execute({sql:`DELETE FROM ${this.tableName} WHERE key IN (${e})`,args:t})).rowsAffected??0}async clear(){await this.ensureInitialized(),await this.client.execute({sql:`DELETE FROM ${this.tableName}`,args:[]})}async close(){}};import{randomUUID as x}from"crypto";function h(p,t={}){let e=t.lockTableName??"awaitly_workflow_lock",i=e.replace(/[^a-zA-Z0-9_]/g,"_");async function r(){await p.execute({sql:`
|
|
43
|
+
CREATE TABLE IF NOT EXISTS ${e} (
|
|
44
|
+
workflow_id TEXT PRIMARY KEY,
|
|
45
|
+
owner_token TEXT NOT NULL,
|
|
46
|
+
expires_at TEXT NOT NULL
|
|
47
|
+
)
|
|
48
|
+
`,args:[]}),await p.execute({sql:`
|
|
49
|
+
CREATE INDEX IF NOT EXISTS idx_${i}_expires_at
|
|
50
|
+
ON ${e}(expires_at)
|
|
51
|
+
`,args:[]})}async function a(c,u){let m=u?.ttlMs??6e4,s=x(),o=new Date(Date.now()+m).toISOString();await r();let n=await p.execute({sql:`
|
|
52
|
+
INSERT INTO ${e} (workflow_id, owner_token, expires_at)
|
|
53
|
+
VALUES (?, ?, ?)
|
|
54
|
+
ON CONFLICT(workflow_id) DO UPDATE SET
|
|
55
|
+
owner_token = excluded.owner_token,
|
|
56
|
+
expires_at = excluded.expires_at
|
|
57
|
+
WHERE ${e}.expires_at < datetime('now')
|
|
58
|
+
RETURNING owner_token
|
|
59
|
+
`,args:[c,s,o]}),l=n.rows[0];return n.rows.length===1&&l?.owner_token===s?{ownerToken:s}:null}async function S(c,u){await p.execute({sql:`DELETE FROM ${e} WHERE workflow_id = ? AND owner_token = ?`,args:[c,u]})}return{tryAcquire:a,release:S,ensureLockTable:r}}import{createStatePersistence as b}from"awaitly/persistence";async function D(p={}){let{prefix:t,lock:e,...i}=p,r=t??"workflow:state:",a=s=>s.slice(r.length),S=s=>`${r}${s}`,c=(s,o)=>Object.assign(s,{async listPage(n={}){let{keys:l,total:d}=await o.listKeys(`${r}*`,n),w=l.map(a),y=Math.min(Math.max(0,n.limit??100),1e4),E=w.length===y?(n.offset??0)+w.length:void 0;return{ids:w,total:d,nextOffset:E}},async deleteMany(n){if(n.length===0)return 0;let l=n.map(S);return o.deleteMany(l)},async clear(){return o.clear()}});if(e!==void 0){let s=i.client??P({url:i.url??"file:./awaitly.db",authToken:i.authToken}),o=new g({...i,client:s}),n=b(o,t),l=c(n,o),d=h(s,e);return Object.assign(l,{tryAcquire:d.tryAcquire.bind(d),release:d.release.bind(d)})}let u=new g(i),m=b(u,t);return c(m,u)}export{g as LibSqlKeyValueStore,D as createLibSqlPersistence};
|
|
32
60
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/libsql-store.ts","../src/index.ts"],"sourcesContent":["/**\n * awaitly-libsql\n *\n * libSQL / SQLite KeyValueStore implementation for awaitly persistence.\n */\n\nimport { createClient, type Client } from \"@libsql/client\";\nimport type { KeyValueStore } from \"awaitly/persistence\";\n\n/**\n * Options for libSQL / SQLite KeyValueStore.\n */\nexport interface LibSqlKeyValueStoreOptions {\n /**\n * libSQL database URL.\n *\n * Examples:\n * - \"file:./awaitly.db\" (local file)\n * - \":memory:\" (in-memory, for tests)\n * - \"libsql://your-db.turso.io\" (remote)\n *\n * @default \"file:./awaitly.db\"\n */\n url?: string;\n\n /**\n * Authentication token for remote libSQL databases (e.g. Turso).\n */\n authToken?: string;\n\n /**\n * Table name for storing key-value pairs.\n * @default \"awaitly_workflow_state\"\n */\n tableName?: string;\n\n /**\n * Existing libSQL client to use.\n * If provided, url/authToken options are ignored.\n */\n client?: Client;\n}\n\n/**\n * libSQL / SQLite implementation of KeyValueStore.\n *\n * Automatically creates the required table on first use.\n * Supports TTL via ISO 8601 `expires_at` column.\n */\nexport class LibSqlKeyValueStore implements KeyValueStore {\n private client: Client;\n private tableName: string;\n private initialized = false;\n private initPromise: Promise<void> | null = null;\n\n constructor(options: LibSqlKeyValueStoreOptions = {}) {\n if (options.client) {\n this.client = options.client;\n } else {\n const url = options.url ?? \"file:./awaitly.db\";\n this.client = createClient({\n url,\n authToken: options.authToken,\n });\n }\n\n const tableName = options.tableName ?? \"awaitly_workflow_state\";\n if (!/^[A-Za-z0-9_]+$/.test(tableName)) {\n throw new Error(\n `Invalid table name '${tableName}'. Only alphanumeric and underscore characters are allowed.`\n );\n }\n this.tableName = tableName;\n }\n\n /**\n * Initialize the store by creating the table and index if they don't exist.\n * This is called automatically on first use.\n */\n private async ensureInitialized(): Promise<void> {\n if (this.initialized) {\n return;\n }\n\n if (this.initPromise) {\n return this.initPromise;\n }\n\n this.initPromise = (async () => {\n try {\n await this.createTable();\n this.initialized = true;\n } finally {\n if (!this.initialized) {\n this.initPromise = null;\n }\n }\n })();\n\n return this.initPromise;\n }\n\n /**\n * Create the table and index if they don't exist.\n */\n private async createTable(): Promise<void> {\n // SQLite / libSQL: execute schema changes as separate statements\n await this.client.execute({\n sql: `\n CREATE TABLE IF NOT EXISTS ${this.tableName} (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL,\n expires_at TEXT\n );\n `,\n args: [],\n });\n\n await this.client.execute({\n sql: `\n CREATE INDEX IF NOT EXISTS idx_${this.tableName}_expires_at\n ON ${this.tableName}(expires_at);\n `,\n args: [],\n });\n }\n\n /**\n * Convert glob pattern to SQL LIKE pattern.\n * Supports * wildcard (matches any characters).\n */\n private patternToLike(pattern: string): string {\n // Escape LIKE special characters and convert * to %\n return pattern.replace(/%/g, \"\\\\%\").replace(/_/g, \"\\\\_\").replace(/\\*/g, \"%\");\n }\n\n async get(key: string): Promise<string | null> {\n await this.ensureInitialized();\n\n const nowIso = new Date().toISOString();\n const result = await this.client.execute({\n sql: `\n SELECT value, expires_at\n FROM ${this.tableName}\n WHERE key = ?\n `,\n args: [key],\n });\n\n if (result.rows.length === 0) {\n return null;\n }\n\n const row = result.rows[0] as Record<string, unknown>;\n const expiresAt = row[\"expires_at\"] as string | null | undefined;\n\n if (expiresAt && expiresAt <= nowIso) {\n // Expired - behave as if key doesn't exist\n return null;\n }\n\n return (row[\"value\"] as string) ?? null;\n }\n\n async set(key: string, value: string, options?: { ttl?: number }): Promise<void> {\n await this.ensureInitialized();\n\n const expiresAt =\n options?.ttl && options.ttl > 0\n ? new Date(Date.now() + options.ttl * 1000).toISOString()\n : null;\n\n await this.client.execute({\n sql: `\n INSERT INTO ${this.tableName} (key, value, expires_at)\n VALUES (?, ?, ?)\n ON CONFLICT(key) DO UPDATE SET\n value = excluded.value,\n expires_at = excluded.expires_at\n `,\n args: [key, value, expiresAt],\n });\n }\n\n async delete(key: string): Promise<boolean> {\n await this.ensureInitialized();\n\n const result = await this.client.execute({\n sql: `DELETE FROM ${this.tableName} WHERE key = ?`,\n args: [key],\n });\n\n // libSQL .rowsAffected is available on hrana responses; fall back to > 0 check\n const affected: number | undefined = result.rowsAffected;\n if (typeof affected === \"number\") {\n return affected > 0;\n }\n\n // If rowsAffected is not available, perform an existence check as a fallback\n const after = await this.get(key);\n return after === null;\n }\n\n async exists(key: string): Promise<boolean> {\n await this.ensureInitialized();\n\n const nowIso = new Date().toISOString();\n const result = await this.client.execute({\n sql: `\n SELECT 1\n FROM ${this.tableName}\n WHERE key = ?\n AND (expires_at IS NULL OR expires_at > ?)\n LIMIT 1\n `,\n args: [key, nowIso],\n });\n\n return result.rows.length > 0;\n }\n\n async keys(pattern: string): Promise<string[]> {\n await this.ensureInitialized();\n\n const likePattern = this.patternToLike(pattern);\n const nowIso = new Date().toISOString();\n\n const result = await this.client.execute({\n sql: `\n SELECT key\n FROM ${this.tableName}\n WHERE key LIKE ? ESCAPE '\\\\'\n AND (expires_at IS NULL OR expires_at > ?)\n `,\n args: [likePattern, nowIso],\n });\n\n return result.rows.map((row) => (row as Record<string, unknown>)[\"key\"] as string);\n }\n\n /**\n * Close the underlying client if it supports close().\n */\n async close(): Promise<void> {\n // libSQL client doesn't expose a close API in all runtimes; no-op for now.\n // If a future version adds client.close(), it can be wired here.\n }\n}\n\n","/**\n * awaitly-libsql\n *\n * libSQL / SQLite persistence adapter for awaitly workflows.\n * Provides ready-to-use StatePersistence backed by libSQL.\n */\n\nimport { LibSqlKeyValueStore, type LibSqlKeyValueStoreOptions } from \"./libsql-store\";\nimport { createStatePersistence, type StatePersistence } from \"awaitly/persistence\";\n\n/**\n * Options for creating libSQL persistence.\n */\nexport interface LibSqlPersistenceOptions extends LibSqlKeyValueStoreOptions {\n /**\n * Key prefix for state entries.\n * @default \"workflow:state:\"\n */\n prefix?: string;\n}\n\n/**\n * Create a StatePersistence instance backed by libSQL / SQLite.\n *\n * The table is automatically created on first use.\n *\n * @param options - libSQL connection and configuration options\n * @returns StatePersistence instance ready to use with durable.run()\n *\n * @example\n * ```typescript\n * import { createLibSqlPersistence } from \"awaitly-libsql\";\n * import { durable } from \"awaitly/durable\";\n *\n * const store = await createLibSqlPersistence({\n * url: \"file:./awaitly.db\",\n * });\n *\n * const result = await durable.run(\n * { fetchUser, createOrder },\n * async (step, { fetchUser, createOrder }) => {\n * const user = await step(() => fetchUser(\"123\"), { key: \"fetch-user\" });\n * const order = await step(() => createOrder(user), { key: \"create-order\" });\n * return order;\n * },\n * {\n * id: \"checkout-123\",\n * store,\n * }\n * );\n * ```\n *\n * @example\n * ```typescript\n * // Using remote Turso (libSQL) instance\n * const store = await createLibSqlPersistence({\n * url: process.env.LIBSQL_URL!,\n * authToken: process.env.LIBSQL_AUTH_TOKEN,\n * tableName: \"awaitly_workflow_state\",\n * });\n * ```\n */\nexport async function createLibSqlPersistence(\n options: LibSqlPersistenceOptions = {}\n): Promise<\n StatePersistence & {\n loadRaw(runId: string): Promise<import(\"awaitly/persistence\").SerializedState | undefined>;\n }\n> {\n const { prefix, ...storeOptions } = options;\n\n const store = new LibSqlKeyValueStore(storeOptions);\n return createStatePersistence(store, prefix);\n}\n\n/**\n * libSQL KeyValueStore implementation.\n * Use this directly if you need more control over the store.\n *\n * @example\n * ```typescript\n * import { LibSqlKeyValueStore } from \"awaitly-libsql\";\n * import { createStatePersistence } from \"awaitly/persistence\";\n *\n * const store = new LibSqlKeyValueStore({\n * url: \"file:./awaitly.db\",\n * });\n *\n * const persistence = createStatePersistence(store, \"custom:prefix:\");\n * ```\n */\nexport { LibSqlKeyValueStore, type LibSqlKeyValueStoreOptions };\n\n"],"mappings":"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","names":["createClient","LibSqlKeyValueStore","options","url","tableName","pattern","key","nowIso","result","row","expiresAt","value","affected","likePattern","createStatePersistence","createLibSqlPersistence","options","prefix","storeOptions","store","LibSqlKeyValueStore"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/libsql-store.ts","../src/libsql-lock.ts"],"sourcesContent":["/**\n * awaitly-libsql\n *\n * libSQL / SQLite persistence adapter for awaitly workflows.\n * Provides ready-to-use StatePersistence backed by libSQL.\n */\n\nimport { createClient } from \"@libsql/client\";\nimport { LibSqlKeyValueStore, type LibSqlKeyValueStoreOptions } from \"./libsql-store\";\nimport { createLibSqlLock, type LibSqlLockOptions } from \"./libsql-lock\";\nimport {\n createStatePersistence,\n type StatePersistence,\n type SerializedState,\n type ListPageOptions,\n type ListPageResult,\n} from \"awaitly/persistence\";\nimport type { WorkflowLock } from \"awaitly/durable\";\n\n/**\n * Options for cross-process locking (lease + owner token).\n * When set, the returned store implements WorkflowLock so only one process\n * runs a given workflow ID at a time (when durable.run allowConcurrent is false).\n */\nexport type { LibSqlLockOptions } from \"./libsql-lock\";\n\n/**\n * Options for creating libSQL persistence.\n */\nexport interface LibSqlPersistenceOptions extends LibSqlKeyValueStoreOptions {\n /**\n * Key prefix for state entries.\n * @default \"workflow:state:\"\n */\n prefix?: string;\n\n /**\n * When set, the store implements WorkflowLock for cross-process concurrency control.\n * Uses a lease (TTL) + owner token; release verifies the token.\n */\n lock?: LibSqlLockOptions;\n}\n\n/**\n * Create a StatePersistence instance backed by libSQL / SQLite.\n *\n * The table is automatically created on first use.\n *\n * @param options - libSQL connection and configuration options\n * @returns StatePersistence instance ready to use with durable.run()\n *\n * @example\n * ```typescript\n * import { createLibSqlPersistence } from \"awaitly-libsql\";\n * import { durable } from \"awaitly/durable\";\n *\n * const store = await createLibSqlPersistence({\n * url: \"file:./awaitly.db\",\n * });\n *\n * const result = await durable.run(\n * { fetchUser, createOrder },\n * async (step, { fetchUser, createOrder }) => {\n * const user = await step(() => fetchUser(\"123\"), { key: \"fetch-user\" });\n * const order = await step(() => createOrder(user), { key: \"create-order\" });\n * return order;\n * },\n * {\n * id: \"checkout-123\",\n * store,\n * }\n * );\n * ```\n *\n * @example\n * ```typescript\n * // Using remote Turso (libSQL) instance\n * const store = await createLibSqlPersistence({\n * url: process.env.LIBSQL_URL!,\n * authToken: process.env.LIBSQL_AUTH_TOKEN,\n * tableName: \"awaitly_workflow_state\",\n * });\n * ```\n */\nexport type LibSqlStatePersistence = StatePersistence & {\n loadRaw(runId: string): Promise<SerializedState | undefined>;\n listPage(options?: ListPageOptions): Promise<ListPageResult>;\n deleteMany(ids: string[]): Promise<number>;\n clear(): Promise<void>;\n};\n\nexport type LibSqlStatePersistenceWithLock = LibSqlStatePersistence & WorkflowLock;\n\nexport async function createLibSqlPersistence(\n options: LibSqlPersistenceOptions = {}\n): Promise<LibSqlStatePersistence | LibSqlStatePersistenceWithLock> {\n const { prefix, lock: lockOptions, ...storeOptions } = options;\n\n const effectivePrefix = prefix ?? \"workflow:state:\";\n const stripPrefix = (key: string): string => key.slice(effectivePrefix.length);\n const prefixKey = (runId: string): string => `${effectivePrefix}${runId}`;\n\n const addExtensions = (\n base: StatePersistence & { loadRaw(runId: string): Promise<SerializedState | undefined> },\n store: LibSqlKeyValueStore\n ): LibSqlStatePersistence =>\n Object.assign(base, {\n async listPage(options: ListPageOptions = {}): Promise<ListPageResult> {\n const { keys, total } = await store.listKeys(`${effectivePrefix}*`, options);\n const ids = keys.map(stripPrefix);\n const limit = Math.min(Math.max(0, options.limit ?? 100), 10_000);\n const nextOffset =\n ids.length === limit ? (options.offset ?? 0) + ids.length : undefined;\n return { ids, total, nextOffset };\n },\n async deleteMany(ids: string[]): Promise<number> {\n if (ids.length === 0) return 0;\n const keys = ids.map(prefixKey);\n return store.deleteMany(keys);\n },\n async clear(): Promise<void> {\n return store.clear();\n },\n });\n\n if (lockOptions !== undefined) {\n const client =\n storeOptions.client ??\n createClient({\n url: storeOptions.url ?? \"file:./awaitly.db\",\n authToken: storeOptions.authToken,\n });\n const store = new LibSqlKeyValueStore({ ...storeOptions, client });\n const base = createStatePersistence(store, prefix) as LibSqlStatePersistence;\n const persistence = addExtensions(\n base as StatePersistence & { loadRaw(runId: string): Promise<SerializedState | undefined> },\n store\n );\n const lock = createLibSqlLock(client, lockOptions);\n return Object.assign(persistence, {\n tryAcquire: lock.tryAcquire.bind(lock),\n release: lock.release.bind(lock),\n });\n }\n\n const store = new LibSqlKeyValueStore(storeOptions);\n const base = createStatePersistence(store, prefix);\n return addExtensions(\n base as StatePersistence & { loadRaw(runId: string): Promise<SerializedState | undefined> },\n store\n );\n}\n\n/**\n * libSQL KeyValueStore implementation.\n * Use this directly if you need more control over the store.\n *\n * @example\n * ```typescript\n * import { LibSqlKeyValueStore } from \"awaitly-libsql\";\n * import { createStatePersistence } from \"awaitly/persistence\";\n *\n * const store = new LibSqlKeyValueStore({\n * url: \"file:./awaitly.db\",\n * });\n *\n * const persistence = createStatePersistence(store, \"custom:prefix:\");\n * ```\n */\nexport { LibSqlKeyValueStore, type LibSqlKeyValueStoreOptions };\n\n","/**\n * awaitly-libsql\n *\n * libSQL / SQLite KeyValueStore implementation for awaitly persistence.\n */\n\nimport { createClient, type Client } from \"@libsql/client\";\nimport type { KeyValueStore, ListPageOptions } from \"awaitly/persistence\";\n\n/**\n * Options for libSQL / SQLite KeyValueStore.\n */\nexport interface LibSqlKeyValueStoreOptions {\n /**\n * libSQL database URL.\n *\n * Examples:\n * - \"file:./awaitly.db\" (local file)\n * - \":memory:\" (in-memory, for tests)\n * - \"libsql://your-db.turso.io\" (remote)\n *\n * @default \"file:./awaitly.db\"\n */\n url?: string;\n\n /**\n * Authentication token for remote libSQL databases (e.g. Turso).\n */\n authToken?: string;\n\n /**\n * Table name for storing key-value pairs.\n * @default \"awaitly_workflow_state\"\n */\n tableName?: string;\n\n /**\n * Existing libSQL client to use.\n * If provided, url/authToken options are ignored.\n */\n client?: Client;\n}\n\n/**\n * libSQL / SQLite implementation of KeyValueStore.\n *\n * Automatically creates the required table on first use.\n * Supports TTL via ISO 8601 `expires_at` column.\n */\nexport class LibSqlKeyValueStore implements KeyValueStore {\n private client: Client;\n private tableName: string;\n private initialized = false;\n private initPromise: Promise<void> | null = null;\n\n constructor(options: LibSqlKeyValueStoreOptions = {}) {\n if (options.client) {\n this.client = options.client;\n } else {\n const url = options.url ?? \"file:./awaitly.db\";\n this.client = createClient({\n url,\n authToken: options.authToken,\n });\n }\n\n const tableName = options.tableName ?? \"awaitly_workflow_state\";\n if (!/^[A-Za-z0-9_]+$/.test(tableName)) {\n throw new Error(\n `Invalid table name '${tableName}'. Only alphanumeric and underscore characters are allowed.`\n );\n }\n this.tableName = tableName;\n }\n\n /**\n * Initialize the store by creating the table and index if they don't exist.\n * This is called automatically on first use.\n */\n private async ensureInitialized(): Promise<void> {\n if (this.initialized) {\n return;\n }\n\n if (this.initPromise) {\n return this.initPromise;\n }\n\n this.initPromise = (async () => {\n try {\n await this.createTable();\n this.initialized = true;\n } finally {\n if (!this.initialized) {\n this.initPromise = null;\n }\n }\n })();\n\n return this.initPromise;\n }\n\n /**\n * Create the table and index if they don't exist.\n */\n private async createTable(): Promise<void> {\n // SQLite / libSQL: execute schema changes as separate statements\n await this.client.execute({\n sql: `\n CREATE TABLE IF NOT EXISTS ${this.tableName} (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL,\n expires_at TEXT,\n updated_at TEXT\n );\n `,\n args: [],\n });\n\n await this.client.execute({\n sql: `\n CREATE INDEX IF NOT EXISTS idx_${this.tableName}_expires_at\n ON ${this.tableName}(expires_at);\n `,\n args: [],\n });\n\n await this.client.execute({\n sql: `\n CREATE INDEX IF NOT EXISTS idx_${this.tableName}_updated_at\n ON ${this.tableName}(updated_at);\n `,\n args: [],\n });\n\n // Add updated_at to existing tables (ignore if column already exists)\n try {\n await this.client.execute({\n sql: `ALTER TABLE ${this.tableName} ADD COLUMN updated_at TEXT`,\n args: [],\n });\n } catch {\n // Column may already exist\n }\n }\n\n /**\n * Convert glob pattern to SQL LIKE pattern.\n * Supports * wildcard (matches any characters).\n */\n private patternToLike(pattern: string): string {\n // Escape LIKE special characters and convert * to %\n return pattern.replace(/%/g, \"\\\\%\").replace(/_/g, \"\\\\_\").replace(/\\*/g, \"%\");\n }\n\n async get(key: string): Promise<string | null> {\n await this.ensureInitialized();\n\n const nowIso = new Date().toISOString();\n const result = await this.client.execute({\n sql: `\n SELECT value, expires_at\n FROM ${this.tableName}\n WHERE key = ?\n `,\n args: [key],\n });\n\n if (result.rows.length === 0) {\n return null;\n }\n\n const row = result.rows[0] as Record<string, unknown>;\n const expiresAt = row[\"expires_at\"] as string | null | undefined;\n\n if (expiresAt && expiresAt <= nowIso) {\n // Expired - behave as if key doesn't exist\n return null;\n }\n\n return (row[\"value\"] as string) ?? null;\n }\n\n async set(key: string, value: string, options?: { ttl?: number }): Promise<void> {\n await this.ensureInitialized();\n\n const expiresAt =\n options?.ttl && options.ttl > 0\n ? new Date(Date.now() + options.ttl * 1000).toISOString()\n : null;\n const updatedAt = new Date().toISOString();\n\n await this.client.execute({\n sql: `\n INSERT INTO ${this.tableName} (key, value, expires_at, updated_at)\n VALUES (?, ?, ?, ?)\n ON CONFLICT(key) DO UPDATE SET\n value = excluded.value,\n expires_at = excluded.expires_at,\n updated_at = excluded.updated_at\n `,\n args: [key, value, expiresAt, updatedAt],\n });\n }\n\n async delete(key: string): Promise<boolean> {\n await this.ensureInitialized();\n\n const result = await this.client.execute({\n sql: `DELETE FROM ${this.tableName} WHERE key = ?`,\n args: [key],\n });\n\n // libSQL .rowsAffected is available on hrana responses; fall back to > 0 check\n const affected: number | undefined = result.rowsAffected;\n if (typeof affected === \"number\") {\n return affected > 0;\n }\n\n // If rowsAffected is not available, perform an existence check as a fallback\n const after = await this.get(key);\n return after === null;\n }\n\n async exists(key: string): Promise<boolean> {\n await this.ensureInitialized();\n\n const nowIso = new Date().toISOString();\n const result = await this.client.execute({\n sql: `\n SELECT 1\n FROM ${this.tableName}\n WHERE key = ?\n AND (expires_at IS NULL OR expires_at > ?)\n LIMIT 1\n `,\n args: [key, nowIso],\n });\n\n return result.rows.length > 0;\n }\n\n async keys(pattern: string): Promise<string[]> {\n await this.ensureInitialized();\n\n const likePattern = this.patternToLike(pattern);\n const nowIso = new Date().toISOString();\n\n const result = await this.client.execute({\n sql: `\n SELECT key\n FROM ${this.tableName}\n WHERE key LIKE ? ESCAPE '\\\\'\n AND (expires_at IS NULL OR expires_at > ?)\n `,\n args: [likePattern, nowIso],\n });\n\n return result.rows.map((row) => (row as Record<string, unknown>)[\"key\"] as string);\n }\n\n /**\n * List keys with pagination, filtering, and ordering.\n */\n async listKeys(\n pattern: string,\n options: ListPageOptions = {}\n ): Promise<{ keys: string[]; total?: number }> {\n await this.ensureInitialized();\n\n const limit = Math.min(Math.max(0, options.limit ?? 100), 10_000);\n const offset = Math.max(0, options.offset ?? 0);\n const orderBy = options.orderBy === \"key\" ? \"key\" : \"updated_at\";\n const orderDir = options.orderDir === \"asc\" ? \"ASC\" : \"DESC\";\n const likePattern = this.patternToLike(pattern);\n const nowIso = new Date().toISOString();\n\n const conditions: string[] = [\n \"key LIKE ? ESCAPE '\\\\'\",\n \"(expires_at IS NULL OR expires_at > ?)\",\n ];\n const args: (string | number)[] = [likePattern, nowIso];\n let paramIndex = 3;\n\n if (options.updatedBefore != null) {\n conditions.push(`updated_at < ?`);\n args.push(options.updatedBefore.toISOString());\n paramIndex++;\n }\n if (options.updatedAfter != null) {\n conditions.push(`updated_at > ?`);\n args.push(options.updatedAfter.toISOString());\n paramIndex++;\n }\n\n const whereClause = conditions.join(\" AND \");\n const orderNulls = orderBy === \"updated_at\" ? \" NULLS LAST\" : \"\";\n\n const listArgs = [...args, limit, offset];\n const listQuery = `\n SELECT key\n FROM ${this.tableName}\n WHERE ${whereClause}\n ORDER BY ${orderBy} ${orderDir}${orderNulls}\n LIMIT ? OFFSET ?\n `;\n\n const result = await this.client.execute({\n sql: listQuery,\n args: listArgs,\n });\n const keys = result.rows.map((row) => (row as Record<string, unknown>)[\"key\"] as string);\n\n let total: number | undefined;\n if (options.includeTotal === true || offset > 0) {\n const countResult = await this.client.execute({\n sql: `SELECT COUNT(*) AS count FROM ${this.tableName} WHERE ${whereClause}`,\n args,\n });\n const countRow = countResult.rows[0] as Record<string, unknown>;\n total = typeof countRow?.count === \"number\" ? countRow.count : parseInt(String(countRow?.count ?? \"0\"), 10);\n }\n\n return { keys, total };\n }\n\n /**\n * Delete multiple keys in one round-trip.\n */\n async deleteMany(keys: string[]): Promise<number> {\n if (keys.length === 0) return 0;\n await this.ensureInitialized();\n const placeholders = keys.map(() => \"?\").join(\", \");\n const result = await this.client.execute({\n sql: `DELETE FROM ${this.tableName} WHERE key IN (${placeholders})`,\n args: keys,\n });\n return result.rowsAffected ?? 0;\n }\n\n /**\n * Remove all entries from the table (clear all workflow state).\n */\n async clear(): Promise<void> {\n await this.ensureInitialized();\n await this.client.execute({\n sql: `DELETE FROM ${this.tableName}`,\n args: [],\n });\n }\n\n /**\n * Close the underlying client if it supports close().\n */\n async close(): Promise<void> {\n // libSQL client doesn't expose a close API in all runtimes; no-op for now.\n // If a future version adds client.close(), it can be wired here.\n }\n}\n\n","/**\n * libSQL workflow lock (lease) for cross-process concurrency control.\n * Uses a lease (TTL) + owner token; release verifies the token.\n */\n\nimport type { Client } from \"@libsql/client\";\nimport { randomUUID } from \"node:crypto\";\n\nexport interface LibSqlLockOptions {\n /**\n * Table name for workflow locks.\n * @default 'awaitly_workflow_lock'\n */\n lockTableName?: string;\n}\n\n/**\n * Create tryAcquire and release functions that use a libSQL lock table.\n * Caller must pass the same client used for state (so one connection).\n */\nexport function createLibSqlLock(\n client: Client,\n options: LibSqlLockOptions = {}\n): {\n tryAcquire(\n id: string,\n opts?: { ttlMs?: number }\n ): Promise<{ ownerToken: string } | null>;\n release(id: string, ownerToken: string): Promise<void>;\n ensureLockTable(): Promise<void>;\n} {\n const lockTableName = options.lockTableName ?? \"awaitly_workflow_lock\";\n\n const safeTableName = lockTableName.replace(/[^a-zA-Z0-9_]/g, \"_\");\n\n async function ensureLockTable(): Promise<void> {\n await client.execute({\n sql: `\n CREATE TABLE IF NOT EXISTS ${lockTableName} (\n workflow_id TEXT PRIMARY KEY,\n owner_token TEXT NOT NULL,\n expires_at TEXT NOT NULL\n )\n `,\n args: [],\n });\n await client.execute({\n sql: `\n CREATE INDEX IF NOT EXISTS idx_${safeTableName}_expires_at\n ON ${lockTableName}(expires_at)\n `,\n args: [],\n });\n }\n\n async function tryAcquire(\n id: string,\n opts?: { ttlMs?: number }\n ): Promise<{ ownerToken: string } | null> {\n const ttlMs = opts?.ttlMs ?? 60_000;\n const ownerToken = randomUUID();\n const expiresAt = new Date(Date.now() + ttlMs).toISOString();\n\n await ensureLockTable();\n\n // Insert new row or update only if current row is expired (or missing).\n // SQLite 3.35+ / libSQL support RETURNING.\n const result = await client.execute({\n sql: `\n INSERT INTO ${lockTableName} (workflow_id, owner_token, expires_at)\n VALUES (?, ?, ?)\n ON CONFLICT(workflow_id) DO UPDATE SET\n owner_token = excluded.owner_token,\n expires_at = excluded.expires_at\n WHERE ${lockTableName}.expires_at < datetime('now')\n RETURNING owner_token\n `,\n args: [id, ownerToken, expiresAt],\n });\n\n const row = result.rows[0] as Record<string, unknown> | undefined;\n if (result.rows.length === 1 && row?.owner_token === ownerToken) {\n return { ownerToken };\n }\n return null;\n }\n\n async function release(id: string, ownerToken: string): Promise<void> {\n await client.execute({\n sql: `DELETE FROM ${lockTableName} WHERE workflow_id = ? AND owner_token = ?`,\n args: [id, ownerToken],\n });\n }\n\n return { tryAcquire, release, ensureLockTable };\n}\n"],"mappings":"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","names":["createClient","createClient","LibSqlKeyValueStore","options","url","tableName","pattern","key","nowIso","result","row","expiresAt","value","updatedAt","affected","likePattern","limit","offset","orderBy","orderDir","conditions","args","paramIndex","whereClause","orderNulls","listArgs","listQuery","keys","total","countRow","placeholders","randomUUID","createLibSqlLock","client","options","lockTableName","safeTableName","ensureLockTable","tryAcquire","id","opts","ttlMs","ownerToken","expiresAt","result","row","release","createStatePersistence","createLibSqlPersistence","options","prefix","lockOptions","storeOptions","effectivePrefix","stripPrefix","key","prefixKey","runId","addExtensions","base","store","keys","total","ids","limit","nextOffset","client","createClient","LibSqlKeyValueStore","persistence","lock","createLibSqlLock"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "awaitly-libsql",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "libSQL / SQLite persistence adapter for awaitly workflows",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
},
|
|
40
40
|
"license": "MIT",
|
|
41
41
|
"peerDependencies": {
|
|
42
|
-
"awaitly": "^1.
|
|
42
|
+
"awaitly": "^1.13.0"
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@libsql/client": "^0.17.0"
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"tsup": "^8.5.1",
|
|
52
52
|
"typescript": "^5.9.3",
|
|
53
53
|
"vitest": "^4.0.18",
|
|
54
|
-
"awaitly": "^1.
|
|
54
|
+
"awaitly": "^1.13.0"
|
|
55
55
|
},
|
|
56
56
|
"publishConfig": {
|
|
57
57
|
"access": "public",
|