@workglow/storage 0.0.57 → 0.0.58

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/browser.js +1266 -121
  2. package/dist/browser.js.map +15 -10
  3. package/dist/bun.js +2017 -252
  4. package/dist/bun.js.map +19 -12
  5. package/dist/common-server.d.ts +4 -0
  6. package/dist/common-server.d.ts.map +1 -1
  7. package/dist/common.d.ts +2 -0
  8. package/dist/common.d.ts.map +1 -1
  9. package/dist/limiter/IRateLimiterStorage.d.ts +81 -0
  10. package/dist/limiter/IRateLimiterStorage.d.ts.map +1 -0
  11. package/dist/limiter/InMemoryRateLimiterStorage.d.ts +32 -0
  12. package/dist/limiter/InMemoryRateLimiterStorage.d.ts.map +1 -0
  13. package/dist/limiter/IndexedDbRateLimiterStorage.d.ts +52 -0
  14. package/dist/limiter/IndexedDbRateLimiterStorage.d.ts.map +1 -0
  15. package/dist/limiter/PostgresRateLimiterStorage.d.ts +54 -0
  16. package/dist/limiter/PostgresRateLimiterStorage.d.ts.map +1 -0
  17. package/dist/limiter/SqliteRateLimiterStorage.d.ts +53 -0
  18. package/dist/limiter/SqliteRateLimiterStorage.d.ts.map +1 -0
  19. package/dist/limiter/SupabaseRateLimiterStorage.d.ts +53 -0
  20. package/dist/limiter/SupabaseRateLimiterStorage.d.ts.map +1 -0
  21. package/dist/node.js +2017 -252
  22. package/dist/node.js.map +19 -12
  23. package/dist/queue/IQueueStorage.d.ts +72 -1
  24. package/dist/queue/IQueueStorage.d.ts.map +1 -1
  25. package/dist/queue/InMemoryQueueStorage.d.ts +44 -11
  26. package/dist/queue/InMemoryQueueStorage.d.ts.map +1 -1
  27. package/dist/queue/IndexedDbQueueStorage.d.ts +70 -5
  28. package/dist/queue/IndexedDbQueueStorage.d.ts.map +1 -1
  29. package/dist/queue/PostgresQueueStorage.d.ts +80 -8
  30. package/dist/queue/PostgresQueueStorage.d.ts.map +1 -1
  31. package/dist/queue/SqliteQueueStorage.d.ts +90 -34
  32. package/dist/queue/SqliteQueueStorage.d.ts.map +1 -1
  33. package/dist/queue/SupabaseQueueStorage.d.ts +98 -4
  34. package/dist/queue/SupabaseQueueStorage.d.ts.map +1 -1
  35. package/dist/tabular/ITabularRepository.d.ts +18 -0
  36. package/dist/tabular/ITabularRepository.d.ts.map +1 -1
  37. package/dist/tabular/InMemoryTabularRepository.d.ts +9 -1
  38. package/dist/tabular/InMemoryTabularRepository.d.ts.map +1 -1
  39. package/dist/tabular/SupabaseTabularRepository.d.ts +21 -1
  40. package/dist/tabular/SupabaseTabularRepository.d.ts.map +1 -1
  41. package/dist/tabular/TabularRepository.d.ts +10 -1
  42. package/dist/tabular/TabularRepository.d.ts.map +1 -1
  43. package/dist/util/PollingSubscriptionManager.d.ts +112 -0
  44. package/dist/util/PollingSubscriptionManager.d.ts.map +1 -0
  45. package/package.json +5 -5
@@ -1,27 +1,32 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/tabular/CachedTabularRepository.ts", "../src/tabular/InMemoryTabularRepository.ts", "../src/tabular/TabularRepository.ts", "../src/kv/IKvRepository.ts", "../src/kv/InMemoryKvRepository.ts", "../src/kv/KvRepository.ts", "../src/kv/KvViaTabularRepository.ts", "../src/queue/InMemoryQueueStorage.ts", "../src/queue/IQueueStorage.ts", "../src/tabular/IndexedDbTabularRepository.ts", "../src/util/IndexedDbTable.ts", "../src/tabular/SharedInMemoryTabularRepository.ts", "../src/tabular/SupabaseTabularRepository.ts", "../src/tabular/BaseSqlTabularRepository.ts", "../src/kv/IndexedDbKvRepository.ts", "../src/kv/SupabaseKvRepository.ts", "../src/queue/IndexedDbQueueStorage.ts", "../src/queue/SupabaseQueueStorage.ts"],
3
+ "sources": ["../src/tabular/CachedTabularRepository.ts", "../src/tabular/InMemoryTabularRepository.ts", "../src/tabular/TabularRepository.ts", "../src/kv/IKvRepository.ts", "../src/kv/InMemoryKvRepository.ts", "../src/kv/KvRepository.ts", "../src/kv/KvViaTabularRepository.ts", "../src/queue/InMemoryQueueStorage.ts", "../src/queue/IQueueStorage.ts", "../src/limiter/IRateLimiterStorage.ts", "../src/limiter/InMemoryRateLimiterStorage.ts", "../src/tabular/IndexedDbTabularRepository.ts", "../src/util/IndexedDbTable.ts", "../src/tabular/SharedInMemoryTabularRepository.ts", "../src/tabular/SupabaseTabularRepository.ts", "../src/tabular/BaseSqlTabularRepository.ts", "../src/kv/IndexedDbKvRepository.ts", "../src/kv/SupabaseKvRepository.ts", "../src/queue/IndexedDbQueueStorage.ts", "../src/util/PollingSubscriptionManager.ts", "../src/queue/SupabaseQueueStorage.ts", "../src/limiter/IndexedDbRateLimiterStorage.ts", "../src/limiter/SupabaseRateLimiterStorage.ts"],
4
4
  "sourcesContent": [
5
5
  "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { createServiceToken, DataPortSchemaObject, FromSchema } from \"@workglow/util\";\nimport { InMemoryTabularRepository } from \"./InMemoryTabularRepository\";\nimport { ITabularRepository } from \"./ITabularRepository\";\nimport { TabularRepository } from \"./TabularRepository\";\n\nexport const CACHED_TABULAR_REPOSITORY = createServiceToken<\n ITabularRepository<any, any, any, any, any>\n>(\"storage.tabularRepository.cached\");\n\n/**\n * A tabular repository wrapper that adds caching layer to a durable repository.\n * Uses InMemoryTabularRepository or SharedInMemoryTabularRepository as a cache\n * for faster access to frequently used data.\n *\n * @template Schema - The schema definition for the entity using JSON Schema\n * @template PrimaryKeyNames - Array of property names that form the primary key\n */\nexport class CachedTabularRepository<\n Schema extends DataPortSchemaObject,\n PrimaryKeyNames extends ReadonlyArray<keyof Schema[\"properties\"]>,\n // computed types\n Entity = FromSchema<Schema>,\n PrimaryKey = Pick<Entity, PrimaryKeyNames[number] & keyof Entity>,\n Value = Omit<Entity, PrimaryKeyNames[number] & keyof Entity>,\n> extends TabularRepository<Schema, PrimaryKeyNames, Entity, PrimaryKey, Value> {\n public readonly cache: ITabularRepository<Schema, PrimaryKeyNames, Entity, PrimaryKey, Value>;\n private durable: ITabularRepository<Schema, PrimaryKeyNames, Entity, PrimaryKey, Value>;\n private cacheInitialized = false;\n\n /**\n * Creates a new CachedTabularRepository instance\n * @param durable - The durable repository to use as the source of truth\n * @param cache - Optional cache repository (InMemoryTabularRepository or SharedInMemoryTabularRepository).\n * If not provided, a new InMemoryTabularRepository will be created.\n * @param schema - Schema defining the structure of the entity\n * @param primaryKeyNames - Array of property names that form the primary key\n * @param indexes - Array of columns or column arrays to make searchable. Each string or single column creates a single-column index,\n * while each array creates a compound index with columns in the specified order.\n */\n constructor(\n durable: ITabularRepository<Schema, PrimaryKeyNames, Entity, PrimaryKey, Value>,\n cache?: ITabularRepository<Schema, PrimaryKeyNames, Entity, PrimaryKey, Value>,\n schema?: Schema,\n primaryKeyNames?: PrimaryKeyNames,\n indexes?: Array<keyof Entity | Array<keyof Entity>>\n ) {\n // Extract schema and primaryKeyNames from durable repository if not provided\n // Note: This is a limitation - we can't always extract these from an interface\n // So we require them to be provided or assume they match\n if (!schema || !primaryKeyNames) {\n throw new Error(\n \"Schema and primaryKeyNames must be provided when creating CachedTabularRepository\"\n );\n }\n\n super(schema, primaryKeyNames, indexes || []);\n this.durable = durable;\n\n // Create cache if not provided\n if (cache) {\n this.cache = cache;\n } else {\n this.cache = new InMemoryTabularRepository<\n Schema,\n PrimaryKeyNames,\n Entity,\n PrimaryKey,\n Value\n >(schema, primaryKeyNames, indexes || []);\n }\n\n // Forward events from both cache and durable\n this.setupEventForwarding();\n }\n\n /**\n * Sets up event forwarding from cache and durable repositories\n */\n private setupEventForwarding(): void {\n // Forward cache events\n this.cache.on(\"put\", (entity) => {\n this.events.emit(\"put\", entity);\n });\n this.cache.on(\"get\", (key, entity) => {\n this.events.emit(\"get\", key, entity);\n });\n this.cache.on(\"search\", (key, entities) => {\n this.events.emit(\"search\", key, entities);\n });\n this.cache.on(\"delete\", (key) => {\n this.events.emit(\"delete\", key);\n });\n this.cache.on(\"clearall\", () => {\n this.events.emit(\"clearall\");\n });\n }\n\n /**\n * Initializes the cache by loading all data from the durable repository\n */\n private async initializeCache(): Promise<void> {\n if (this.cacheInitialized) return;\n\n try {\n const all = await this.durable.getAll();\n if (all && all.length > 0) {\n await this.cache.putBulk(all);\n }\n this.cacheInitialized = true;\n } catch (error) {\n console.warn(\"Failed to initialize cache from durable repository:\", error);\n this.cacheInitialized = true; // Mark as initialized even on error to avoid retry loops\n }\n }\n\n /**\n * Stores a key-value pair in both cache and durable repository\n * @param value - The combined object to store\n * @returns The stored entity\n * @emits 'put' event with the stored entity when successful\n */\n async put(value: Entity): Promise<Entity> {\n await this.initializeCache();\n\n // Write to durable first (source of truth)\n const result = await this.durable.put(value);\n\n // Then update cache\n await this.cache.put(result);\n\n return result;\n }\n\n /**\n * Stores multiple key-value pairs in both cache and durable repository\n * @param values - Array of combined objects to store\n * @returns Array of stored entities\n * @emits 'put' event for each value stored\n */\n async putBulk(values: Entity[]): Promise<Entity[]> {\n await this.initializeCache();\n\n // Write to durable first (source of truth)\n const results = await this.durable.putBulk(values);\n\n // Then update cache\n await this.cache.putBulk(results);\n\n return results;\n }\n\n /**\n * Retrieves a value by its key, checking cache first, then durable repository\n * @param key - The primary key object to look up\n * @returns The value object if found, undefined otherwise\n * @emits 'get' event with the fingerprint ID and value when found\n */\n async get(key: PrimaryKey): Promise<Entity | undefined> {\n await this.initializeCache();\n\n // Try cache first\n let result = await this.cache.get(key);\n\n // If not in cache, get from durable and cache it\n if (result === undefined) {\n result = await this.durable.get(key);\n if (result) {\n await this.cache.put(result);\n }\n }\n\n return result;\n }\n\n /**\n * Searches for entries matching a partial key\n * @param key - Partial key object to search for\n * @returns Array of matching combined objects\n * @throws Error if search criteria outside of searchable fields\n */\n async search(key: Partial<Entity>): Promise<Entity[] | undefined> {\n await this.initializeCache();\n\n // Try cache first\n let results = await this.cache.search(key);\n\n // If not found in cache, search durable and cache results\n if (results === undefined) {\n results = await this.durable.search(key);\n if (results && results.length > 0) {\n await this.cache.putBulk(results);\n }\n }\n\n return results;\n }\n\n /**\n * Deletes an entry from both cache and durable repository\n * @param value - The primary key object or entity of the entry to delete\n * @emits 'delete' event with the fingerprint ID when successful\n */\n async delete(value: PrimaryKey | Entity): Promise<void> {\n await this.initializeCache();\n\n // Delete from durable first (source of truth)\n await this.durable.delete(value);\n\n // Then delete from cache\n await this.cache.delete(value);\n }\n\n /**\n * Removes all entries from both cache and durable repository\n * @emits 'clearall' event when successful\n */\n async deleteAll(): Promise<void> {\n await this.initializeCache();\n\n // Delete from durable first (source of truth)\n await this.durable.deleteAll();\n\n // Then delete from cache\n await this.cache.deleteAll();\n }\n\n /**\n * Returns an array of all entries in the repository\n * @returns Array of all entries in the repository\n */\n async getAll(): Promise<Entity[] | undefined> {\n await this.initializeCache();\n\n // Try cache first\n let results = await this.cache.getAll();\n\n // If cache is empty, get from durable and populate cache\n if (!results || results.length === 0) {\n results = await this.durable.getAll();\n if (results && results.length > 0) {\n await this.cache.putBulk(results);\n }\n }\n\n return results;\n }\n\n /**\n * Returns the number of entries in the repository\n * @returns The total count of stored entries\n */\n async size(): Promise<number> {\n await this.initializeCache();\n\n // Get size from durable (source of truth)\n return await this.durable.size();\n }\n\n /**\n * Deletes all entries with a date column value matching the provided criteria\n * @param column - The name of the date column to compare against\n * @param value - The value to compare against\n * @param operator - The operator to use for comparison\n */\n async deleteSearch(\n column: keyof Entity,\n value: Entity[keyof Entity],\n operator: \"=\" | \"<\" | \"<=\" | \">\" | \">=\" = \"=\"\n ): Promise<void> {\n await this.initializeCache();\n\n // Delete from durable first (source of truth)\n await this.durable.deleteSearch(column, value, operator);\n\n // Then delete from cache\n await this.cache.deleteSearch(column, value, operator);\n }\n\n /**\n * Invalidates the cache by clearing it and resetting initialization flag\n */\n async invalidateCache(): Promise<void> {\n await this.cache.deleteAll();\n this.cacheInitialized = false;\n }\n\n /**\n * Refreshes the cache by reloading all data from the durable repository\n */\n async refreshCache(): Promise<void> {\n await this.cache.deleteAll();\n this.cacheInitialized = false;\n await this.initializeCache();\n }\n\n /**\n * Destroys the durable and cache repositories.\n */\n destroy(): void {\n this.durable.destroy();\n this.cache.destroy();\n }\n}\n",
6
- "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport {\n createServiceToken,\n DataPortSchemaObject,\n FromSchema,\n makeFingerprint,\n} from \"@workglow/util\";\nimport { ITabularRepository } from \"./ITabularRepository\";\nimport { TabularRepository } from \"./TabularRepository\";\n\nexport const MEMORY_TABULAR_REPOSITORY = createServiceToken<\n ITabularRepository<any, any, any, any, any>\n>(\"storage.tabularRepository.inMemory\");\n\n/**\n * A generic in-memory key-value repository implementation.\n * Provides a simple, non-persistent storage solution suitable for testing and caching scenarios.\n *\n * @template Schema - The schema definition for the entity using JSON Schema\n * @template PrimaryKeyNames - Array of property names that form the primary key\n */\nexport class InMemoryTabularRepository<\n Schema extends DataPortSchemaObject,\n PrimaryKeyNames extends ReadonlyArray<keyof Schema[\"properties\"]>,\n // computed types\n Entity = FromSchema<Schema>,\n PrimaryKey = Pick<Entity, PrimaryKeyNames[number] & keyof Entity>,\n Value = Omit<Entity, PrimaryKeyNames[number] & keyof Entity>,\n> extends TabularRepository<Schema, PrimaryKeyNames, Entity, PrimaryKey, Value> {\n /** Internal storage using a Map with fingerprint strings as keys */\n values = new Map<string, Entity>();\n\n /**\n * Creates a new InMemoryTabularRepository instance\n * @param schema - Schema defining the structure of the entity\n * @param primaryKeyNames - Array of property names that form the primary key\n * @param indexes - Array of columns or column arrays to make searchable. Each string or single column creates a single-column index,\n * while each array creates a compound index with columns in the specified order.\n */\n constructor(\n schema: Schema,\n primaryKeyNames: PrimaryKeyNames,\n indexes: Array<keyof Entity | Array<keyof Entity>> = []\n ) {\n super(schema, primaryKeyNames, indexes);\n }\n\n /**\n * Sets up the database for the repository (no-op for in-memory)\n */\n async setupDatabase(): Promise<void> {\n // No setup needed for in-memory storage\n }\n\n /**\n * Stores a key-value pair in the repository\n * @param value - The combined object to store\n * @returns The stored entity\n * @emits 'put' event with the stored entity when successful\n */\n async put(value: Entity): Promise<Entity> {\n const { key } = this.separateKeyValueFromCombined(value);\n const id = await makeFingerprint(key);\n this.values.set(id, value);\n this.events.emit(\"put\", value);\n return value;\n }\n\n /**\n * Stores multiple key-value pairs in the repository in a bulk operation\n * @param values - Array of combined objects to store\n * @returns Array of stored entities\n * @emits 'put' event for each value stored\n */\n async putBulk(values: Entity[]): Promise<Entity[]> {\n return await Promise.all(values.map(async (value) => this.put(value)));\n }\n\n /**\n * Retrieves a value by its key\n * @param key - The primary key object to look up\n * @returns The value object if found, undefined otherwise\n * @emits 'get' event with the fingerprint ID and value when found\n */\n async get(key: PrimaryKey): Promise<Entity | undefined> {\n const id = await makeFingerprint(key);\n const out = this.values.get(id);\n this.events.emit(\"get\", key, out);\n return out;\n }\n\n /**\n * Searches for entries matching a partial key\n * @param key - Partial key object to search for\n * @returns Array of matching combined objects\n * @throws Error if search criteria outside of searchable fields\n */\n async search(key: Partial<Entity>): Promise<Entity[] | undefined> {\n const searchKeys = Object.keys(key) as Array<keyof Entity>;\n if (searchKeys.length === 0) {\n return undefined;\n }\n\n // Find the best matching index\n const bestIndex = this.findBestMatchingIndex(searchKeys);\n if (!bestIndex) {\n throw new Error(\n `No suitable index found for the search criteria, searching for ['${searchKeys.join(\n \"', '\"\n )}'] with pk ['${this.primaryKeyNames.join(\"', '\")}'] and indexes ['${this.indexes.join(\n \"', '\"\n )}']`\n );\n }\n\n // Filter results based on the search criteria\n const results = Array.from(this.values.values()).filter((item) =>\n // @ts-ignore\n Object.entries(key).every(([k, v]) => item[k] === v)\n );\n\n if (results.length > 0) {\n this.events.emit(\"search\", key, results);\n return results;\n } else {\n this.events.emit(\"search\", key, undefined);\n return undefined;\n }\n }\n\n /**\n * Deletes an entry by its key\n * @param key - The primary key object of the entry to delete\n * @emits 'delete' event with the fingerprint ID when successful\n */\n async delete(value: PrimaryKey | Entity): Promise<void> {\n const { key } = this.separateKeyValueFromCombined(value as Entity);\n const id = await makeFingerprint(key);\n this.values.delete(id);\n this.events.emit(\"delete\", key as keyof Entity);\n }\n\n /**\n * Removes all entries from the repository\n * @emits 'clearall' event when successful\n */\n async deleteAll(): Promise<void> {\n this.values.clear();\n this.events.emit(\"clearall\");\n }\n\n /**\n * Returns an array of all entries in the repository\n * @returns Array of all entries in the repository\n */\n async getAll(): Promise<Entity[] | undefined> {\n const all = Array.from(this.values.values());\n return all.length > 0 ? all : undefined;\n }\n\n /**\n * Returns the number of entries in the repository\n * @returns The total count of stored entries\n */\n async size(): Promise<number> {\n return this.values.size;\n }\n\n /**\n * Deletes all entries with a date column value older than the provided date\n * @param column - The name of the date column to compare against\n * @param value - The value to compare against\n * @param operator - The operator to use for comparison\n */\n async deleteSearch(\n column: keyof Entity,\n value: Entity[keyof Entity],\n operator: \"=\" | \"<\" | \"<=\" | \">\" | \">=\" = \"=\"\n ): Promise<void> {\n const entries = this.values.entries();\n\n const entriesToDelete = entries.filter(([_, entity]) => {\n const columnValue = entity[column];\n switch (operator) {\n case \"=\":\n return columnValue === value;\n case \"<\":\n return columnValue !== null && columnValue !== undefined && columnValue < value;\n case \"<=\":\n return columnValue !== null && columnValue !== undefined && columnValue <= value;\n case \">\":\n return columnValue !== null && columnValue !== undefined && columnValue > value;\n case \">=\":\n return columnValue !== null && columnValue !== undefined && columnValue >= value;\n default:\n return false;\n }\n });\n // Delete the filtered entries\n for (const [id, _] of entriesToDelete) {\n this.values.delete(id);\n }\n\n if (Array.from(entriesToDelete).length > 0) {\n this.events.emit(\"delete\", column);\n }\n }\n\n /**\n * Destroys the repository and frees up resources.\n */\n destroy(): void {\n this.values.clear();\n }\n}\n",
7
- "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport {\n createServiceToken,\n DataPortSchemaObject,\n EventEmitter,\n FromSchema,\n makeFingerprint,\n} from \"@workglow/util\";\nimport {\n ITabularRepository,\n TabularEventListener,\n TabularEventListeners,\n TabularEventName,\n TabularEventParameters,\n ValueOptionType,\n} from \"./ITabularRepository\";\n\nexport const TABULAR_REPOSITORY = createServiceToken<ITabularRepository<any, any, any, any, any>>(\n \"storage.tabularRepository\"\n);\n\n/**\n * Abstract base class for tabular storage repositories.\n * Provides functionality for storing and retrieving data with typed\n * primary keys and values, and supports compound keys and partial key lookup.\n * Has a basic event emitter for listening to repository events.\n *\n * @template Schema - The schema definition for the entity using JSON Schema\n * @template PrimaryKeyNames - Array of property names that form the primary key\n */\nexport abstract class TabularRepository<\n Schema extends DataPortSchemaObject,\n PrimaryKeyNames extends ReadonlyArray<keyof Schema[\"properties\"]>,\n // computed types\n Entity = FromSchema<Schema>,\n PrimaryKey = Pick<Entity, PrimaryKeyNames[number] & keyof Entity>,\n Value = Omit<Entity, PrimaryKeyNames[number] & keyof Entity>,\n> implements ITabularRepository<Schema, PrimaryKeyNames, Entity, PrimaryKey, Value>\n{\n /** Event emitter for repository events */\n protected events = new EventEmitter<TabularEventListeners<PrimaryKey, Entity>>();\n\n protected indexes: Array<keyof Entity>[];\n protected primaryKeySchema: DataPortSchemaObject;\n protected valueSchema: DataPortSchemaObject;\n\n /**\n * Creates a new TabularRepository instance\n * @param schema - Schema defining the structure of the entity\n * @param primaryKeyNames - Array of property names that form the primary key\n * @param indexes - Array of columns or column arrays to make searchable. Each string or single column creates a single-column index,\n * while each array creates a compound index with columns in the specified order.\n */\n constructor(\n protected schema: Schema,\n protected primaryKeyNames: PrimaryKeyNames,\n indexes: Array<keyof Entity | Array<keyof Entity>> = []\n ) {\n const primaryKeyProps: Record<string, any> = {};\n const valueProps: Record<string, any> = {};\n const primaryKeySet = new Set(primaryKeyNames);\n\n // Split the schema properties into primary key and value properties\n for (const [key, typeDef] of Object.entries(schema.properties)) {\n if (primaryKeySet.has(key as keyof Schema[\"properties\"])) {\n primaryKeyProps[key] = Object.assign({}, typeDef);\n } else {\n valueProps[key] = Object.assign({}, typeDef);\n }\n }\n\n // Filter required array to only include primary key fields\n const primaryKeyRequired =\n schema.required?.filter((key) => primaryKeySet.has(key as keyof Schema[\"properties\"])) ?? [];\n // Filter required array to only include value fields\n const valueRequired =\n schema.required?.filter((key) => !primaryKeySet.has(key as keyof Schema[\"properties\"])) ??\n [];\n\n this.primaryKeySchema = {\n type: \"object\",\n properties: primaryKeyProps,\n required: primaryKeyRequired,\n additionalProperties: false,\n } as DataPortSchemaObject;\n this.valueSchema = {\n type: \"object\",\n properties: valueProps,\n required: valueRequired,\n additionalProperties: false,\n } as DataPortSchemaObject;\n\n // validate all combined columns names are \"identifier\" names\n const combinedColumns = [...this.primaryKeyColumns(), ...this.valueColumns()];\n for (const column of combinedColumns) {\n if (typeof column !== \"string\") {\n throw new Error(\"Column names must be strings\");\n }\n if (!/^[a-zA-Z][a-zA-Z0-9_]*$/.test(column)) {\n throw new Error(\n \"Column names must start with a letter and contain only letters, digits, and underscores\"\n );\n }\n }\n\n // Normalize searchable into array of arrays\n this.indexes = indexes.map((spec) => (Array.isArray(spec) ? spec : [spec])) as Array<\n Array<keyof Entity>\n >;\n\n // searchable is an array of compound keys, which are arrays of columns\n // clean up searchable array by removing any compoundkeys that are a prefix of another compoundkey\n // or a prefix of the primary key\n this.indexes = this.filterCompoundKeys(\n this.primaryKeyColumns() as unknown as Array<keyof Entity>,\n this.indexes\n );\n\n // Validate searchable columns\n for (const compoundIndex of this.indexes) {\n for (const column of compoundIndex) {\n if (\n !(column in this.primaryKeySchema.properties) &&\n !(column in this.valueSchema.properties)\n ) {\n throw new Error(\n `Searchable column ${String(column)} is not in the primary key schema or value schema`\n );\n }\n }\n }\n }\n\n protected filterCompoundKeys(\n primaryKey: Array<keyof Entity>,\n potentialKeys: Array<keyof Entity>[]\n ): Array<keyof Entity>[] {\n // Function to check if one array is a prefix of another\n const isPrefix = (prefix: Array<keyof Entity>, arr: Array<keyof Entity>): boolean => {\n if (prefix.length > arr.length) return false;\n return prefix.every((val, index) => val === arr[index]);\n };\n\n // Sort potential keys by length\n potentialKeys.sort((a, b) => a.length - b.length);\n\n let filteredKeys: Array<keyof Entity>[] = [];\n\n for (let i = 0; i < potentialKeys.length; i++) {\n let key = potentialKeys[i];\n\n if (isPrefix(key, primaryKey)) continue;\n\n // Keep single-column indexes regardless of being a prefix\n if (key.length === 1) {\n filteredKeys.push(key);\n continue;\n }\n\n // Skip if the key is a prefix of a later key in the list\n let isRedundant = potentialKeys.some((otherKey, j) => j > i && isPrefix(key, otherKey));\n\n if (!isRedundant) {\n filteredKeys.push(key);\n }\n }\n\n return filteredKeys;\n }\n\n /**\n * Adds an event listener for a specific event\n * @param name The name of the event to listen for\n * @param fn The callback function to execute when the event occurs\n */\n on<Event extends TabularEventName>(\n name: Event,\n fn: TabularEventListener<Event, PrimaryKey, Entity>\n ) {\n this.events.on(name, fn);\n }\n\n /**\n * Removes an event listener for a specific event\n * @param name The name of the event to remove the listener from\n * @param fn The callback function to remove\n */\n off<Event extends TabularEventName>(\n name: Event,\n fn: TabularEventListener<Event, PrimaryKey, Entity>\n ) {\n this.events.off(name, fn);\n }\n\n /**\n * Adds an event listener that will only be called once\n * @param name The name of the event to listen for\n * @param fn The callback function to execute when the event occurs\n */\n once<Event extends TabularEventName>(\n name: Event,\n fn: TabularEventListener<Event, PrimaryKey, Entity>\n ) {\n this.events.once(name, fn);\n }\n\n /**\n * Emits an event with the specified name and arguments\n * @param name The name of the event to emit\n * @param args The arguments to pass to the event listeners\n */\n emit<Event extends TabularEventName>(\n name: Event,\n ...args: TabularEventParameters<Event, PrimaryKey, Entity>\n ) {\n this.events.emit(name, ...args);\n }\n\n /**\n * Returns when the event was emitted (promise form of once)\n * @param name The name of the event to check\n * @returns true if the event has listeners, false otherwise\n */\n waitOn<Event extends TabularEventName>(\n name: Event\n ): Promise<TabularEventParameters<Event, PrimaryKey, Entity>> {\n return this.events.waitOn(name) as Promise<TabularEventParameters<Event, PrimaryKey, Entity>>;\n }\n\n /**\n * Core abstract methods that must be implemented by concrete repositories\n */\n abstract put(value: Entity): Promise<Entity>;\n abstract putBulk(values: Entity[]): Promise<Entity[]>;\n abstract get(key: PrimaryKey): Promise<Entity | undefined>;\n abstract delete(key: PrimaryKey | Entity): Promise<void>;\n abstract getAll(): Promise<Entity[] | undefined>;\n abstract deleteAll(): Promise<void>;\n abstract size(): Promise<number>;\n abstract deleteSearch(\n column: keyof Entity,\n value: Entity[keyof Entity],\n operator: \"=\" | \"<\" | \"<=\" | \">\" | \">=\"\n ): Promise<void>;\n\n /**\n * Abstract method to be implemented by concrete repositories to search for rows\n * based on a partial key.\n *\n * @param key - Partial key to search for\n * @returns Promise resolving to an array of combined row objects or undefined if not found\n */\n public abstract search(key: Partial<Entity>): Promise<Entity[] | undefined>;\n\n protected primaryKeyColumns(): Array<keyof PrimaryKey> {\n const columns: Array<keyof PrimaryKey> = [];\n for (const key of Object.keys(this.primaryKeySchema.properties)) {\n columns.push(key as keyof PrimaryKey);\n }\n return columns;\n }\n\n protected valueColumns(): Array<keyof Value> {\n const columns: Array<keyof Value> = [];\n for (const key of Object.keys(this.valueSchema.properties)) {\n columns.push(key as keyof Value);\n }\n return columns;\n }\n\n /**\n * Utility method to separate a combined object into its key and value components\n * for storage.\n * @param obj - Entity row object\n * @returns Separated key and value objects\n */\n protected separateKeyValueFromCombined(obj: Entity): { value: Value; key: PrimaryKey } {\n if (obj === null) {\n console.warn(\"Key is null\");\n return { value: {} as Value, key: {} as PrimaryKey };\n }\n if (typeof obj !== \"object\") {\n console.warn(\"Object is not an object\");\n return { value: {} as Value, key: {} as PrimaryKey };\n }\n const primaryKeyNames = this.primaryKeyColumns();\n const valueNames = this.valueColumns();\n const value: Partial<Value> = {};\n const key: Partial<PrimaryKey> = {};\n for (const k of primaryKeyNames) {\n key[k as keyof PrimaryKey] = obj[k as unknown as keyof Entity] as any;\n }\n for (const k of valueNames) {\n value[k as keyof Value] = obj[k as unknown as keyof Entity] as any;\n }\n\n return { value: value as Value, key: key as PrimaryKey };\n }\n\n /**\n * Generates a consistent string identifier for a given key.\n *\n * @param key - Primary key to convert\n * @returns Promise resolving to a string fingerprint of the key\n */\n protected async getKeyAsIdString(key: PrimaryKey): Promise<string> {\n return await makeFingerprint(key);\n }\n\n /**\n * Converts a primary key object into an ordered array based on the schema\n * This ensures consistent parameter ordering for storage operations\n * @param key - The primary key object to convert\n * @returns Array of key values ordered according to the schema\n */\n protected getPrimaryKeyAsOrderedArray(key: PrimaryKey): ValueOptionType[] {\n const orderedParams: ValueOptionType[] = [];\n const keyObj = key as Record<string, ValueOptionType>;\n for (const k in this.primaryKeySchema.properties) {\n if (k in keyObj) {\n orderedParams.push(keyObj[k]);\n } else {\n throw new Error(`Missing required primary key field: ${k}`);\n }\n }\n return orderedParams;\n }\n\n /**\n * Finds the best matching index for a set of search keys.\n * @param unorderedSearchKey - Unordered array of keys being searched, can be reordered\n * @returns Array of column names representing the best matching index, or undefined if no suitable index is found\n */\n public findBestMatchingIndex(\n unorderedSearchKey: Array<keyof Entity>\n ): Array<keyof Entity> | undefined {\n if (!unorderedSearchKey.length) return undefined;\n\n const allKeys: Array<keyof Entity>[] = [\n this.primaryKeyColumns() as unknown as Array<keyof Entity>,\n ...(this.indexes as Array<keyof Entity>[]),\n ];\n\n const searchKeySet = new Set(unorderedSearchKey);\n\n const hasMatchingPrefix = (index: Array<keyof Entity>): boolean => {\n // Check if the first column of the index is in our search keys\n return index.length > 0 && searchKeySet.has(index[0]);\n };\n\n let bestMatch: Array<keyof Entity> | undefined;\n let bestMatchScore = 0;\n\n for (const index of allKeys) {\n if (hasMatchingPrefix(index)) {\n // Calculate how many consecutive search keys we can use from this index\n let score = 0;\n for (const col of index) {\n if (!searchKeySet.has(col)) break;\n score++;\n }\n\n if (score > bestMatchScore) {\n bestMatch = index;\n bestMatchScore = score;\n }\n }\n }\n\n return bestMatch;\n }\n\n /**\n * Destroys the repository and frees up resources.\n */\n destroy(): void {\n // no op by default\n }\n\n async [Symbol.asyncDispose](): Promise<void> {\n this.destroy();\n }\n\n [Symbol.dispose](): void {\n this.destroy();\n }\n}\n",
6
+ "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport {\n createServiceToken,\n DataPortSchemaObject,\n FromSchema,\n makeFingerprint,\n} from \"@workglow/util\";\nimport { ITabularRepository, TabularChangePayload } from \"./ITabularRepository\";\nimport { TabularRepository } from \"./TabularRepository\";\n\nexport const MEMORY_TABULAR_REPOSITORY = createServiceToken<\n ITabularRepository<any, any, any, any, any>\n>(\"storage.tabularRepository.inMemory\");\n\n/**\n * A generic in-memory key-value repository implementation.\n * Provides a simple, non-persistent storage solution suitable for testing and caching scenarios.\n *\n * @template Schema - The schema definition for the entity using JSON Schema\n * @template PrimaryKeyNames - Array of property names that form the primary key\n */\nexport class InMemoryTabularRepository<\n Schema extends DataPortSchemaObject,\n PrimaryKeyNames extends ReadonlyArray<keyof Schema[\"properties\"]>,\n // computed types\n Entity = FromSchema<Schema>,\n PrimaryKey = Pick<Entity, PrimaryKeyNames[number] & keyof Entity>,\n Value = Omit<Entity, PrimaryKeyNames[number] & keyof Entity>,\n> extends TabularRepository<Schema, PrimaryKeyNames, Entity, PrimaryKey, Value> {\n /** Internal storage using a Map with fingerprint strings as keys */\n values = new Map<string, Entity>();\n\n /**\n * Creates a new InMemoryTabularRepository instance\n * @param schema - Schema defining the structure of the entity\n * @param primaryKeyNames - Array of property names that form the primary key\n * @param indexes - Array of columns or column arrays to make searchable. Each string or single column creates a single-column index,\n * while each array creates a compound index with columns in the specified order.\n */\n constructor(\n schema: Schema,\n primaryKeyNames: PrimaryKeyNames,\n indexes: Array<keyof Entity | Array<keyof Entity>> = []\n ) {\n super(schema, primaryKeyNames, indexes);\n }\n\n /**\n * Sets up the database for the repository (no-op for in-memory)\n */\n async setupDatabase(): Promise<void> {\n // No setup needed for in-memory storage\n }\n\n /**\n * Stores a key-value pair in the repository\n * @param value - The combined object to store\n * @returns The stored entity\n * @emits 'put' event with the stored entity when successful\n */\n async put(value: Entity): Promise<Entity> {\n const { key } = this.separateKeyValueFromCombined(value);\n const id = await makeFingerprint(key);\n this.values.set(id, value);\n this.events.emit(\"put\", value);\n return value;\n }\n\n /**\n * Stores multiple key-value pairs in the repository in a bulk operation\n * @param values - Array of combined objects to store\n * @returns Array of stored entities\n * @emits 'put' event for each value stored\n */\n async putBulk(values: Entity[]): Promise<Entity[]> {\n return await Promise.all(values.map(async (value) => this.put(value)));\n }\n\n /**\n * Retrieves a value by its key\n * @param key - The primary key object to look up\n * @returns The value object if found, undefined otherwise\n * @emits 'get' event with the fingerprint ID and value when found\n */\n async get(key: PrimaryKey): Promise<Entity | undefined> {\n const id = await makeFingerprint(key);\n const out = this.values.get(id);\n this.events.emit(\"get\", key, out);\n return out;\n }\n\n /**\n * Searches for entries matching a partial key\n * @param key - Partial key object to search for\n * @returns Array of matching combined objects\n * @throws Error if search criteria outside of searchable fields\n */\n async search(key: Partial<Entity>): Promise<Entity[] | undefined> {\n const searchKeys = Object.keys(key) as Array<keyof Entity>;\n if (searchKeys.length === 0) {\n return undefined;\n }\n\n // Find the best matching index\n const bestIndex = this.findBestMatchingIndex(searchKeys);\n if (!bestIndex) {\n throw new Error(\n `No suitable index found for the search criteria, searching for ['${searchKeys.join(\n \"', '\"\n )}'] with pk ['${this.primaryKeyNames.join(\"', '\")}'] and indexes ['${this.indexes.join(\n \"', '\"\n )}']`\n );\n }\n\n // Filter results based on the search criteria\n const results = Array.from(this.values.values()).filter((item) =>\n // @ts-ignore\n Object.entries(key).every(([k, v]) => item[k] === v)\n );\n\n if (results.length > 0) {\n this.events.emit(\"search\", key, results);\n return results;\n } else {\n this.events.emit(\"search\", key, undefined);\n return undefined;\n }\n }\n\n /**\n * Deletes an entry by its key\n * @param key - The primary key object of the entry to delete\n * @emits 'delete' event with the fingerprint ID when successful\n */\n async delete(value: PrimaryKey | Entity): Promise<void> {\n const { key } = this.separateKeyValueFromCombined(value as Entity);\n const id = await makeFingerprint(key);\n this.values.delete(id);\n this.events.emit(\"delete\", key as keyof Entity);\n }\n\n /**\n * Removes all entries from the repository\n * @emits 'clearall' event when successful\n */\n async deleteAll(): Promise<void> {\n this.values.clear();\n this.events.emit(\"clearall\");\n }\n\n /**\n * Returns an array of all entries in the repository\n * @returns Array of all entries in the repository\n */\n async getAll(): Promise<Entity[] | undefined> {\n const all = Array.from(this.values.values());\n return all.length > 0 ? all : undefined;\n }\n\n /**\n * Returns the number of entries in the repository\n * @returns The total count of stored entries\n */\n async size(): Promise<number> {\n return this.values.size;\n }\n\n /**\n * Deletes all entries with a date column value older than the provided date\n * @param column - The name of the date column to compare against\n * @param value - The value to compare against\n * @param operator - The operator to use for comparison\n */\n async deleteSearch(\n column: keyof Entity,\n value: Entity[keyof Entity],\n operator: \"=\" | \"<\" | \"<=\" | \">\" | \">=\" = \"=\"\n ): Promise<void> {\n const entries = this.values.entries();\n\n const entriesToDelete = entries.filter(([_, entity]) => {\n const columnValue = entity[column];\n switch (operator) {\n case \"=\":\n return columnValue === value;\n case \"<\":\n return columnValue !== null && columnValue !== undefined && columnValue < value;\n case \"<=\":\n return columnValue !== null && columnValue !== undefined && columnValue <= value;\n case \">\":\n return columnValue !== null && columnValue !== undefined && columnValue > value;\n case \">=\":\n return columnValue !== null && columnValue !== undefined && columnValue >= value;\n default:\n return false;\n }\n });\n // Delete the filtered entries and emit events for each\n for (const [id, entity] of entriesToDelete) {\n this.values.delete(id);\n const { key } = this.separateKeyValueFromCombined(entity);\n this.events.emit(\"delete\", key as keyof Entity);\n }\n }\n\n /**\n * Subscribes to changes in the repository.\n * Since InMemory is both client and server, changes are detected via local events.\n *\n * @param callback - Function called when a change occurs\n * @returns Unsubscribe function\n */\n subscribeToChanges(callback: (change: TabularChangePayload<Entity>) => void): () => void {\n const handlePut = (entity: Entity) => {\n // InMemory can't distinguish INSERT vs UPDATE without tracking\n callback({ type: \"UPDATE\", new: entity });\n };\n\n const handleDelete = (_key: keyof Entity) => {\n callback({ type: \"DELETE\" });\n };\n\n const handleClearAll = () => {\n callback({ type: \"DELETE\" });\n };\n\n this.events.on(\"put\", handlePut);\n this.events.on(\"delete\", handleDelete);\n this.events.on(\"clearall\", handleClearAll);\n\n return () => {\n this.events.off(\"put\", handlePut);\n this.events.off(\"delete\", handleDelete);\n this.events.off(\"clearall\", handleClearAll);\n };\n }\n\n /**\n * Destroys the repository and frees up resources.\n */\n destroy(): void {\n this.values.clear();\n }\n}\n",
7
+ "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport {\n createServiceToken,\n DataPortSchemaObject,\n EventEmitter,\n FromSchema,\n makeFingerprint,\n} from \"@workglow/util\";\nimport {\n ITabularRepository,\n TabularChangePayload,\n TabularEventListener,\n TabularEventListeners,\n TabularEventName,\n TabularEventParameters,\n ValueOptionType,\n} from \"./ITabularRepository\";\n\nexport const TABULAR_REPOSITORY = createServiceToken<ITabularRepository<any, any, any, any, any>>(\n \"storage.tabularRepository\"\n);\n\n/**\n * Abstract base class for tabular storage repositories.\n * Provides functionality for storing and retrieving data with typed\n * primary keys and values, and supports compound keys and partial key lookup.\n * Has a basic event emitter for listening to repository events.\n *\n * @template Schema - The schema definition for the entity using JSON Schema\n * @template PrimaryKeyNames - Array of property names that form the primary key\n */\nexport abstract class TabularRepository<\n Schema extends DataPortSchemaObject,\n PrimaryKeyNames extends ReadonlyArray<keyof Schema[\"properties\"]>,\n // computed types\n Entity = FromSchema<Schema>,\n PrimaryKey = Pick<Entity, PrimaryKeyNames[number] & keyof Entity>,\n Value = Omit<Entity, PrimaryKeyNames[number] & keyof Entity>,\n> implements ITabularRepository<Schema, PrimaryKeyNames, Entity, PrimaryKey, Value> {\n /** Event emitter for repository events */\n protected events = new EventEmitter<TabularEventListeners<PrimaryKey, Entity>>();\n\n protected indexes: Array<keyof Entity>[];\n protected primaryKeySchema: DataPortSchemaObject;\n protected valueSchema: DataPortSchemaObject;\n\n /**\n * Creates a new TabularRepository instance\n * @param schema - Schema defining the structure of the entity\n * @param primaryKeyNames - Array of property names that form the primary key\n * @param indexes - Array of columns or column arrays to make searchable. Each string or single column creates a single-column index,\n * while each array creates a compound index with columns in the specified order.\n */\n constructor(\n protected schema: Schema,\n protected primaryKeyNames: PrimaryKeyNames,\n indexes: Array<keyof Entity | Array<keyof Entity>> = []\n ) {\n const primaryKeyProps: Record<string, any> = {};\n const valueProps: Record<string, any> = {};\n const primaryKeySet = new Set(primaryKeyNames);\n\n // Split the schema properties into primary key and value properties\n for (const [key, typeDef] of Object.entries(schema.properties)) {\n if (primaryKeySet.has(key as keyof Schema[\"properties\"])) {\n primaryKeyProps[key] = Object.assign({}, typeDef);\n } else {\n valueProps[key] = Object.assign({}, typeDef);\n }\n }\n\n // Filter required array to only include primary key fields\n const primaryKeyRequired =\n schema.required?.filter((key) => primaryKeySet.has(key as keyof Schema[\"properties\"])) ?? [];\n // Filter required array to only include value fields\n const valueRequired =\n schema.required?.filter((key) => !primaryKeySet.has(key as keyof Schema[\"properties\"])) ?? [];\n\n this.primaryKeySchema = {\n type: \"object\",\n properties: primaryKeyProps,\n required: primaryKeyRequired,\n additionalProperties: false,\n } as DataPortSchemaObject;\n this.valueSchema = {\n type: \"object\",\n properties: valueProps,\n required: valueRequired,\n additionalProperties: false,\n } as DataPortSchemaObject;\n\n // validate all combined columns names are \"identifier\" names\n const combinedColumns = [...this.primaryKeyColumns(), ...this.valueColumns()];\n for (const column of combinedColumns) {\n if (typeof column !== \"string\") {\n throw new Error(\"Column names must be strings\");\n }\n if (!/^[a-zA-Z][a-zA-Z0-9_]*$/.test(column)) {\n throw new Error(\n \"Column names must start with a letter and contain only letters, digits, and underscores\"\n );\n }\n }\n\n // Normalize searchable into array of arrays\n this.indexes = indexes.map((spec) => (Array.isArray(spec) ? spec : [spec])) as Array<\n Array<keyof Entity>\n >;\n\n // searchable is an array of compound keys, which are arrays of columns\n // clean up searchable array by removing any compoundkeys that are a prefix of another compoundkey\n // or a prefix of the primary key\n this.indexes = this.filterCompoundKeys(\n this.primaryKeyColumns() as unknown as Array<keyof Entity>,\n this.indexes\n );\n\n // Validate searchable columns\n for (const compoundIndex of this.indexes) {\n for (const column of compoundIndex) {\n if (\n !(column in this.primaryKeySchema.properties) &&\n !(column in this.valueSchema.properties)\n ) {\n throw new Error(\n `Searchable column ${String(column)} is not in the primary key schema or value schema`\n );\n }\n }\n }\n }\n\n protected filterCompoundKeys(\n primaryKey: Array<keyof Entity>,\n potentialKeys: Array<keyof Entity>[]\n ): Array<keyof Entity>[] {\n // Function to check if one array is a prefix of another\n const isPrefix = (prefix: Array<keyof Entity>, arr: Array<keyof Entity>): boolean => {\n if (prefix.length > arr.length) return false;\n return prefix.every((val, index) => val === arr[index]);\n };\n\n // Sort potential keys by length\n potentialKeys.sort((a, b) => a.length - b.length);\n\n let filteredKeys: Array<keyof Entity>[] = [];\n\n for (let i = 0; i < potentialKeys.length; i++) {\n let key = potentialKeys[i];\n\n if (isPrefix(key, primaryKey)) continue;\n\n // Keep single-column indexes regardless of being a prefix\n if (key.length === 1) {\n filteredKeys.push(key);\n continue;\n }\n\n // Skip if the key is a prefix of a later key in the list\n let isRedundant = potentialKeys.some((otherKey, j) => j > i && isPrefix(key, otherKey));\n\n if (!isRedundant) {\n filteredKeys.push(key);\n }\n }\n\n return filteredKeys;\n }\n\n /**\n * Adds an event listener for a specific event\n * @param name The name of the event to listen for\n * @param fn The callback function to execute when the event occurs\n */\n on<Event extends TabularEventName>(\n name: Event,\n fn: TabularEventListener<Event, PrimaryKey, Entity>\n ) {\n this.events.on(name, fn);\n }\n\n /**\n * Removes an event listener for a specific event\n * @param name The name of the event to remove the listener from\n * @param fn The callback function to remove\n */\n off<Event extends TabularEventName>(\n name: Event,\n fn: TabularEventListener<Event, PrimaryKey, Entity>\n ) {\n this.events.off(name, fn);\n }\n\n /**\n * Adds an event listener that will only be called once\n * @param name The name of the event to listen for\n * @param fn The callback function to execute when the event occurs\n */\n once<Event extends TabularEventName>(\n name: Event,\n fn: TabularEventListener<Event, PrimaryKey, Entity>\n ) {\n this.events.once(name, fn);\n }\n\n /**\n * Emits an event with the specified name and arguments\n * @param name The name of the event to emit\n * @param args The arguments to pass to the event listeners\n */\n emit<Event extends TabularEventName>(\n name: Event,\n ...args: TabularEventParameters<Event, PrimaryKey, Entity>\n ) {\n this.events.emit(name, ...args);\n }\n\n /**\n * Returns when the event was emitted (promise form of once)\n * @param name The name of the event to check\n * @returns true if the event has listeners, false otherwise\n */\n waitOn<Event extends TabularEventName>(\n name: Event\n ): Promise<TabularEventParameters<Event, PrimaryKey, Entity>> {\n return this.events.waitOn(name) as Promise<TabularEventParameters<Event, PrimaryKey, Entity>>;\n }\n\n /**\n * Core abstract methods that must be implemented by concrete repositories\n */\n abstract put(value: Entity): Promise<Entity>;\n abstract putBulk(values: Entity[]): Promise<Entity[]>;\n abstract get(key: PrimaryKey): Promise<Entity | undefined>;\n abstract delete(key: PrimaryKey | Entity): Promise<void>;\n abstract getAll(): Promise<Entity[] | undefined>;\n abstract deleteAll(): Promise<void>;\n abstract size(): Promise<number>;\n abstract deleteSearch(\n column: keyof Entity,\n value: Entity[keyof Entity],\n operator: \"=\" | \"<\" | \"<=\" | \">\" | \">=\"\n ): Promise<void>;\n\n /**\n * Abstract method to be implemented by concrete repositories to search for rows\n * based on a partial key.\n *\n * @param key - Partial key to search for\n * @returns Promise resolving to an array of combined row objects or undefined if not found\n */\n public abstract search(key: Partial<Entity>): Promise<Entity[] | undefined>;\n\n /**\n * Subscribes to changes in the repository (including remote changes).\n * Default implementation throws an error - override in subclasses that support subscriptions.\n *\n * @param callback - Function called when a change occurs\n * @returns Unsubscribe function\n * @throws Error if not implemented by the concrete repository\n */\n public subscribeToChanges(_callback: (change: TabularChangePayload<Entity>) => void): () => void {\n throw new Error(\n `subscribeToChanges is not implemented for ${this.constructor.name}. ` +\n `Use InMemoryTabularRepository or SupabaseTabularRepository for subscription support.`\n );\n }\n\n protected primaryKeyColumns(): Array<keyof PrimaryKey> {\n const columns: Array<keyof PrimaryKey> = [];\n for (const key of Object.keys(this.primaryKeySchema.properties)) {\n columns.push(key as keyof PrimaryKey);\n }\n return columns;\n }\n\n protected valueColumns(): Array<keyof Value> {\n const columns: Array<keyof Value> = [];\n for (const key of Object.keys(this.valueSchema.properties)) {\n columns.push(key as keyof Value);\n }\n return columns;\n }\n\n /**\n * Utility method to separate a combined object into its key and value components\n * for storage.\n * @param obj - Entity row object\n * @returns Separated key and value objects\n */\n protected separateKeyValueFromCombined(obj: Entity): { value: Value; key: PrimaryKey } {\n if (obj === null) {\n console.warn(\"Key is null\");\n return { value: {} as Value, key: {} as PrimaryKey };\n }\n if (typeof obj !== \"object\") {\n console.warn(\"Object is not an object\");\n return { value: {} as Value, key: {} as PrimaryKey };\n }\n const primaryKeyNames = this.primaryKeyColumns();\n const valueNames = this.valueColumns();\n const value: Partial<Value> = {};\n const key: Partial<PrimaryKey> = {};\n for (const k of primaryKeyNames) {\n key[k as keyof PrimaryKey] = obj[k as unknown as keyof Entity] as any;\n }\n for (const k of valueNames) {\n value[k as keyof Value] = obj[k as unknown as keyof Entity] as any;\n }\n\n return { value: value as Value, key: key as PrimaryKey };\n }\n\n /**\n * Generates a consistent string identifier for a given key.\n *\n * @param key - Primary key to convert\n * @returns Promise resolving to a string fingerprint of the key\n */\n protected async getKeyAsIdString(key: PrimaryKey): Promise<string> {\n return await makeFingerprint(key);\n }\n\n /**\n * Converts a primary key object into an ordered array based on the schema\n * This ensures consistent parameter ordering for storage operations\n * @param key - The primary key object to convert\n * @returns Array of key values ordered according to the schema\n */\n protected getPrimaryKeyAsOrderedArray(key: PrimaryKey): ValueOptionType[] {\n const orderedParams: ValueOptionType[] = [];\n const keyObj = key as Record<string, ValueOptionType>;\n for (const k in this.primaryKeySchema.properties) {\n if (k in keyObj) {\n orderedParams.push(keyObj[k]);\n } else {\n throw new Error(`Missing required primary key field: ${k}`);\n }\n }\n return orderedParams;\n }\n\n /**\n * Finds the best matching index for a set of search keys.\n * @param unorderedSearchKey - Unordered array of keys being searched, can be reordered\n * @returns Array of column names representing the best matching index, or undefined if no suitable index is found\n */\n public findBestMatchingIndex(\n unorderedSearchKey: Array<keyof Entity>\n ): Array<keyof Entity> | undefined {\n if (!unorderedSearchKey.length) return undefined;\n\n const allKeys: Array<keyof Entity>[] = [\n this.primaryKeyColumns() as unknown as Array<keyof Entity>,\n ...(this.indexes as Array<keyof Entity>[]),\n ];\n\n const searchKeySet = new Set(unorderedSearchKey);\n\n const hasMatchingPrefix = (index: Array<keyof Entity>): boolean => {\n // Check if the first column of the index is in our search keys\n return index.length > 0 && searchKeySet.has(index[0]);\n };\n\n let bestMatch: Array<keyof Entity> | undefined;\n let bestMatchScore = 0;\n\n for (const index of allKeys) {\n if (hasMatchingPrefix(index)) {\n // Calculate how many consecutive search keys we can use from this index\n let score = 0;\n for (const col of index) {\n if (!searchKeySet.has(col)) break;\n score++;\n }\n\n if (score > bestMatchScore) {\n bestMatch = index;\n bestMatchScore = score;\n }\n }\n }\n\n return bestMatch;\n }\n\n /**\n * Destroys the repository and frees up resources.\n */\n destroy(): void {\n // no op by default\n }\n\n async [Symbol.asyncDispose](): Promise<void> {\n this.destroy();\n }\n\n [Symbol.dispose](): void {\n this.destroy();\n }\n}\n",
8
8
  "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { DataPortSchemaObject, EventParameters } from \"@workglow/util\";\nimport { JSONValue } from \"../tabular/ITabularRepository\";\n\n/**\n * Default schema types for simple string row data\n */\nexport const DefaultKeyValueSchema = {\n type: \"object\",\n properties: {\n key: { type: \"string\" },\n value: {},\n },\n additionalProperties: false,\n} as const satisfies DataPortSchemaObject;\nexport const DefaultKeyValueKey = [\"key\"] as const;\n\n/**\n * Type definitions for kv repository events\n */\nexport type KvEventListeners<Key, Value, Combined> = {\n put: (key: Key, value: Value) => void;\n get: (key: Key, value: Value | undefined) => void;\n getAll: (results: Combined[] | undefined) => void;\n delete: (key: unknown) => void;\n deleteall: () => void;\n};\n\nexport type KvEventName = keyof KvEventListeners<any, any, any>;\nexport type KvEventListener<Event extends KvEventName, Key, Value, Combined> = KvEventListeners<\n Key,\n Value,\n Combined\n>[Event];\n\nexport type KvEventParameters<Event extends KvEventName, Key, Value, Combined> = EventParameters<\n KvEventListeners<Key, Value, Combined>,\n Event\n>;\n\n/**\n * Interface defining the contract for kv storage repositories.\n * Provides a flexible interface for storing and retrieving data with typed\n * primary keys and values, and supports compound keys and partial key lookup.\n *\n * @typeParam Key - Type for the primary key\n * @typeParam Value - Type for the value struct re\n * @typeParam Combined - Combined type of Key & Value\n */\nexport interface IKvRepository<\n Key extends string | number = string,\n Value extends any = any,\n Combined = { key: Key; value: Value },\n> {\n // Core methods\n put(key: Key, value: Value): Promise<void>;\n putBulk(items: Array<{ key: Key; value: Value }>): Promise<void>;\n get(key: Key): Promise<Value | undefined>;\n delete(key: Key): Promise<void>;\n getAll(): Promise<Combined[] | undefined>;\n deleteAll(): Promise<void>;\n size(): Promise<number>;\n\n getObjectAsIdString(object: JSONValue): Promise<string>;\n\n // Event handling methods\n on<Event extends KvEventName>(\n name: Event,\n fn: KvEventListener<Event, Key, Value, Combined>\n ): void;\n off<Event extends KvEventName>(\n name: Event,\n fn: KvEventListener<Event, Key, Value, Combined>\n ): void;\n emit<Event extends KvEventName>(\n name: Event,\n ...args: KvEventParameters<Event, Key, Value, Combined>\n ): void;\n once<Event extends KvEventName>(\n name: Event,\n fn: KvEventListener<Event, Key, Value, Combined>\n ): void;\n waitOn<Event extends KvEventName>(\n name: Event\n ): Promise<KvEventParameters<Event, Key, Value, Combined>>;\n}\n",
9
9
  "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { createServiceToken, JsonSchema } from \"@workglow/util\";\nimport { InMemoryTabularRepository } from \"../tabular/InMemoryTabularRepository\";\nimport { DefaultKeyValueKey, DefaultKeyValueSchema, IKvRepository } from \"./IKvRepository\";\nimport { KvViaTabularRepository } from \"./KvViaTabularRepository\";\n\nexport const MEMORY_KV_REPOSITORY = createServiceToken<IKvRepository<string, any, any>>(\n \"storage.kvRepository.inMemory\"\n);\n\n/**\n * An in-memory key-value repository implementation for fast, ephemeral storage.\n * Uses a tabular repository abstraction for in-memory persistence.\n *\n * @template Key - The type of the primary key\n * @template Value - The type of the value being stored\n * @template Combined - Combined type of Key & Value\n */\nexport class InMemoryKvRepository extends KvViaTabularRepository {\n public tabularRepository: InMemoryTabularRepository<\n typeof DefaultKeyValueSchema,\n typeof DefaultKeyValueKey\n >;\n\n /**\n * Creates a new KvRepository instance\n */\n constructor(keySchema: JsonSchema = { type: \"string\" }, valueSchema: JsonSchema = {}) {\n super(keySchema, valueSchema);\n this.tabularRepository = new InMemoryTabularRepository(\n DefaultKeyValueSchema,\n DefaultKeyValueKey\n );\n }\n}\n",
10
10
  "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { createServiceToken, EventEmitter, JsonSchema, makeFingerprint } from \"@workglow/util\";\nimport { JSONValue } from \"../tabular/ITabularRepository\";\nimport {\n IKvRepository,\n KvEventListener,\n KvEventListeners,\n KvEventName,\n KvEventParameters,\n} from \"./IKvRepository\";\n\nexport const KV_REPOSITORY =\n createServiceToken<IKvRepository<any, any, any>>(\"storage.kvRepository\");\n\n/**\n * Abstract base class for key-value storage repositories.\n * Has a basic event emitter for listening to repository events.\n *\n * @template Key - The type of the primary key\n * @template Value - The type of the value being stored\n * @template Combined - Combined type of Key & Value\n */\nexport abstract class KvRepository<\n Key extends string = string,\n Value extends any = any,\n Combined = { key: Key; value: Value },\n> implements IKvRepository<Key, Value, Combined>\n{\n /** Event emitter for repository events */\n protected events = new EventEmitter<KvEventListeners<Key, Value, Combined>>();\n\n /**\n * Creates a new KvRepository instance\n */\n constructor(\n public keySchema: JsonSchema = { type: \"string\" },\n public valueSchema: JsonSchema = {}\n ) {}\n\n /**\n * Stores a row in the repository.\n * @param key - The primary key\n * @param value - The value to store\n */\n abstract put(key: Key, value: Value): Promise<void>;\n\n /**\n * Stores multiple rows in the repository in a bulk operation.\n * @param items - Array of key-value pairs to store\n */\n abstract putBulk(items: Array<{ key: Key; value: Value }>): Promise<void>;\n\n /**\n * Retrieves a value by its key.\n * This is a convenience method that automatically converts simple types to structured format if using default schema.\n *\n * @param key - Primary key to look up (basic key like default schema)\n * @returns The stored value or undefined if not found\n */\n abstract get(key: Key): Promise<Value | undefined>;\n\n /**\n * Deletes a row from the repository.\n * @param key - The primary key of the row to delete\n */\n abstract delete(key: Key): Promise<void>;\n\n /**\n * Retrieves all rows from the repository.\n * @returns An array of all rows in the repository or undefined if empty\n */\n abstract getAll(): Promise<Combined[] | undefined>;\n\n /**\n * Deletes all rows from the repository.\n */\n abstract deleteAll(): Promise<void>;\n\n /**\n * Retrieves the number of rows in the repository.\n * @returns The number of rows in the repository\n */\n abstract size(): Promise<number>;\n\n /**\n * Generates a consistent string identifier for a given key.\n *\n * @param object - Object to convert\n * @returns Promise resolving to a string fingerprint of the object for use as an id\n */\n public async getObjectAsIdString(object: JSONValue): Promise<string> {\n return await makeFingerprint(object);\n }\n\n /**\n * Adds an event listener for a specific event\n * @param name The name of the event to listen for\n * @param fn The callback function to execute when the event occurs\n */\n on<Event extends KvEventName>(name: Event, fn: KvEventListener<Event, Key, Value, Combined>) {\n this.events.on(name, fn);\n }\n\n /**\n * Removes an event listener for a specific event\n * @param name The name of the event to remove the listener from\n * @param fn The callback function to remove\n */\n off<Event extends KvEventName>(name: Event, fn: KvEventListener<Event, Key, Value, Combined>) {\n this.events.off(name, fn);\n }\n\n /**\n * Adds an event listener that will only be called once\n * @param name The name of the event to listen for\n * @param fn The callback function to execute when the event occurs\n */\n once<Event extends KvEventName>(name: Event, fn: KvEventListener<Event, Key, Value, Combined>) {\n this.events.once(name, fn);\n }\n\n /**\n * Emits an event with the specified name and arguments\n * @param name The name of the event to emit\n * @param args The arguments to pass to the event listeners\n */\n emit<Event extends KvEventName>(\n name: Event,\n ...args: KvEventParameters<Event, Key, Value, Combined>\n ) {\n this.events.emit(name, ...args);\n }\n\n /**\n * Returns when the event was emitted (promise form of once)\n * @param name The name of the event to check\n * @returns true if the event has listeners, false otherwise\n */\n waitOn<Event extends KvEventName>(\n name: Event\n ): Promise<KvEventParameters<Event, Key, Value, Combined>> {\n return this.events.waitOn(name) as Promise<KvEventParameters<Event, Key, Value, Combined>>;\n }\n}\n",
11
11
  "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type { TabularRepository } from \"../tabular/TabularRepository\";\nimport { DefaultKeyValueKey, DefaultKeyValueSchema } from \"./IKvRepository\";\nimport { KvRepository } from \"./KvRepository\";\n\n/**\n * Abstract base class for key-value storage repositories that uses a tabular repository for storage.\n * Has a basic event emitter for listening to repository events.\n *\n * @template Key - The type of the primary key\n * @template Value - The type of the value being stored\n * @template Combined - Combined type of Key & Value\n */\nexport abstract class KvViaTabularRepository<\n Key extends string = string,\n Value extends any = any,\n Combined = { key: Key; value: Value },\n> extends KvRepository<Key, Value, Combined> {\n public abstract tabularRepository: TabularRepository<\n typeof DefaultKeyValueSchema,\n typeof DefaultKeyValueKey\n >;\n\n /**\n * Stores a row in the repository.\n * @param key - The primary key\n * @param value - The value to store\n */\n public async put(key: Key, value: Value): Promise<void> {\n // Handle objects that need to be JSON-stringified, TODO(str): should put in the type\n const schemaType =\n typeof this.valueSchema === \"object\" &&\n this.valueSchema !== null &&\n \"type\" in this.valueSchema\n ? this.valueSchema.type\n : undefined;\n const shouldStringify = ![\"number\", \"boolean\", \"string\", \"blob\"].includes(schemaType as string);\n\n if (shouldStringify) {\n value = JSON.stringify(value) as Value;\n }\n await this.tabularRepository.put({ key, value });\n }\n\n /**\n * Stores multiple rows in the repository in a bulk operation.\n * @param items - Array of key-value pairs to store\n */\n public async putBulk(items: Array<{ key: Key; value: Value }>): Promise<void> {\n // Handle objects that need to be JSON-stringified, TODO(str): should put in the type\n const schemaType =\n typeof this.valueSchema === \"object\" &&\n this.valueSchema !== null &&\n \"type\" in this.valueSchema\n ? this.valueSchema.type\n : undefined;\n const shouldStringify = ![\"number\", \"boolean\", \"string\", \"blob\"].includes(schemaType as string);\n\n const entities = items.map(({ key, value }) => {\n if (shouldStringify) {\n value = JSON.stringify(value) as Value;\n }\n return { key, value };\n });\n\n await this.tabularRepository.putBulk(entities);\n }\n\n /**\n * Retrieves a value by its key.\n * This is a convenience method that automatically converts simple types to structured format if using default schema.\n *\n * @param key - Primary key to look up (basic key like default schema)\n * @returns The stored value or undefined if not found\n */\n public async get(key: Key): Promise<Value | undefined> {\n const result = await this.tabularRepository.get({ key });\n if (result) {\n const schemaType =\n typeof this.valueSchema === \"object\" &&\n this.valueSchema !== null &&\n \"type\" in this.valueSchema\n ? this.valueSchema.type\n : undefined;\n const shouldParse = ![\"number\", \"boolean\", \"string\", \"blob\"].includes(schemaType as string);\n\n if (shouldParse) {\n try {\n return JSON.parse(result.value as unknown as string) as Value;\n } catch (e) {\n return result.value as unknown as Value;\n }\n } else {\n return result.value as unknown as Value;\n }\n } else {\n return undefined;\n }\n }\n\n /**\n * Deletes a row from the repository.\n * @param key - The primary key of the row to delete\n */\n public async delete(key: Key): Promise<void> {\n return await this.tabularRepository.delete({ key });\n }\n\n /**\n * Retrieves all rows from the repository.\n * @returns An array of all rows in the repository or undefined if empty\n */\n public async getAll(): Promise<Combined[] | undefined> {\n const values = await this.tabularRepository.getAll();\n if (values) {\n return values.map(\n (value) =>\n ({\n key: value.key,\n value: (() => {\n const schemaType =\n typeof this.valueSchema === \"object\" &&\n this.valueSchema !== null &&\n \"type\" in this.valueSchema\n ? this.valueSchema.type\n : undefined;\n const shouldParse = ![\"number\", \"boolean\", \"string\"].includes(schemaType as string);\n\n if (shouldParse && typeof value.value === \"string\") {\n try {\n return JSON.parse(value.value);\n } catch (e) {\n return value.value;\n }\n }\n return value.value;\n })(),\n }) as Combined\n );\n }\n }\n\n /**\n * Deletes all rows from the repository.\n */\n public async deleteAll(): Promise<void> {\n return await this.tabularRepository.deleteAll();\n }\n\n /**\n * Retrieves the number of rows in the repository.\n * @returns The number of rows in the repository\n */\n public async size(): Promise<number> {\n return await this.tabularRepository.size();\n }\n}\n",
12
- "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { createServiceToken, makeFingerprint, sleep, uuid4 } from \"@workglow/util\";\nimport { IQueueStorage, JobStatus, JobStorageFormat } from \"./IQueueStorage\";\n\nexport const IN_MEMORY_QUEUE_STORAGE = createServiceToken<IQueueStorage<any, any>>(\n \"jobqueue.storage.inMemory\"\n);\n\n/**\n * In-memory implementation of a job queue that manages asynchronous tasks.\n * Supports job scheduling, status tracking, and result caching.\n */\nexport class InMemoryQueueStorage<Input, Output> implements IQueueStorage<Input, Output> {\n /**\n * Creates a new in-memory job queue\n * @param queue - Name of the queue\n */\n constructor(public readonly queueName: string) {\n this.jobQueue = [];\n }\n\n /** Internal array storing all jobs */\n public jobQueue: JobStorageFormat<Input, Output>[];\n\n /**\n * Returns a filtered and sorted list of pending jobs that are ready to run\n * Sorts by creation time to maintain FIFO order\n */\n private pendingQueue() {\n const now = new Date().toISOString();\n return this.jobQueue\n .filter((job) => job.status === JobStatus.PENDING)\n .filter((job) => !job.run_after || job.run_after <= now)\n .sort((a, b) => (a.run_after || \"\").localeCompare(b.run_after || \"\"));\n }\n\n /**\n * Adds a new job to the queue\n * Generates an ID and fingerprint if not provided\n */\n public async add(job: JobStorageFormat<Input, Output>) {\n await sleep(0);\n const now = new Date().toISOString();\n job.id = job.id ?? uuid4();\n job.job_run_id = job.job_run_id ?? uuid4();\n job.queue = this.queueName;\n job.fingerprint = await makeFingerprint(job.input);\n job.status = JobStatus.PENDING;\n job.progress = 0;\n job.progress_message = \"\";\n job.progress_details = null;\n job.created_at = now;\n job.run_after = now;\n\n this.jobQueue.push(job);\n return job.id;\n }\n\n /**\n * Retrieves a job from the queue by its id.\n * @param id - The id of the job to retrieve.\n * @returns A promise that resolves to the job or undefined if the job is not found.\n */\n public async get(id: unknown) {\n await sleep(0);\n return this.jobQueue.find((j) => j.id === id);\n }\n\n /**\n * Retrieves a slice of jobs from anywherethe queue.\n * @param status - The status of the jobs to retrieve.\n * @param num - The number of jobs to retrieve.\n * @returns A promise that resolves to an array of jobs.\n */\n public async peek(status: JobStatus = JobStatus.PENDING, num: number = 100) {\n await sleep(0);\n num = Number(num) || 100;\n return this.jobQueue\n .sort((a, b) => (a.run_after || \"\").localeCompare(b.run_after || \"\"))\n .filter((j) => j.status === status)\n .slice(0, num);\n }\n\n /**\n * Retrieves the next available job that is ready to be processed\n * Updates the job status to PROCESSING before returning\n */\n public async next() {\n await sleep(0);\n const top = this.pendingQueue();\n\n const job = top[0];\n if (job) {\n job.status = JobStatus.PROCESSING;\n job.last_ran_at = new Date().toISOString();\n return job;\n }\n }\n\n /**\n * Retrieves the size of the queue for a given status\n * @param status - The status of the jobs to retrieve.\n * @returns A promise that resolves to the number of jobs.\n */\n public async size(status = JobStatus.PENDING): Promise<number> {\n await sleep(0);\n return this.jobQueue.filter((j) => j.status === status).length;\n }\n\n /**\n * Saves the progress of a job\n * @param jobId - The id of the job to save the progress for.\n * @param progress - The progress of the job.\n * @param message - The message of the job.\n * @param details - The details of the job.\n */\n public async saveProgress(\n id: unknown,\n progress: number,\n message: string,\n details: Record<string, any> | null\n ): Promise<void> {\n await sleep(0);\n const job = this.jobQueue.find((j) => j.id === id);\n if (!job) {\n throw new Error(`Job ${id} not found`);\n }\n\n job.progress = progress;\n job.progress_message = message;\n job.progress_details = details;\n }\n\n /**\n * Marks a job as complete with its output or error\n * Handles run_attempts for failed jobs and triggers completion callbacks\n * @param id - ID of the job to complete\n * @param output - Result of the job execution\n * @param error - Optional error message if job failed\n */\n public async complete(job: JobStorageFormat<Input, Output>) {\n await sleep(0);\n const index = this.jobQueue.findIndex((j) => j.id === job.id);\n if (index !== -1) {\n const existing = this.jobQueue[index];\n const currentAttempts = existing?.run_attempts ?? 0;\n job.run_attempts = currentAttempts + 1;\n this.jobQueue[index] = job;\n }\n }\n\n /**\n * Aborts a job\n * @param id - The id of the job to abort.\n */\n public async abort(id: unknown) {\n await sleep(0);\n const job = this.jobQueue.find((j) => j.id === id);\n if (job) {\n job.status = JobStatus.ABORTING;\n }\n }\n\n /**\n * Retrieves all jobs by their job_run_id.\n * @param job_run_id - The job_run_id of the jobs to retrieve.\n * @returns A promise that resolves to an array of jobs.\n */\n public async getByRunId(runId: string): Promise<Array<JobStorageFormat<Input, Output>>> {\n await sleep(0);\n return this.jobQueue.filter((job) => job.job_run_id === runId);\n }\n\n /**\n * Deletes all jobs from the queue.\n */\n public async deleteAll() {\n await sleep(0);\n this.jobQueue = [];\n }\n\n /**\n * Looks up cached output for a given and input\n * Uses input fingerprinting for efficient matching\n * @param input - The input to look up the cached output for.\n * @returns The cached output or null if not found\n */\n public async outputForInput(input: Input) {\n await sleep(0);\n const fingerprint = await makeFingerprint(input);\n return (\n this.jobQueue.find((j) => j.fingerprint === fingerprint && j.status === JobStatus.COMPLETED)\n ?.output ?? null\n );\n }\n\n /**\n * Deletes a job by its ID\n */\n public async delete(id: unknown): Promise<void> {\n await sleep(0);\n this.jobQueue = this.jobQueue.filter((job) => job.id !== id);\n }\n\n /**\n * Delete jobs with a specific status older than a cutoff date\n * @param status - Status of jobs to delete\n * @param olderThanMs - Delete jobs completed more than this many milliseconds ago\n */\n public async deleteJobsByStatusAndAge(status: JobStatus, olderThanMs: number): Promise<void> {\n await sleep(0);\n const cutoffDate = new Date(Date.now() - olderThanMs).toISOString();\n this.jobQueue = this.jobQueue.filter(\n (job) => job.status !== status || !job.completed_at || job.completed_at > cutoffDate\n );\n }\n\n /**\n * Sets up the database schema and tables.\n * No-op for in-memory storage as it doesn't require database setup.\n */\n public async setupDatabase(): Promise<void> {\n // No-op for in-memory storage\n }\n}\n",
13
- "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { createServiceToken } from \"@workglow/util\";\n\nexport const QUEUE_STORAGE = createServiceToken<IQueueStorage<any, any>>(\"jobqueue.storage\");\n\nexport enum JobStatus {\n PENDING = \"PENDING\",\n PROCESSING = \"PROCESSING\",\n COMPLETED = \"COMPLETED\",\n ABORTING = \"ABORTING\",\n FAILED = \"FAILED\",\n DISABLED = \"DISABLED\",\n}\n\n/**\n * Details about a job that reflect the structure in the database.\n */\nexport type JobStorageFormat<Input, Output> = {\n id?: unknown;\n job_run_id?: string;\n queue?: string;\n input: Input;\n output?: Output | null;\n error?: string | null;\n error_code?: string | null;\n fingerprint?: string;\n max_retries?: number;\n status?: JobStatus;\n created_at?: string;\n deadline_at?: string | null;\n last_ran_at?: string | null;\n run_after: string | null;\n completed_at: string | null;\n run_attempts?: number;\n progress?: number;\n progress_message?: string;\n progress_details?: Record<string, any> | null;\n};\n\n/**\n * Interface defining the storage operations for a job queue\n */\nexport interface IQueueStorage<Input, Output> {\n /**\n * Adds a job to the queue storage\n * @param job - The job to add to the queue storage\n * @returns The ID of the job\n */\n add(job: JobStorageFormat<Input, Output>): Promise<unknown>;\n\n /**\n * Gets a job from the queue storage by ID\n * @param id - The ID of the job to get\n * @returns The job with the given ID\n */\n get(id: unknown): Promise<JobStorageFormat<Input, Output> | undefined>;\n\n /**\n * Gets the next job from the queue storage\n * @returns The next job from the queue storage\n */\n next(): Promise<JobStorageFormat<Input, Output> | undefined>;\n\n /**\n * Peeks at the next job(s) from the queue storage without removing them\n * @param status - The status of the jobs to peek at\n * @param num - The number of jobs to peek at\n * @returns The jobs with the given status\n */\n peek(status?: JobStatus, num?: number): Promise<Array<JobStorageFormat<Input, Output>>>;\n\n /**\n * Gets the size of the queue storage\n * @param status - The status of the jobs to get the size for\n * @returns The size of the queue storage\n */\n size(status?: JobStatus): Promise<number>;\n\n /**\n * Completes a job in the queue storage\n * @param job - The job to complete\n */\n complete(job: JobStorageFormat<Input, Output>): Promise<void>;\n\n /**\n * Deletes all jobs from the queue storage\n */\n deleteAll(): Promise<void>;\n\n /**\n * Gets the output for a given input from the queue storage\n * @param input - The input to get the output for\n * @returns The output of the job\n */\n outputForInput(input: Input): Promise<Output | null>;\n\n /**\n * Aborts a job in the queue storage\n * @param id - The ID of the job to abort\n */\n abort(id: unknown): Promise<void>;\n\n /**\n * Gets the jobs by job run ID from the queue storage\n * @param runId - The job run ID of the jobs to get\n * @returns The jobs with the given job run ID\n */\n getByRunId(runId: string): Promise<Array<JobStorageFormat<Input, Output>>>;\n\n /**\n * Saves progress updates for a job in the queue storage\n * @param id - The ID of the job to save the progress for\n * @param progress - The progress of the job\n * @param message - The message of the job\n * @param details - The details of the job\n */\n saveProgress(\n id: unknown,\n progress: number,\n message: string,\n details: Record<string, any> | null\n ): Promise<void>;\n\n /**\n * Deletes a job by its ID from the queue storage\n * @param id - The ID of the job to delete\n */\n delete(id: unknown): Promise<void>;\n\n /**\n * Deletes jobs from the queue storage that are of a specific status and older than the specified time\n * @param status - The status of the jobs to delete\n * @param olderThanMs - The time in milliseconds that the jobs must be older than to be deleted\n */\n deleteJobsByStatusAndAge(status: JobStatus, olderThanMs: number): Promise<void>;\n\n /**\n * Sets up the database schema and tables.\n * This method should be called before using the storage in tests.\n * For production use, database setup should be done via migrations.\n */\n setupDatabase(): Promise<void>;\n}\n",
12
+ "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { createServiceToken, EventEmitter, makeFingerprint, sleep, uuid4 } from \"@workglow/util\";\nimport {\n IQueueStorage,\n JobStatus,\n JobStorageFormat,\n QueueChangePayload,\n QueueStorageOptions,\n QueueSubscribeOptions,\n} from \"./IQueueStorage\";\n\n/**\n * Event listeners for queue storage events\n */\ntype QueueEventListeners<Input, Output> = {\n change: (payload: QueueChangePayload<Input, Output>) => void;\n};\n\nexport const IN_MEMORY_QUEUE_STORAGE = createServiceToken<IQueueStorage<any, any>>(\n \"jobqueue.storage.inMemory\"\n);\n\n/**\n * In-memory implementation of a job queue that manages asynchronous tasks.\n * Supports job scheduling, status tracking, result caching, and prefix-based filtering.\n */\nexport class InMemoryQueueStorage<Input, Output> implements IQueueStorage<Input, Output> {\n /** The prefix values for filtering jobs */\n protected readonly prefixValues: Readonly<Record<string, string | number>>;\n /** Event emitter for change notifications */\n protected readonly events = new EventEmitter<QueueEventListeners<Input, Output>>();\n\n /**\n * Creates a new in-memory job queue\n * @param queueName - Name of the queue\n * @param options - Optional configuration including prefix filters\n */\n constructor(\n public readonly queueName: string,\n options?: QueueStorageOptions\n ) {\n this.jobQueue = [];\n this.prefixValues = options?.prefixValues ?? {};\n }\n\n /** Internal array storing all jobs */\n public jobQueue: Array<JobStorageFormat<Input, Output> & Record<string, unknown>>;\n\n /**\n * Checks if a job matches the current prefix values\n */\n private matchesPrefixes(job: JobStorageFormat<Input, Output> & Record<string, unknown>): boolean {\n for (const [key, value] of Object.entries(this.prefixValues)) {\n if (job[key] !== value) {\n return false;\n }\n }\n return true;\n }\n\n /**\n * Returns a filtered and sorted list of pending jobs that are ready to run\n * Sorts by creation time to maintain FIFO order\n */\n private pendingQueue(): Array<JobStorageFormat<Input, Output> & Record<string, unknown>> {\n const now = new Date().toISOString();\n return this.jobQueue\n .filter((job) => this.matchesPrefixes(job))\n .filter((job) => job.status === JobStatus.PENDING)\n .filter((job) => !job.run_after || job.run_after <= now)\n .sort((a, b) => (a.run_after || \"\").localeCompare(b.run_after || \"\"));\n }\n\n /**\n * Adds a new job to the queue\n * Generates an ID and fingerprint if not provided\n */\n public async add(job: JobStorageFormat<Input, Output>): Promise<unknown> {\n await sleep(0);\n const now = new Date().toISOString();\n const jobWithPrefixes = job as JobStorageFormat<Input, Output> & Record<string, unknown>;\n jobWithPrefixes.id = jobWithPrefixes.id ?? uuid4();\n jobWithPrefixes.job_run_id = jobWithPrefixes.job_run_id ?? uuid4();\n jobWithPrefixes.queue = this.queueName;\n jobWithPrefixes.fingerprint = await makeFingerprint(jobWithPrefixes.input);\n jobWithPrefixes.status = JobStatus.PENDING;\n jobWithPrefixes.progress = 0;\n jobWithPrefixes.progress_message = \"\";\n jobWithPrefixes.progress_details = null;\n jobWithPrefixes.created_at = now;\n jobWithPrefixes.run_after = now;\n\n // Add prefix values to the job\n for (const [key, value] of Object.entries(this.prefixValues)) {\n jobWithPrefixes[key] = value;\n }\n\n this.jobQueue.push(jobWithPrefixes);\n this.events.emit(\"change\", { type: \"INSERT\", new: jobWithPrefixes });\n return jobWithPrefixes.id;\n }\n\n /**\n * Retrieves a job from the queue by its id.\n * @param id - The id of the job to retrieve.\n * @returns A promise that resolves to the job or undefined if the job is not found.\n */\n public async get(id: unknown): Promise<JobStorageFormat<Input, Output> | undefined> {\n await sleep(0);\n const job = this.jobQueue.find((j) => j.id === id);\n if (job && this.matchesPrefixes(job)) {\n return job;\n }\n return undefined;\n }\n\n /**\n * Retrieves a slice of jobs from the queue.\n * @param status - The status of the jobs to retrieve.\n * @param num - The number of jobs to retrieve.\n * @returns A promise that resolves to an array of jobs.\n */\n public async peek(\n status: JobStatus = JobStatus.PENDING,\n num: number = 100\n ): Promise<Array<JobStorageFormat<Input, Output>>> {\n await sleep(0);\n num = Number(num) || 100;\n return this.jobQueue\n .filter((j) => this.matchesPrefixes(j))\n .sort((a, b) => (a.run_after || \"\").localeCompare(b.run_after || \"\"))\n .filter((j) => j.status === status)\n .slice(0, num);\n }\n\n /**\n * Retrieves the next available job that is ready to be processed\n * Updates the job status to PROCESSING before returning\n * @param workerId - Optional worker ID to associate with the job\n */\n public async next(workerId?: string) {\n await sleep(0);\n const top = this.pendingQueue();\n\n const job = top[0];\n if (job) {\n const oldJob = { ...job };\n job.status = JobStatus.PROCESSING;\n job.last_ran_at = new Date().toISOString();\n job.worker_id = workerId ?? null;\n this.events.emit(\"change\", { type: \"UPDATE\", old: oldJob, new: job });\n return job;\n }\n }\n\n /**\n * Retrieves the size of the queue for a given status\n * @param status - The status of the jobs to retrieve.\n * @returns A promise that resolves to the number of jobs.\n */\n public async size(status = JobStatus.PENDING): Promise<number> {\n await sleep(0);\n return this.jobQueue.filter((j) => this.matchesPrefixes(j) && j.status === status).length;\n }\n\n /**\n * Saves the progress of a job\n * @param jobId - The id of the job to save the progress for.\n * @param progress - The progress of the job.\n * @param message - The message of the job.\n * @param details - The details of the job.\n */\n public async saveProgress(\n id: unknown,\n progress: number,\n message: string,\n details: Record<string, any> | null\n ): Promise<void> {\n await sleep(0);\n const job = this.jobQueue.find((j) => j.id === id && this.matchesPrefixes(j));\n if (!job) {\n throw new Error(`Job ${id} not found`);\n }\n\n const oldJob = { ...job };\n job.progress = progress;\n job.progress_message = message;\n job.progress_details = details;\n this.events.emit(\"change\", { type: \"UPDATE\", old: oldJob, new: job });\n }\n\n /**\n * Marks a job as complete with its output or error\n * Handles run_attempts for failed jobs and triggers completion callbacks\n * @param id - ID of the job to complete\n * @param output - Result of the job execution\n * @param error - Optional error message if job failed\n */\n public async complete(job: JobStorageFormat<Input, Output>) {\n await sleep(0);\n const index = this.jobQueue.findIndex((j) => j.id === job.id);\n if (index !== -1) {\n const existing = this.jobQueue[index];\n const currentAttempts = existing?.run_attempts ?? 0;\n job.run_attempts = currentAttempts + 1;\n this.jobQueue[index] = job;\n this.events.emit(\"change\", { type: \"UPDATE\", old: existing, new: job });\n }\n }\n\n /**\n * Aborts a job\n * @param id - The id of the job to abort.\n */\n public async abort(id: unknown): Promise<void> {\n await sleep(0);\n const job = this.jobQueue.find((j) => j.id === id && this.matchesPrefixes(j));\n if (job) {\n const oldJob = { ...job };\n job.status = JobStatus.ABORTING;\n this.events.emit(\"change\", { type: \"UPDATE\", old: oldJob, new: job });\n }\n }\n\n /**\n * Retrieves all jobs by their job_run_id.\n * @param job_run_id - The job_run_id of the jobs to retrieve.\n * @returns A promise that resolves to an array of jobs.\n */\n public async getByRunId(runId: string): Promise<Array<JobStorageFormat<Input, Output>>> {\n await sleep(0);\n return this.jobQueue.filter((job) => this.matchesPrefixes(job) && job.job_run_id === runId);\n }\n\n /**\n * Deletes all jobs from the queue that match the current prefix values.\n */\n public async deleteAll(): Promise<void> {\n await sleep(0);\n const deletedJobs = this.jobQueue.filter((job) => this.matchesPrefixes(job));\n this.jobQueue = this.jobQueue.filter((job) => !this.matchesPrefixes(job));\n for (const job of deletedJobs) {\n this.events.emit(\"change\", { type: \"DELETE\", old: job });\n }\n }\n\n /**\n * Looks up cached output for a given input\n * Uses input fingerprinting for efficient matching\n * @param input - The input to look up the cached output for.\n * @returns The cached output or null if not found\n */\n public async outputForInput(input: Input): Promise<Output | null> {\n await sleep(0);\n const fingerprint = await makeFingerprint(input);\n return (\n this.jobQueue.find(\n (j) =>\n this.matchesPrefixes(j) &&\n j.fingerprint === fingerprint &&\n j.status === JobStatus.COMPLETED\n )?.output ?? null\n );\n }\n\n /**\n * Deletes a job by its ID\n */\n public async delete(id: unknown): Promise<void> {\n await sleep(0);\n const deletedJob = this.jobQueue.find((job) => job.id === id && this.matchesPrefixes(job));\n this.jobQueue = this.jobQueue.filter((job) => !(job.id === id && this.matchesPrefixes(job)));\n if (deletedJob) {\n this.events.emit(\"change\", { type: \"DELETE\", old: deletedJob });\n }\n }\n\n /**\n * Delete jobs with a specific status older than a cutoff date\n * @param status - Status of jobs to delete\n * @param olderThanMs - Delete jobs completed more than this many milliseconds ago\n */\n public async deleteJobsByStatusAndAge(status: JobStatus, olderThanMs: number): Promise<void> {\n await sleep(0);\n const cutoffDate = new Date(Date.now() - olderThanMs).toISOString();\n const deletedJobs = this.jobQueue.filter(\n (job) =>\n this.matchesPrefixes(job) &&\n job.status === status &&\n job.completed_at &&\n job.completed_at <= cutoffDate\n );\n this.jobQueue = this.jobQueue.filter(\n (job) =>\n !this.matchesPrefixes(job) ||\n job.status !== status ||\n !job.completed_at ||\n job.completed_at > cutoffDate\n );\n for (const job of deletedJobs) {\n this.events.emit(\"change\", { type: \"DELETE\", old: job });\n }\n }\n\n /**\n * Sets up the database schema and tables.\n * No-op for in-memory storage as it doesn't require database setup.\n */\n public async setupDatabase(): Promise<void> {\n // No-op for in-memory storage\n }\n\n /**\n * Checks if a job matches the specified prefix filter\n * @param job - The job to check\n * @param prefixFilter - The prefix filter (undefined = use instance prefixes, {} = no filter)\n */\n private matchesPrefixFilter(\n job: JobStorageFormat<Input, Output>,\n prefixFilter?: Readonly<Record<string, string | number>>\n ): boolean {\n // If prefixFilter is explicitly an empty object, no prefix filtering\n if (prefixFilter && Object.keys(prefixFilter).length === 0) {\n return true;\n }\n\n // Use provided prefixFilter or fall back to instance's prefixValues\n const filterValues = prefixFilter ?? this.prefixValues;\n\n // If no filter values, match all\n if (Object.keys(filterValues).length === 0) {\n return true;\n }\n\n // Check each filter value\n const jobWithPrefixes = job as JobStorageFormat<Input, Output> & Record<string, unknown>;\n for (const [key, value] of Object.entries(filterValues)) {\n if (jobWithPrefixes[key] !== value) {\n return false;\n }\n }\n return true;\n }\n\n /**\n * Subscribes to changes in the queue.\n * Since InMemory is both client and server, changes are detected via local events.\n *\n * @param callback - Function called when a change occurs\n * @param options - Subscription options including prefix filter\n * @returns Unsubscribe function\n */\n public subscribeToChanges(\n callback: (change: QueueChangePayload<Input, Output>) => void,\n options?: QueueSubscribeOptions\n ): () => void {\n const prefixFilter = options?.prefixFilter;\n\n // Create a filtered callback wrapper\n const filteredCallback = (change: QueueChangePayload<Input, Output>) => {\n // Check if either old or new job matches the filter\n const newMatches = change.new ? this.matchesPrefixFilter(change.new, prefixFilter) : false;\n const oldMatches = change.old ? this.matchesPrefixFilter(change.old, prefixFilter) : false;\n\n if (!newMatches && !oldMatches) {\n return;\n }\n\n callback(change);\n };\n\n return this.events.subscribe(\"change\", filteredCallback);\n }\n}\n",
13
+ "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { createServiceToken } from \"@workglow/util\";\n\nexport const QUEUE_STORAGE = createServiceToken<IQueueStorage<any, any>>(\"jobqueue.storage\");\n\n/**\n * The type of a prefix column.\n * - \"uuid\" maps to UUID in PostgreSQL/Supabase, TEXT in SQLite/IndexedDB/InMemory\n * - \"number\" maps to INTEGER in PostgreSQL/Supabase/SQLite, number in IndexedDB/InMemory\n */\nexport type PrefixColumnType = \"uuid\" | \"number\";\n\n/**\n * Defines a prefix column for queue storage filtering.\n */\nexport interface PrefixColumn {\n readonly name: string;\n readonly type: PrefixColumnType;\n}\n\n/**\n * Options for configuring queue storage with prefix filters.\n */\nexport interface QueueStorageOptions {\n /** The prefix column definitions for this storage */\n readonly prefixes?: readonly PrefixColumn[];\n /** The values for each prefix column */\n readonly prefixValues?: Readonly<Record<string, string | number>>;\n}\n\nexport enum JobStatus {\n PENDING = \"PENDING\",\n PROCESSING = \"PROCESSING\",\n COMPLETED = \"COMPLETED\",\n ABORTING = \"ABORTING\",\n FAILED = \"FAILED\",\n DISABLED = \"DISABLED\",\n}\n\n/**\n * Type of change that occurred in the queue\n */\nexport type QueueChangeType = \"INSERT\" | \"UPDATE\" | \"DELETE\";\n\n/**\n * Payload describing a change to a job\n */\nexport interface QueueChangePayload<Input, Output> {\n readonly type: QueueChangeType;\n readonly old?: JobStorageFormat<Input, Output>;\n readonly new?: JobStorageFormat<Input, Output>;\n}\n\n/**\n * Options for subscribing to queue changes\n */\nexport interface QueueSubscribeOptions {\n /** Polling interval in milliseconds (used by implementations that rely on polling) */\n readonly pollingIntervalMs?: number;\n /**\n * Custom prefix filter for this subscription.\n *\n * - If not provided (undefined): Uses the storage instance's configured prefixValues\n * - If empty object ({}): Receives ALL changes across all prefix combinations\n * - If partial object: Receives changes matching the specified subset of prefixes\n *\n * @example\n * // Storage configured with prefixes: [{name: \"user_id\"}, {name: \"project_id\"}]\n * // and prefixValues: {user_id: \"abc\", project_id: \"123\"}\n *\n * // Subscribe to only this user+project (default behavior)\n * storage.subscribeToChanges(callback);\n *\n * // Subscribe to all projects for this user\n * storage.subscribeToChanges(callback, { prefixFilter: { user_id: \"abc\" } });\n *\n * // Subscribe to ALL jobs in this queue (admin/supervisor view)\n * storage.subscribeToChanges(callback, { prefixFilter: {} });\n */\n readonly prefixFilter?: Readonly<Record<string, string | number>>;\n}\n\n/**\n * Details about a job that reflect the structure in the database.\n */\nexport type JobStorageFormat<Input, Output> = {\n id?: unknown;\n job_run_id?: string;\n queue?: string;\n input: Input;\n output?: Output | null;\n error?: string | null;\n error_code?: string | null;\n fingerprint?: string;\n max_retries?: number;\n status?: JobStatus;\n created_at?: string;\n deadline_at?: string | null;\n last_ran_at?: string | null;\n run_after: string | null;\n completed_at: string | null;\n run_attempts?: number;\n progress?: number;\n progress_message?: string;\n progress_details?: Record<string, any> | null;\n worker_id?: string | null;\n};\n\n/**\n * Interface defining the storage operations for a job queue\n */\nexport interface IQueueStorage<Input, Output> {\n /**\n * Adds a job to the queue storage\n * @param job - The job to add to the queue storage\n * @returns The ID of the job\n */\n add(job: JobStorageFormat<Input, Output>): Promise<unknown>;\n\n /**\n * Gets a job from the queue storage by ID\n * @param id - The ID of the job to get\n * @returns The job with the given ID\n */\n get(id: unknown): Promise<JobStorageFormat<Input, Output> | undefined>;\n\n /**\n * Gets the next job from the queue storage\n * @param workerId - Optional worker ID to associate with the job\n * @returns The next job from the queue storage\n */\n next(workerId?: string): Promise<JobStorageFormat<Input, Output> | undefined>;\n\n /**\n * Peeks at the next job(s) from the queue storage without removing them\n * @param status - The status of the jobs to peek at\n * @param num - The number of jobs to peek at\n * @returns The jobs with the given status\n */\n peek(status?: JobStatus, num?: number): Promise<Array<JobStorageFormat<Input, Output>>>;\n\n /**\n * Gets the size of the queue storage\n * @param status - The status of the jobs to get the size for\n * @returns The size of the queue storage\n */\n size(status?: JobStatus): Promise<number>;\n\n /**\n * Completes a job in the queue storage\n * @param job - The job to complete\n */\n complete(job: JobStorageFormat<Input, Output>): Promise<void>;\n\n /**\n * Deletes all jobs from the queue storage\n */\n deleteAll(): Promise<void>;\n\n /**\n * Gets the output for a given input from the queue storage\n * @param input - The input to get the output for\n * @returns The output of the job\n */\n outputForInput(input: Input): Promise<Output | null>;\n\n /**\n * Aborts a job in the queue storage\n * @param id - The ID of the job to abort\n */\n abort(id: unknown): Promise<void>;\n\n /**\n * Gets the jobs by job run ID from the queue storage\n * @param runId - The job run ID of the jobs to get\n * @returns The jobs with the given job run ID\n */\n getByRunId(runId: string): Promise<Array<JobStorageFormat<Input, Output>>>;\n\n /**\n * Saves progress updates for a job in the queue storage\n * @param id - The ID of the job to save the progress for\n * @param progress - The progress of the job\n * @param message - The message of the job\n * @param details - The details of the job\n */\n saveProgress(\n id: unknown,\n progress: number,\n message: string,\n details: Record<string, any> | null\n ): Promise<void>;\n\n /**\n * Deletes a job by its ID from the queue storage\n * @param id - The ID of the job to delete\n */\n delete(id: unknown): Promise<void>;\n\n /**\n * Deletes jobs from the queue storage that are of a specific status and older than the specified time\n * @param status - The status of the jobs to delete\n * @param olderThanMs - The time in milliseconds that the jobs must be older than to be deleted\n */\n deleteJobsByStatusAndAge(status: JobStatus, olderThanMs: number): Promise<void>;\n\n /**\n * Sets up the database schema and tables.\n * This method should be called before using the storage in tests.\n * For production use, database setup should be done via migrations.\n */\n setupDatabase(): Promise<void>;\n\n /**\n * Subscribes to changes in the queue (including remote changes).\n * @param callback - Function called when a change occurs\n * @param options - Optional subscription options (e.g., polling interval)\n * @returns Unsubscribe function\n */\n subscribeToChanges(\n callback: (change: QueueChangePayload<Input, Output>) => void,\n options?: QueueSubscribeOptions\n ): () => void;\n}\n",
14
+ "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { createServiceToken } from \"@workglow/util\";\nimport type { PrefixColumn } from \"../queue/IQueueStorage\";\n\nexport const RATE_LIMITER_STORAGE = createServiceToken<IRateLimiterStorage>(\"ratelimiter.storage\");\n\n/**\n * Options for configuring rate limiter storage with prefix filters.\n */\nexport interface RateLimiterStorageOptions {\n /** The prefix column definitions for this storage */\n readonly prefixes?: readonly PrefixColumn[];\n /** The values for each prefix column */\n readonly prefixValues?: Readonly<Record<string, string | number>>;\n}\n\n/**\n * Record of a job execution for rate limiting tracking.\n */\nexport interface ExecutionRecord {\n readonly id?: unknown;\n readonly queue_name: string;\n readonly executed_at: string;\n}\n\n/**\n * Record of the next available time for a queue.\n */\nexport interface NextAvailableRecord {\n readonly queue_name: string;\n readonly next_available_at: string;\n}\n\n/**\n * Interface defining the storage operations for a rate limiter.\n * This separates the storage concerns from the rate limiting logic.\n */\nexport interface IRateLimiterStorage {\n /**\n * Sets up the database schema and tables.\n * This method should be called before using the storage.\n * For production use, database setup should be done via migrations.\n */\n setupDatabase(): Promise<void>;\n\n /**\n * Records a job execution for rate limiting tracking.\n * @param queueName - The name of the queue\n */\n recordExecution(queueName: string): Promise<void>;\n\n /**\n * Gets the count of executions within a time window.\n * @param queueName - The name of the queue\n * @param windowStartTime - The start of the time window (ISO string)\n * @returns The count of executions within the window\n */\n getExecutionCount(queueName: string, windowStartTime: string): Promise<number>;\n\n /**\n * Gets the oldest execution time within the window, offset by a count.\n * Used to calculate when the rate limit will allow the next execution.\n * @param queueName - The name of the queue\n * @param offset - The offset (typically maxExecutions - 1)\n * @returns The execution time or undefined if not enough executions\n */\n getOldestExecutionAtOffset(queueName: string, offset: number): Promise<string | undefined>;\n\n /**\n * Gets the next available time for a queue.\n * @param queueName - The name of the queue\n * @returns The next available time or undefined if not set\n */\n getNextAvailableTime(queueName: string): Promise<string | undefined>;\n\n /**\n * Sets the next available time for a queue.\n * @param queueName - The name of the queue\n * @param nextAvailableAt - The next available time (ISO string)\n */\n setNextAvailableTime(queueName: string, nextAvailableAt: string): Promise<void>;\n\n /**\n * Clears all rate limit entries for a queue.\n * @param queueName - The name of the queue\n */\n clear(queueName: string): Promise<void>;\n}\n",
15
+ "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { createServiceToken, sleep } from \"@workglow/util\";\nimport { IRateLimiterStorage, RateLimiterStorageOptions } from \"./IRateLimiterStorage\";\n\nexport const IN_MEMORY_RATE_LIMITER_STORAGE = createServiceToken<IRateLimiterStorage>(\n \"ratelimiter.storage.inMemory\"\n);\n\n/**\n * Record of a job execution stored in memory.\n */\ninterface ExecutionEntry {\n readonly queueName: string;\n readonly executedAt: Date;\n}\n\n/**\n * In-memory implementation of rate limiter storage.\n * Manages execution records and next available times for rate limiting.\n */\nexport class InMemoryRateLimiterStorage implements IRateLimiterStorage {\n /** The prefix values for filtering */\n protected readonly prefixValues: Readonly<Record<string, string | number>>;\n\n /** Execution records keyed by a composite of prefix values and queue name */\n private readonly executions: Map<string, ExecutionEntry[]> = new Map();\n\n /** Next available times keyed by a composite of prefix values and queue name */\n private readonly nextAvailableTimes: Map<string, Date> = new Map();\n\n constructor(options?: RateLimiterStorageOptions) {\n this.prefixValues = options?.prefixValues ?? {};\n }\n\n /**\n * Creates a storage key from the queue name and prefix values.\n */\n private makeKey(queueName: string): string {\n const prefixPart = Object.entries(this.prefixValues)\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([k, v]) => `${k}:${v}`)\n .join(\"|\");\n return prefixPart ? `${prefixPart}|${queueName}` : queueName;\n }\n\n public async setupDatabase(): Promise<void> {\n // No-op for in-memory storage\n }\n\n public async recordExecution(queueName: string): Promise<void> {\n await sleep(0);\n const key = this.makeKey(queueName);\n const executions = this.executions.get(key) ?? [];\n executions.push({\n queueName,\n executedAt: new Date(),\n });\n this.executions.set(key, executions);\n }\n\n public async getExecutionCount(queueName: string, windowStartTime: string): Promise<number> {\n await sleep(0);\n const key = this.makeKey(queueName);\n const executions = this.executions.get(key) ?? [];\n const windowStart = new Date(windowStartTime);\n return executions.filter((e) => e.executedAt > windowStart).length;\n }\n\n public async getOldestExecutionAtOffset(\n queueName: string,\n offset: number\n ): Promise<string | undefined> {\n await sleep(0);\n const key = this.makeKey(queueName);\n const executions = this.executions.get(key) ?? [];\n const sorted = [...executions].sort((a, b) => a.executedAt.getTime() - b.executedAt.getTime());\n const execution = sorted[offset];\n return execution?.executedAt.toISOString();\n }\n\n public async getNextAvailableTime(queueName: string): Promise<string | undefined> {\n await sleep(0);\n const key = this.makeKey(queueName);\n const time = this.nextAvailableTimes.get(key);\n return time?.toISOString();\n }\n\n public async setNextAvailableTime(queueName: string, nextAvailableAt: string): Promise<void> {\n await sleep(0);\n const key = this.makeKey(queueName);\n this.nextAvailableTimes.set(key, new Date(nextAvailableAt));\n }\n\n public async clear(queueName: string): Promise<void> {\n await sleep(0);\n const key = this.makeKey(queueName);\n this.executions.delete(key);\n this.nextAvailableTimes.delete(key);\n }\n}\n",
14
16
  "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { createServiceToken, DataPortSchemaObject, FromSchema } from \"@workglow/util\";\nimport {\n ensureIndexedDbTable,\n ExpectedIndexDefinition,\n MigrationOptions,\n} from \"../util/IndexedDbTable\";\nimport { ITabularRepository } from \"./ITabularRepository\";\nimport { TabularRepository } from \"./TabularRepository\";\n\nexport const IDB_TABULAR_REPOSITORY = createServiceToken<\n ITabularRepository<any, any, any, any, any>\n>(\"storage.tabularRepository.indexedDb\");\n\n/**\n * A tabular repository implementation using IndexedDB for browser-based storage.\n *\n * @template Schema - The schema definition for the entity\n * @template PrimaryKeyNames - Array of property names that form the primary key\n */\nexport class IndexedDbTabularRepository<\n Schema extends DataPortSchemaObject,\n PrimaryKeyNames extends ReadonlyArray<keyof Schema[\"properties\"]>,\n // computed types\n Entity = FromSchema<Schema>,\n PrimaryKey = Pick<Entity, PrimaryKeyNames[number] & keyof Entity>,\n Value = Omit<Entity, PrimaryKeyNames[number] & keyof Entity>,\n> extends TabularRepository<Schema, PrimaryKeyNames, Entity, PrimaryKey, Value> {\n /** Promise that resolves to the IndexedDB database instance */\n private db: IDBDatabase | undefined;\n /** Migration options for database schema changes */\n private migrationOptions: MigrationOptions;\n\n /**\n * Creates a new IndexedDB-based tabular repository.\n * @param table - Name of the IndexedDB store to use.\n * @param schema - Schema defining the structure of the entity\n * @param primaryKeyNames - Array of property names that form the primary key\n * @param indexes - Array of columns or column arrays to make searchable. Each string or single column creates a single-column index,\n * while each array creates a compound index with columns in the specified order.\n * @param migrationOptions - Options for handling database schema migrations\n */\n constructor(\n public table: string = \"tabular_store\",\n schema: Schema,\n primaryKeyNames: PrimaryKeyNames,\n indexes: Array<keyof Entity | Array<keyof Entity>> = [],\n migrationOptions: MigrationOptions = {}\n ) {\n super(schema, primaryKeyNames, indexes);\n this.migrationOptions = migrationOptions;\n }\n\n /**\n * Sets up the IndexedDB database table with the required schema and indexes.\n * Must be called before using any other methods.\n */\n public async setupDatabase(): Promise<IDBDatabase> {\n if (this.db) return this.db;\n const pkColumns = super.primaryKeyColumns() as string[];\n\n // Create index definitions for both single and compound indexes\n const expectedIndexes: ExpectedIndexDefinition[] = [];\n\n for (const spec of this.indexes) {\n // Handle compound index\n const columns = spec as Array<keyof Entity>;\n // Skip if this is just the primary key or a prefix of it\n if (columns.length <= pkColumns.length) {\n const isPkPrefix = columns.every((col, idx) => col === pkColumns[idx]);\n if (isPkPrefix) continue;\n }\n\n // Create compound index name and keyPath\n const columnNames = columns.map((col) => String(col));\n const indexName = columnNames.join(\"_\");\n expectedIndexes.push({\n name: indexName,\n keyPath: columnNames.length === 1 ? columnNames[0] : columnNames,\n options: { unique: false },\n });\n }\n\n const primaryKey = pkColumns.length === 1 ? pkColumns[0] : pkColumns;\n\n // Ensure that our table is created/upgraded only if the structure (indexes) has changed.\n this.db = await ensureIndexedDbTable(\n this.table,\n primaryKey,\n expectedIndexes,\n this.migrationOptions\n );\n return this.db;\n }\n\n /**\n * Stores a row in the repository.\n * @param record - The entity to store.\n * @returns The stored entity\n * @emits put - Emitted when the value is successfully stored\n */\n async put(record: Entity): Promise<Entity> {\n const db = await this.setupDatabase();\n const { key } = this.separateKeyValueFromCombined(record);\n // Merge key and value, ensuring all fields are at the root level for indexing\n return new Promise((resolve, reject) => {\n const transaction = db.transaction(this.table, \"readwrite\");\n const store = transaction.objectStore(this.table);\n const request = store.put(record);\n request.onerror = () => {\n reject(request.error);\n };\n request.onsuccess = () => {\n this.events.emit(\"put\", record);\n resolve(record);\n };\n });\n }\n\n /**\n * Stores multiple rows in the repository in a bulk operation.\n * @param records - Array of entities to store.\n * @returns Array of stored entities\n * @emits put - Emitted for each record successfully stored\n */\n async putBulk(records: Entity[]): Promise<Entity[]> {\n const db = await this.setupDatabase();\n return new Promise((resolve, reject) => {\n const transaction = db.transaction(this.table, \"readwrite\");\n const store = transaction.objectStore(this.table);\n\n let completed = 0;\n let hasError = false;\n\n transaction.onerror = () => {\n if (!hasError) {\n hasError = true;\n reject(transaction.error);\n }\n };\n\n transaction.oncomplete = () => {\n if (!hasError) {\n resolve(records);\n }\n };\n\n // Add all records to the transaction\n for (const record of records) {\n const request = store.put(record);\n request.onsuccess = () => {\n this.events.emit(\"put\", record);\n completed++;\n };\n request.onerror = () => {\n if (!hasError) {\n hasError = true;\n reject(request.error);\n }\n };\n }\n });\n }\n\n protected getPrimaryKeyAsOrderedArray(key: PrimaryKey) {\n return super\n .getPrimaryKeyAsOrderedArray(key)\n .map((value) => (typeof value === \"bigint\" ? value.toString() : value));\n }\n\n private getIndexedKey(key: PrimaryKey): any {\n const keys = super\n .getPrimaryKeyAsOrderedArray(key)\n .map((value) => (typeof value === \"bigint\" ? value.toString() : value));\n return keys.length === 1 ? keys[0] : keys;\n }\n\n /**\n * Retrieves a value from the repository by its key.\n * @param key - The key object.\n * @returns The value object or undefined if not found.\n * @emits get - Emitted when the value is successfully retrieved\n */\n async get(key: PrimaryKey): Promise<Entity | undefined> {\n const db = await this.setupDatabase();\n return new Promise((resolve, reject) => {\n const transaction = db.transaction(this.table, \"readonly\");\n const store = transaction.objectStore(this.table);\n const request = store.get(this.getIndexedKey(key));\n request.onerror = () => reject(request.error);\n request.onsuccess = () => {\n if (!request.result) {\n this.events.emit(\"get\", key, undefined);\n resolve(undefined);\n return;\n }\n this.events.emit(\"get\", key, request.result);\n resolve(request.result);\n };\n });\n }\n\n /**\n * Returns an array of all entries in the repository.\n * @returns Array of all entries in the repository.\n */\n async getAll(): Promise<Entity[] | undefined> {\n const db = await this.setupDatabase();\n const transaction = db.transaction(this.table, \"readonly\");\n const store = transaction.objectStore(this.table);\n const request = store.getAll();\n return new Promise((resolve, reject) => {\n request.onerror = () => reject(request.error);\n request.onsuccess = () => {\n const values = request.result;\n resolve(values.length > 0 ? values : undefined);\n };\n });\n }\n\n /**\n * Searches for records matching the specified partial query.\n * It uses an appropriate index if one exists, or scans all records.\n * @param key - Partial query object.\n * @returns Array of matching records or undefined.\n */\n async search(key: Partial<Entity>): Promise<Entity[] | undefined> {\n const db = await this.setupDatabase();\n const searchKeys = Object.keys(key) as Array<keyof Entity>;\n if (searchKeys.length === 0) {\n return undefined;\n }\n\n // Find the best matching index for the search\n const bestIndex = this.findBestMatchingIndex(searchKeys);\n if (!bestIndex) {\n throw new Error(\"No suitable index found for the search criteria\");\n }\n\n return new Promise((resolve, reject) => {\n const transaction = db.transaction(this.table, \"readonly\");\n const store = transaction.objectStore(this.table);\n\n // For compound indexes, the index name is the columns joined by underscore\n const indexName = bestIndex.join(\"_\");\n const primaryKeyName = this.primaryKeyColumns().join(\"_\");\n const isPrimaryKey = indexName === primaryKeyName;\n\n // Get the values for the index columns that we have\n const indexValues: IDBValidKey[] = [];\n\n // Collect values for consecutive columns from the start of the index\n for (const col of bestIndex) {\n const val = key[col];\n // Break on first undefined value for compound index\n if (val === undefined) break;\n if (typeof val !== \"string\" && typeof val !== \"number\") {\n throw new Error(`Invalid value type for indexed column ${String(col)}`);\n }\n indexValues.push(val);\n }\n\n // If we have at least one valid index value, use it\n if (indexValues.length > 0) {\n const index = isPrimaryKey ? store : store.index(indexName);\n const isPartialMatch = indexValues.length < bestIndex.length;\n\n if (isPartialMatch) {\n // For partial matches on compound indexes, we need to handle two cases:\n // 1. If all columns in the compound index are required in the schema,\n // we can use cursor-based prefix matching (efficient)\n // 2. If any columns are optional (could be undefined), records without those\n // values won't be in the index, so we must do a full scan\n\n // Check if all columns in the compound index are required\n const allColumnsRequired = bestIndex.every((col) => {\n const colName = String(col);\n return this.schema.required?.includes(colName);\n });\n\n if (allColumnsRequired) {\n // All index columns are required, so all records will be in the index\n // We can use cursor-based prefix matching for better performance\n const results: Entity[] = [];\n const keyRange = IDBKeyRange.lowerBound(indexValues);\n const cursorRequest = index.openCursor(keyRange);\n\n cursorRequest.onsuccess = () => {\n const cursor = cursorRequest.result;\n if (cursor) {\n const item = cursor.value as Entity;\n const cursorKey = Array.isArray(cursor.key) ? cursor.key : [cursor.key];\n\n // Check if cursor key still matches our prefix\n const prefixMatches = indexValues.every((val, idx) => cursorKey[idx] === val);\n\n if (!prefixMatches) {\n // Moved past our prefix range\n resolve(results.length > 0 ? results : undefined);\n return;\n }\n\n // Check all search criteria (including non-indexed columns)\n // @ts-ignore\n const matches = Object.entries(key).every(([k, v]) => item[k] === v);\n if (matches) {\n results.push(item);\n }\n cursor.continue();\n } else {\n // Cursor exhausted\n resolve(results.length > 0 ? results : undefined);\n }\n };\n\n cursorRequest.onerror = () => {\n reject(cursorRequest.error);\n };\n } else {\n // Some index columns are optional, records with undefined values won't be indexed\n // Fall back to full scan to ensure we don't miss any matching records\n const getAllRequest = store.getAll();\n\n getAllRequest.onsuccess = () => {\n const allRecords: Entity[] = getAllRequest.result;\n const results = allRecords.filter((item) =>\n // @ts-ignore\n Object.entries(key).every(([k, v]) => item[k] === v)\n );\n resolve(results.length > 0 ? results : undefined);\n };\n\n getAllRequest.onerror = () => {\n reject(getAllRequest.error);\n };\n }\n } else {\n // Exact match: use getAll with the exact key\n const request = index.getAll(indexValues.length === 1 ? indexValues[0] : indexValues);\n\n request.onsuccess = () => {\n // Filter results for any additional search keys\n const results = request.result.filter((item: Entity) =>\n // @ts-ignore\n Object.entries(key).every(([k, v]) => item[k] === v)\n );\n resolve(results.length > 0 ? results : undefined);\n };\n\n request.onerror = () => {\n console.error(\"Search error:\", request.error);\n reject(request.error);\n };\n }\n } else {\n throw new Error(`No valid values provided for indexed columns: ${bestIndex.join(\", \")}`);\n }\n });\n }\n\n /**\n * Deletes a row from the repository.\n * @param key - The key object to delete.\n */\n async delete(key: PrimaryKey): Promise<void> {\n const db = await this.setupDatabase();\n return new Promise((resolve, reject) => {\n const transaction = db.transaction(this.table, \"readwrite\");\n const store = transaction.objectStore(this.table);\n const request = store.delete(this.getIndexedKey(key));\n request.onerror = () => reject(request.error);\n request.onsuccess = () => {\n this.events.emit(\"delete\", key as keyof Entity);\n resolve();\n };\n });\n }\n\n /**\n * Deletes all records from the repository.\n * @emits clearall - Emitted when all values are deleted\n */\n async deleteAll(): Promise<void> {\n const db = await this.setupDatabase();\n return new Promise((resolve, reject) => {\n const transaction = db.transaction(this.table, \"readwrite\");\n const store = transaction.objectStore(this.table);\n const request = store.clear();\n request.onerror = () => reject(request.error);\n request.onsuccess = () => {\n this.events.emit(\"clearall\");\n resolve();\n };\n });\n }\n\n /**\n * Returns the total number of rows in the repository.\n * @returns Count of stored items.\n */\n async size(): Promise<number> {\n const db = await this.setupDatabase();\n return new Promise((resolve, reject) => {\n const transaction = db.transaction(this.table, \"readonly\");\n const store = transaction.objectStore(this.table);\n const request = store.count();\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve(request.result);\n });\n }\n\n /**\n * Deletes all entries with a date column value older than the provided date\n * @param column - The name of the date column to compare against\n * @param value - The value to compare against\n * @param operator - The operator to use for comparison\n */\n async deleteSearch(\n column: keyof Entity,\n value: Entity[keyof Entity],\n operator: \"=\" | \"<\" | \"<=\" | \">\" | \">=\" = \"=\"\n ): Promise<void> {\n const db = await this.setupDatabase();\n\n return new Promise(async (resolve, reject) => {\n try {\n // For equality operator, we can use the search method directly\n if (operator === \"=\") {\n // Create a search key based on the column and value\n const searchKey: Partial<Entity> = { [column]: value } as Partial<Entity>;\n\n // Search for records to delete\n const recordsToDelete = await this.search(searchKey);\n\n if (!recordsToDelete || recordsToDelete.length === 0) {\n // No records found to delete\n this.events.emit(\"delete\", column);\n resolve();\n return;\n }\n\n const transaction = db.transaction(this.table, \"readwrite\");\n const store = transaction.objectStore(this.table);\n\n // Set up transaction event handlers\n transaction.oncomplete = () => {\n this.events.emit(\"delete\", column);\n resolve();\n };\n\n transaction.onerror = () => {\n reject(transaction.error);\n };\n\n // Delete each record that matches the criteria\n for (const record of recordsToDelete) {\n // Extract the primary key from the record\n const primaryKey = this.primaryKeyColumns().reduce((key, column) => {\n // @ts-ignore - We know these properties exist on the record\n key[column] = record[column];\n return key;\n }, {} as PrimaryKey);\n\n // Delete the record using the primary key\n const request = store.delete(this.getIndexedKey(primaryKey));\n\n request.onerror = () => {\n console.error(\"Error deleting record:\", request.error);\n };\n }\n } else {\n // For non-equality operators, we need to get all records and filter\n const transaction = db.transaction(this.table, \"readwrite\");\n const store = transaction.objectStore(this.table);\n\n // Set up transaction event handlers\n transaction.oncomplete = () => {\n this.events.emit(\"delete\", column);\n resolve();\n };\n\n transaction.onerror = () => {\n reject(transaction.error);\n };\n\n // Get all records\n const getAllRequest = store.getAll();\n\n getAllRequest.onsuccess = () => {\n const allRecords: Entity[] = getAllRequest.result;\n\n // Filter records based on the operator and value\n const recordsToDelete = allRecords.filter((record) => {\n const recordValue = record[column];\n\n // Skip null values or handle them based on business logic\n if (\n recordValue === null ||\n recordValue === undefined ||\n value === null ||\n value === undefined\n ) {\n return false;\n }\n\n switch (operator) {\n case \"<\":\n return recordValue < value;\n case \"<=\":\n return recordValue <= value;\n case \">\":\n return recordValue > value;\n case \">=\":\n return recordValue >= value;\n default:\n return false;\n }\n });\n\n if (recordsToDelete.length === 0) {\n // No records to delete\n return;\n }\n\n // Delete each record that matches the criteria\n for (const record of recordsToDelete) {\n // Extract the primary key from the record\n const primaryKey = this.primaryKeyColumns().reduce((key, column) => {\n // @ts-ignore - We know these properties exist on the record\n key[column] = record[column];\n return key;\n }, {} as PrimaryKey);\n\n // Delete the record using the primary key\n const request = store.delete(this.getIndexedKey(primaryKey));\n\n request.onerror = () => {\n console.error(\"Error deleting record:\", request.error);\n };\n }\n };\n\n getAllRequest.onerror = () => {\n reject(getAllRequest.error);\n };\n }\n } catch (error) {\n reject(error);\n }\n });\n }\n\n /**\n * Destroys this repository and frees up resources.\n */\n destroy(): void {\n this.db?.close();\n }\n}\n",
15
17
  "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\n// Production-ready IndexedDB table management with proper migration support.\n// Handles schema evolution without data loss by incrementally migrating the database\n// structure and transforming existing data as needed.\n\nexport interface ExpectedIndexDefinition {\n name: string;\n keyPath: string | string[];\n options?: IDBIndexParameters;\n}\n\nexport interface MigrationContext {\n db: IDBDatabase;\n transaction: IDBTransaction;\n oldVersion: number;\n newVersion: number;\n tableName: string;\n}\n\nexport interface DataTransformer {\n (oldData: any): any | Promise<any>;\n}\n\nexport interface MigrationOptions {\n /** Custom data transformer to apply during migration */\n dataTransformer?: DataTransformer;\n /** Whether to allow destructive operations (delete and recreate). Default: false */\n allowDestructiveMigration?: boolean;\n /** Callback for migration progress/logging */\n onMigrationProgress?: (message: string, progress?: number) => void;\n /** Callback for migration errors (non-fatal warnings) */\n onMigrationWarning?: (message: string, error?: Error) => void;\n}\n\ninterface SchemaSnapshot {\n version: number;\n primaryKey: string | string[];\n indexes: ExpectedIndexDefinition[];\n recordCount?: number;\n timestamp: number;\n}\n\nconst METADATA_STORE_NAME = \"__schema_metadata__\";\n\n/**\n * Stores metadata about the database schema for migration tracking\n */\nasync function saveSchemaMetadata(\n db: IDBDatabase,\n tableName: string,\n snapshot: SchemaSnapshot\n): Promise<void> {\n return new Promise((resolve, reject) => {\n try {\n const transaction = db.transaction(METADATA_STORE_NAME, \"readwrite\");\n const store = transaction.objectStore(METADATA_STORE_NAME);\n const request = store.put({ ...snapshot, tableName }, tableName);\n\n request.onsuccess = () => resolve();\n request.onerror = () => reject(request.error);\n transaction.onerror = () => reject(transaction.error);\n } catch (err) {\n // Metadata store might not exist in old databases, that's OK\n resolve();\n }\n });\n}\n\n/**\n * Retrieves stored metadata about the database schema\n */\nasync function loadSchemaMetadata(\n db: IDBDatabase,\n tableName: string\n): Promise<SchemaSnapshot | null> {\n return new Promise((resolve) => {\n try {\n if (!db.objectStoreNames.contains(METADATA_STORE_NAME)) {\n resolve(null);\n return;\n }\n\n const transaction = db.transaction(METADATA_STORE_NAME, \"readonly\");\n const store = transaction.objectStore(METADATA_STORE_NAME);\n const request = store.get(tableName);\n\n request.onsuccess = () => resolve(request.result || null);\n request.onerror = () => resolve(null);\n transaction.onerror = () => resolve(null);\n } catch (err) {\n resolve(null);\n }\n });\n}\n\n/**\n * Opens an IndexedDB database with proper error handling\n */\nasync function openIndexedDbTable(\n tableName: string,\n version?: number,\n upgradeNeededCallback?: (event: IDBVersionChangeEvent) => void\n): Promise<IDBDatabase> {\n return new Promise((resolve, reject) => {\n const openRequest = indexedDB.open(tableName, version);\n\n openRequest.onsuccess = (event) => {\n const db = (event.target as IDBOpenDBRequest).result;\n\n // Handle unexpected close\n db.onversionchange = () => {\n db.close();\n };\n\n resolve(db);\n };\n\n openRequest.onupgradeneeded = (event) => {\n if (upgradeNeededCallback) {\n upgradeNeededCallback(event);\n }\n };\n\n openRequest.onerror = () => {\n const error = openRequest.error;\n // Check if it's a VersionError - this means the database exists at a higher version\n if (error && error.name === \"VersionError\") {\n reject(\n new Error(\n `Database ${tableName} exists at a higher version. Cannot open at version ${version || \"current\"}.`\n )\n );\n } else {\n reject(error);\n }\n };\n openRequest.onblocked = () => {\n reject(\n new Error(`Database ${tableName} is blocked. Close all other tabs using this database.`)\n );\n };\n });\n}\n\n/**\n * Deletes an IndexedDB database completely\n */\nasync function deleteIndexedDbTable(tableName: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const deleteRequest = indexedDB.deleteDatabase(tableName);\n\n deleteRequest.onsuccess = () => resolve();\n deleteRequest.onerror = () => reject(deleteRequest.error);\n deleteRequest.onblocked = () => {\n reject(\n new Error(`Cannot delete database ${tableName}. Close all other tabs using this database.`)\n );\n };\n });\n}\n\n/**\n * Compares two schema definitions to determine what changes are needed\n */\ninterface SchemaDiff {\n indexesToAdd: ExpectedIndexDefinition[];\n indexesToRemove: string[];\n indexesToModify: ExpectedIndexDefinition[];\n primaryKeyChanged: boolean;\n needsObjectStoreRecreation: boolean;\n}\n\nfunction compareSchemas(\n store: IDBObjectStore,\n expectedPrimaryKey: string | string[],\n expectedIndexes: ExpectedIndexDefinition[]\n): SchemaDiff {\n const diff: SchemaDiff = {\n indexesToAdd: [],\n indexesToRemove: [],\n indexesToModify: [],\n primaryKeyChanged: false,\n needsObjectStoreRecreation: false,\n };\n\n // Check primary key\n const actualKeyPath = store.keyPath;\n const normalizedExpected = Array.isArray(expectedPrimaryKey)\n ? expectedPrimaryKey\n : expectedPrimaryKey;\n const normalizedActual = Array.isArray(actualKeyPath) ? actualKeyPath : actualKeyPath;\n\n if (JSON.stringify(normalizedExpected) !== JSON.stringify(normalizedActual)) {\n diff.primaryKeyChanged = true;\n diff.needsObjectStoreRecreation = true;\n return diff; // If primary key changed, we need full recreation\n }\n\n // Build a map of existing indexes\n const existingIndexes = new Map<string, IDBIndex>();\n for (let i = 0; i < store.indexNames.length; i++) {\n const indexName = store.indexNames[i];\n existingIndexes.set(indexName, store.index(indexName));\n }\n\n // Check for indexes to add or modify\n for (const expectedIdx of expectedIndexes) {\n const existingIdx = existingIndexes.get(expectedIdx.name);\n\n if (!existingIdx) {\n diff.indexesToAdd.push(expectedIdx);\n } else {\n // Compare index properties\n const expectedKeyPath = Array.isArray(expectedIdx.keyPath)\n ? expectedIdx.keyPath\n : [expectedIdx.keyPath];\n const actualKeyPath = Array.isArray(existingIdx.keyPath)\n ? existingIdx.keyPath\n : [existingIdx.keyPath];\n\n const keyPathChanged = JSON.stringify(expectedKeyPath) !== JSON.stringify(actualKeyPath);\n const uniqueChanged = existingIdx.unique !== (expectedIdx.options?.unique ?? false);\n const multiEntryChanged =\n existingIdx.multiEntry !== (expectedIdx.options?.multiEntry ?? false);\n\n if (keyPathChanged || uniqueChanged || multiEntryChanged) {\n diff.indexesToModify.push(expectedIdx);\n }\n\n existingIndexes.delete(expectedIdx.name);\n }\n }\n\n // Remaining indexes should be removed\n diff.indexesToRemove = Array.from(existingIndexes.keys());\n\n return diff;\n}\n\n/**\n * Reads all data from a store\n */\nasync function readAllData(store: IDBObjectStore): Promise<any[]> {\n return new Promise((resolve, reject) => {\n const request = store.getAll();\n request.onsuccess = () => resolve(request.result || []);\n request.onerror = () => reject(request.error);\n });\n}\n\n/**\n * Writes data back to a store\n */\nasync function writeAllData(store: IDBObjectStore, data: any[]): Promise<void> {\n return new Promise((resolve, reject) => {\n let completed = 0;\n const total = data.length;\n\n if (total === 0) {\n resolve();\n return;\n }\n\n const transaction = store.transaction;\n transaction.oncomplete = () => resolve();\n transaction.onerror = () => reject(transaction.error);\n\n for (const record of data) {\n const request = store.put(record);\n request.onerror = () => reject(request.error);\n }\n });\n}\n\n/**\n * Performs a non-destructive migration by adding/removing indexes\n */\nasync function performIncrementalMigration(\n db: IDBDatabase,\n tableName: string,\n diff: SchemaDiff,\n options: MigrationOptions = {}\n): Promise<IDBDatabase> {\n const currentVersion = db.version;\n const newVersion = currentVersion + 1;\n\n db.close();\n\n options.onMigrationProgress?.(\n `Migrating ${tableName} from version ${currentVersion} to ${newVersion}...`,\n 0\n );\n\n return openIndexedDbTable(tableName, newVersion, (event: IDBVersionChangeEvent) => {\n const db = (event.target as IDBOpenDBRequest).result;\n const transaction = (event.target as IDBOpenDBRequest).transaction!;\n const store = transaction.objectStore(tableName);\n\n // Remove outdated indexes\n for (const indexName of diff.indexesToRemove) {\n options.onMigrationProgress?.(`Removing index: ${indexName}`, 0.2);\n store.deleteIndex(indexName);\n }\n\n // Remove and recreate modified indexes\n for (const indexDef of diff.indexesToModify) {\n options.onMigrationProgress?.(`Updating index: ${indexDef.name}`, 0.4);\n if (store.indexNames.contains(indexDef.name)) {\n store.deleteIndex(indexDef.name);\n }\n store.createIndex(indexDef.name, indexDef.keyPath, indexDef.options);\n }\n\n // Add new indexes\n for (const indexDef of diff.indexesToAdd) {\n options.onMigrationProgress?.(`Adding index: ${indexDef.name}`, 0.6);\n store.createIndex(indexDef.name, indexDef.keyPath, indexDef.options);\n }\n\n options.onMigrationProgress?.(`Migration complete`, 1.0);\n });\n}\n\n/**\n * Performs a destructive migration by recreating the object store\n * This is needed when the primary key changes\n */\nasync function performDestructiveMigration(\n db: IDBDatabase,\n tableName: string,\n primaryKey: string | string[],\n expectedIndexes: ExpectedIndexDefinition[],\n options: MigrationOptions = {}\n): Promise<IDBDatabase> {\n if (!options.allowDestructiveMigration) {\n throw new Error(\n `Destructive migration required for ${tableName} but not allowed. ` +\n `Primary key has changed. Set allowDestructiveMigration=true to proceed with data loss, ` +\n `or provide a dataTransformer to migrate data.`\n );\n }\n\n const currentVersion = db.version;\n const newVersion = currentVersion + 1;\n\n options.onMigrationProgress?.(\n `Performing destructive migration of ${tableName}. Reading existing data...`,\n 0\n );\n\n // Read all existing data\n let existingData: any[] = [];\n try {\n const transaction = db.transaction(tableName, \"readonly\");\n const store = transaction.objectStore(tableName);\n existingData = await readAllData(store);\n options.onMigrationProgress?.(`Read ${existingData.length} records`, 0.3);\n } catch (err) {\n options.onMigrationWarning?.(\n `Failed to read existing data during migration: ${err}`,\n err as Error\n );\n }\n\n db.close();\n\n // Apply data transformer if provided\n if (options.dataTransformer && existingData.length > 0) {\n options.onMigrationProgress?.(`Transforming ${existingData.length} records...`, 0.4);\n try {\n const transformed = [];\n for (let i = 0; i < existingData.length; i++) {\n const record = existingData[i];\n const transformedRecord = await options.dataTransformer(record);\n if (transformedRecord !== undefined && transformedRecord !== null) {\n transformed.push(transformedRecord);\n }\n if (i % 100 === 0) {\n options.onMigrationProgress?.(\n `Transformed ${i}/${existingData.length} records`,\n 0.4 + (i / existingData.length) * 0.3\n );\n }\n }\n existingData = transformed;\n options.onMigrationProgress?.(`Transformation complete: ${existingData.length} records`, 0.7);\n } catch (err) {\n options.onMigrationWarning?.(\n `Data transformation failed: ${err}. Some data may be lost.`,\n err as Error\n );\n existingData = [];\n }\n }\n\n // Open with new version and recreate object store\n options.onMigrationProgress?.(`Recreating object store...`, 0.75);\n\n const newDb = await openIndexedDbTable(tableName, newVersion, (event: IDBVersionChangeEvent) => {\n const db = (event.target as IDBOpenDBRequest).result;\n const transaction = (event.target as IDBOpenDBRequest).transaction!;\n\n // Delete old object store if it exists\n if (db.objectStoreNames.contains(tableName)) {\n db.deleteObjectStore(tableName);\n }\n\n // Create new object store with new schema\n const store = db.createObjectStore(tableName, { keyPath: primaryKey });\n\n // Create indexes\n for (const idx of expectedIndexes) {\n store.createIndex(idx.name, idx.keyPath, idx.options);\n }\n\n // Restore data\n if (existingData.length > 0) {\n options.onMigrationProgress?.(`Restoring ${existingData.length} records...`, 0.8);\n\n for (const record of existingData) {\n try {\n store.put(record);\n } catch (err) {\n options.onMigrationWarning?.(`Failed to restore record: ${err}`, err as Error);\n }\n }\n }\n });\n\n options.onMigrationProgress?.(`Destructive migration complete`, 1.0);\n\n return newDb;\n}\n\n/**\n * Creates a new database with the specified schema\n */\nasync function createNewDatabase(\n tableName: string,\n primaryKey: string | string[],\n expectedIndexes: ExpectedIndexDefinition[],\n options: MigrationOptions = {}\n): Promise<IDBDatabase> {\n options.onMigrationProgress?.(`Creating new database: ${tableName}`, 0);\n\n // Delete existing database if it exists to avoid version conflicts\n try {\n await deleteIndexedDbTable(tableName);\n // Wait a bit for deletion to complete\n await new Promise((resolve) => setTimeout(resolve, 50));\n } catch (err) {\n // Ignore errors - database might not exist\n }\n\n const version = 1;\n\n const db = await openIndexedDbTable(tableName, version, (event: IDBVersionChangeEvent) => {\n const db = (event.target as IDBOpenDBRequest).result;\n\n // Create metadata store\n if (!db.objectStoreNames.contains(METADATA_STORE_NAME)) {\n db.createObjectStore(METADATA_STORE_NAME, { keyPath: \"tableName\" });\n }\n\n // Create main object store\n const store = db.createObjectStore(tableName, { keyPath: primaryKey });\n\n // Create indexes\n for (const idx of expectedIndexes) {\n store.createIndex(idx.name, idx.keyPath, idx.options);\n }\n });\n\n // Save schema metadata\n const snapshot: SchemaSnapshot = {\n version: db.version,\n primaryKey,\n indexes: expectedIndexes,\n recordCount: 0,\n timestamp: Date.now(),\n };\n\n await saveSchemaMetadata(db, tableName, snapshot);\n\n options.onMigrationProgress?.(`Database created successfully`, 1.0);\n\n return db;\n}\n\n/**\n * Ensures that an IndexedDB table exists with the specified schema.\n * Performs migrations as needed without data loss when possible.\n */\nexport async function ensureIndexedDbTable(\n tableName: string,\n primaryKey: string | string[],\n expectedIndexes: ExpectedIndexDefinition[] = [],\n options: MigrationOptions = {}\n): Promise<IDBDatabase> {\n try {\n // Try to open existing database at current version (or create if doesn't exist)\n let db: IDBDatabase;\n let wasJustCreated = false;\n try {\n // Open without version - this will open at current version if exists, or create at version 1 if doesn't exist\n db = await openIndexedDbTable(tableName);\n\n // Check if database was just created (version 1 and no object stores)\n // This happens when indexedDB.open creates a new database without stores\n if (db.version === 1 && !db.objectStoreNames.contains(tableName)) {\n wasJustCreated = true;\n db.close();\n }\n } catch (err: any) {\n // If opening fails, database might not exist or there's a version conflict\n // Try to create it fresh\n options.onMigrationProgress?.(\n `Database ${tableName} does not exist or has version conflict, creating...`,\n 0\n );\n return await createNewDatabase(tableName, primaryKey, expectedIndexes, options);\n }\n\n // If database was just created, we need to create the stores\n // We'll upgrade from version 1 to version 1 (which triggers onupgradeneeded with oldVersion=0)\n // Actually, we need to explicitly create at version 1 with stores\n if (wasJustCreated) {\n options.onMigrationProgress?.(`Creating new database: ${tableName}`, 0);\n // Delete the empty database and create it properly at version 1\n try {\n await deleteIndexedDbTable(tableName);\n await new Promise((resolve) => setTimeout(resolve, 50));\n } catch (err) {\n // Ignore errors\n }\n\n // Create at version 1 with stores\n db = await openIndexedDbTable(tableName, 1, (event: IDBVersionChangeEvent) => {\n const db = (event.target as IDBOpenDBRequest).result;\n\n // Create metadata store\n if (!db.objectStoreNames.contains(METADATA_STORE_NAME)) {\n db.createObjectStore(METADATA_STORE_NAME, { keyPath: \"tableName\" });\n }\n\n // Create main object store\n const store = db.createObjectStore(tableName, { keyPath: primaryKey });\n\n // Create indexes\n for (const idx of expectedIndexes) {\n store.createIndex(idx.name, idx.keyPath, idx.options);\n }\n });\n\n // Save schema metadata\n const snapshot: SchemaSnapshot = {\n version: db.version,\n primaryKey,\n indexes: expectedIndexes,\n recordCount: 0,\n timestamp: Date.now(),\n };\n await saveSchemaMetadata(db, tableName, snapshot);\n\n options.onMigrationProgress?.(`Database created successfully`, 1.0);\n return db;\n }\n\n // Ensure metadata store exists\n if (!db.objectStoreNames.contains(METADATA_STORE_NAME)) {\n const currentVersion = db.version;\n db.close();\n\n db = await openIndexedDbTable(\n tableName,\n currentVersion + 1,\n (event: IDBVersionChangeEvent) => {\n const db = (event.target as IDBOpenDBRequest).result;\n if (!db.objectStoreNames.contains(METADATA_STORE_NAME)) {\n db.createObjectStore(METADATA_STORE_NAME, { keyPath: \"tableName\" });\n }\n }\n );\n }\n\n // Load stored metadata\n const metadata = await loadSchemaMetadata(db, tableName);\n\n // Check if table structure matches expected\n if (!db.objectStoreNames.contains(tableName)) {\n // Object store doesn't exist, create it\n options.onMigrationProgress?.(`Object store ${tableName} does not exist, creating...`, 0);\n db.close();\n return await createNewDatabase(tableName, primaryKey, expectedIndexes, options);\n }\n\n // Compare schemas to determine what migration is needed\n const transaction = db.transaction(tableName, \"readonly\");\n const store = transaction.objectStore(tableName);\n const diff = compareSchemas(store, primaryKey, expectedIndexes);\n\n await new Promise<void>((resolve) => {\n transaction.oncomplete = () => resolve();\n transaction.onerror = () => resolve();\n });\n\n // Determine migration strategy\n const needsMigration =\n diff.indexesToAdd.length > 0 ||\n diff.indexesToRemove.length > 0 ||\n diff.indexesToModify.length > 0 ||\n diff.needsObjectStoreRecreation;\n\n if (!needsMigration) {\n // Schema matches, no migration needed\n options.onMigrationProgress?.(`Schema for ${tableName} is up to date`, 1.0);\n\n // Update metadata anyway to keep timestamp current\n const snapshot: SchemaSnapshot = {\n version: db.version,\n primaryKey,\n indexes: expectedIndexes,\n timestamp: Date.now(),\n };\n await saveSchemaMetadata(db, tableName, snapshot);\n\n return db;\n }\n\n // Perform appropriate migration\n if (diff.needsObjectStoreRecreation) {\n options.onMigrationProgress?.(\n `Schema change requires object store recreation for ${tableName}`,\n 0\n );\n db = await performDestructiveMigration(db, tableName, primaryKey, expectedIndexes, options);\n } else {\n options.onMigrationProgress?.(`Performing incremental migration for ${tableName}`, 0);\n db = await performIncrementalMigration(db, tableName, diff, options);\n }\n\n // Save updated metadata\n const snapshot: SchemaSnapshot = {\n version: db.version,\n primaryKey,\n indexes: expectedIndexes,\n timestamp: Date.now(),\n };\n await saveSchemaMetadata(db, tableName, snapshot);\n\n return db;\n } catch (err) {\n options.onMigrationWarning?.(`Migration failed for ${tableName}: ${err}`, err as Error);\n throw err;\n }\n}\n\n/**\n * Utility function to delete a database (for testing or cleanup)\n */\nexport async function dropIndexedDbTable(tableName: string): Promise<void> {\n return deleteIndexedDbTable(tableName);\n}\n",
16
18
  "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { createServiceToken, DataPortSchemaObject, FromSchema } from \"@workglow/util\";\nimport { ITabularRepository } from \"./ITabularRepository\";\nimport { InMemoryTabularRepository } from \"./InMemoryTabularRepository\";\nimport { TabularRepository } from \"./TabularRepository\";\n\nexport const SHARED_IN_MEMORY_TABULAR_REPOSITORY = createServiceToken<\n ITabularRepository<any, any, any, any, any>\n>(\"storage.tabularRepository.sharedInMemory\");\n\n/**\n * Message types for BroadcastChannel communication\n */\ntype BroadcastMessage =\n | { type: \"SYNC_REQUEST\" }\n | { type: \"SYNC_RESPONSE\"; data: any[] }\n | { type: \"PUT\"; entity: any }\n | { type: \"PUT_BULK\"; entities: any[] }\n | { type: \"DELETE\"; key: any }\n | { type: \"DELETE_ALL\" }\n | { type: \"DELETE_SEARCH\"; column: string; value: any; operator: string };\n\n/**\n * A tabular repository implementation that shares data across browser tabs/windows\n * using BroadcastChannel API. Uses InMemoryTabularRepository internally and\n * synchronizes changes across all instances.\n *\n * @template Schema - The schema definition for the entity using JSON Schema\n * @template PrimaryKeyNames - Array of property names that form the primary key\n */\nexport class SharedInMemoryTabularRepository<\n Schema extends DataPortSchemaObject,\n PrimaryKeyNames extends ReadonlyArray<keyof Schema[\"properties\"]>,\n // computed types\n Entity = FromSchema<Schema>,\n PrimaryKey = Pick<Entity, PrimaryKeyNames[number] & keyof Entity>,\n Value = Omit<Entity, PrimaryKeyNames[number] & keyof Entity>,\n> extends TabularRepository<Schema, PrimaryKeyNames, Entity, PrimaryKey, Value> {\n private channel: BroadcastChannel | null = null;\n private channelName: string;\n private inMemoryRepo: InMemoryTabularRepository<\n Schema,\n PrimaryKeyNames,\n Entity,\n PrimaryKey,\n Value\n >;\n private isInitialized = false;\n private syncInProgress = false;\n\n /**\n * Creates a new SharedInMemoryTabularRepository instance\n * @param channelName - Unique name for the BroadcastChannel (defaults to \"tabular_store\")\n * @param schema - Schema defining the structure of the entity\n * @param primaryKeyNames - Array of property names that form the primary key\n * @param indexes - Array of columns or column arrays to make searchable. Each string or single column creates a single-column index,\n * while each array creates a compound index with columns in the specified order.\n */\n constructor(\n channelName: string = \"tabular_store\",\n schema: Schema,\n primaryKeyNames: PrimaryKeyNames,\n indexes: Array<keyof Entity | Array<keyof Entity>> = []\n ) {\n super(schema, primaryKeyNames, indexes);\n this.channelName = channelName;\n this.inMemoryRepo = new InMemoryTabularRepository<\n Schema,\n PrimaryKeyNames,\n Entity,\n PrimaryKey,\n Value\n >(schema, primaryKeyNames, indexes);\n\n // Forward events from the in-memory repository\n this.setupEventForwarding();\n\n // Initialize BroadcastChannel if available\n this.initializeBroadcastChannel();\n }\n\n /**\n * Checks if BroadcastChannel is available in the current environment\n */\n private isBroadcastChannelAvailable(): boolean {\n return typeof BroadcastChannel !== \"undefined\";\n }\n\n /**\n * Initializes the BroadcastChannel and sets up message handlers\n */\n private initializeBroadcastChannel(): void {\n if (!this.isBroadcastChannelAvailable()) {\n console.warn(\"BroadcastChannel is not available. Tab synchronization will not work.\");\n return;\n }\n\n try {\n this.channel = new BroadcastChannel(this.channelName);\n this.channel.onmessage = (event: MessageEvent<BroadcastMessage>) => {\n this.handleBroadcastMessage(event.data);\n };\n\n // Request sync from other tabs on initialization\n this.syncFromOtherTabs();\n } catch (error) {\n console.error(\"Failed to initialize BroadcastChannel:\", error);\n }\n }\n\n /**\n * Sets up event forwarding from the internal InMemoryTabularRepository\n */\n private setupEventForwarding(): void {\n this.inMemoryRepo.on(\"put\", (entity) => {\n this.events.emit(\"put\", entity);\n });\n this.inMemoryRepo.on(\"get\", (key, entity) => {\n this.events.emit(\"get\", key, entity);\n });\n this.inMemoryRepo.on(\"search\", (key, entities) => {\n this.events.emit(\"search\", key, entities);\n });\n this.inMemoryRepo.on(\"delete\", (key) => {\n this.events.emit(\"delete\", key);\n });\n this.inMemoryRepo.on(\"clearall\", () => {\n this.events.emit(\"clearall\");\n });\n }\n\n /**\n * Handles incoming BroadcastChannel messages\n */\n private async handleBroadcastMessage(message: BroadcastMessage): Promise<void> {\n if (this.syncInProgress && message.type !== \"SYNC_RESPONSE\") {\n // Ignore messages during sync to avoid race conditions\n return;\n }\n\n switch (message.type) {\n case \"SYNC_REQUEST\":\n // Respond to sync request with current data\n const all = await this.inMemoryRepo.getAll();\n if (this.channel && all) {\n this.channel.postMessage({\n type: \"SYNC_RESPONSE\",\n data: all,\n } as BroadcastMessage);\n }\n break;\n\n case \"SYNC_RESPONSE\":\n // Copy data from the responding tab\n if (message.data && Array.isArray(message.data)) {\n await this.copyDataFromArray(message.data);\n }\n this.syncInProgress = false;\n break;\n\n case \"PUT\":\n // Apply put from another tab\n await this.inMemoryRepo.put(message.entity);\n break;\n\n case \"PUT_BULK\":\n // Apply bulk put from another tab\n await this.inMemoryRepo.putBulk(message.entities);\n break;\n\n case \"DELETE\":\n // Apply delete from another tab\n await this.inMemoryRepo.delete(message.key);\n break;\n\n case \"DELETE_ALL\":\n // Apply deleteAll from another tab\n await this.inMemoryRepo.deleteAll();\n break;\n\n case \"DELETE_SEARCH\":\n // Apply deleteSearch from another tab\n await this.inMemoryRepo.deleteSearch(\n message.column as keyof Entity,\n message.value,\n message.operator as \"=\" | \"<\" | \"<=\" | \">\" | \">=\"\n );\n break;\n }\n }\n\n /**\n * Requests synchronization from other tabs\n */\n private syncFromOtherTabs(): void {\n if (!this.channel) return;\n\n this.syncInProgress = true;\n this.channel.postMessage({ type: \"SYNC_REQUEST\" } as BroadcastMessage);\n\n // Set a timeout to stop waiting for sync response\n setTimeout(() => {\n this.syncInProgress = false;\n }, 1000);\n }\n\n /**\n * Copies data from an array of entities into the repository\n */\n private async copyDataFromArray(entities: Entity[]): Promise<void> {\n if (entities.length === 0) return;\n\n // Clear existing data\n await this.inMemoryRepo.deleteAll();\n\n // Bulk insert the new data\n await this.inMemoryRepo.putBulk(entities);\n }\n\n /**\n * Broadcasts a message to other tabs\n */\n private broadcast(message: BroadcastMessage): void {\n if (this.channel) {\n this.channel.postMessage(message);\n }\n }\n\n /**\n * Sets up the database for the repository (syncs from other tabs)\n */\n async setupDatabase(): Promise<void> {\n if (this.isInitialized) return;\n this.isInitialized = true;\n await this.syncFromOtherTabs();\n }\n\n /**\n * Stores a key-value pair in the repository\n * @param value - The combined object to store\n * @returns The stored entity\n * @emits 'put' event with the stored entity when successful\n */\n async put(value: Entity): Promise<Entity> {\n await this.setupDatabase();\n const result = await this.inMemoryRepo.put(value);\n this.broadcast({ type: \"PUT\", entity: value });\n return result;\n }\n\n /**\n * Stores multiple key-value pairs in the repository in a bulk operation\n * @param values - Array of combined objects to store\n * @returns Array of stored entities\n * @emits 'put' event for each value stored\n */\n async putBulk(values: Entity[]): Promise<Entity[]> {\n await this.setupDatabase();\n const result = await this.inMemoryRepo.putBulk(values);\n this.broadcast({ type: \"PUT_BULK\", entities: values });\n return result;\n }\n\n /**\n * Retrieves a value by its key\n * @param key - The primary key object to look up\n * @returns The value object if found, undefined otherwise\n * @emits 'get' event with the fingerprint ID and value when found\n */\n async get(key: PrimaryKey): Promise<Entity | undefined> {\n await this.setupDatabase();\n return await this.inMemoryRepo.get(key);\n }\n\n /**\n * Searches for entries matching a partial key\n * @param key - Partial key object to search for\n * @returns Array of matching combined objects\n * @throws Error if search criteria outside of searchable fields\n */\n async search(key: Partial<Entity>): Promise<Entity[] | undefined> {\n await this.setupDatabase();\n return await this.inMemoryRepo.search(key);\n }\n\n /**\n * Deletes an entry by its key\n * @param value - The primary key object or entity of the entry to delete\n * @emits 'delete' event with the fingerprint ID when successful\n */\n async delete(value: PrimaryKey | Entity): Promise<void> {\n await this.setupDatabase();\n await this.inMemoryRepo.delete(value);\n const { key } = this.separateKeyValueFromCombined(value as Entity);\n this.broadcast({ type: \"DELETE\", key });\n }\n\n /**\n * Removes all entries from the repository\n * @emits 'clearall' event when successful\n */\n async deleteAll(): Promise<void> {\n await this.setupDatabase();\n await this.inMemoryRepo.deleteAll();\n this.broadcast({ type: \"DELETE_ALL\" });\n }\n\n /**\n * Returns an array of all entries in the repository\n * @returns Array of all entries in the repository\n */\n async getAll(): Promise<Entity[] | undefined> {\n await this.setupDatabase();\n return await this.inMemoryRepo.getAll();\n }\n\n /**\n * Returns the number of entries in the repository\n * @returns The total count of stored entries\n */\n async size(): Promise<number> {\n await this.setupDatabase();\n return await this.inMemoryRepo.size();\n }\n\n /**\n * Deletes all entries with a date column value matching the provided criteria\n * @param column - The name of the date column to compare against\n * @param value - The value to compare against\n * @param operator - The operator to use for comparison\n */\n async deleteSearch(\n column: keyof Entity,\n value: Entity[keyof Entity],\n operator: \"=\" | \"<\" | \"<=\" | \">\" | \">=\" = \"=\"\n ): Promise<void> {\n await this.setupDatabase();\n await this.inMemoryRepo.deleteSearch(column, value, operator);\n this.broadcast({\n type: \"DELETE_SEARCH\",\n column: String(column),\n value,\n operator,\n });\n }\n\n /**\n * Cleanup method to close the BroadcastChannel\n */\n destroy(): void {\n if (this.channel) {\n this.channel.close();\n this.channel = null;\n }\n }\n}\n",
17
- "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type { SupabaseClient } from \"@supabase/supabase-js\";\nimport { createServiceToken, DataPortSchemaObject, FromSchema, JsonSchema } from \"@workglow/util\";\nimport { BaseSqlTabularRepository } from \"./BaseSqlTabularRepository\";\nimport { ITabularRepository, ValueOptionType } from \"./ITabularRepository\";\n\nexport const SUPABASE_TABULAR_REPOSITORY = createServiceToken<\n ITabularRepository<any, any, any, any, any>\n>(\"storage.tabularRepository.supabase\");\n\n/**\n * A Supabase-based tabular repository implementation that extends BaseSqlTabularRepository.\n * This class provides persistent storage for data in a Supabase database,\n * making it suitable for multi-user scenarios.\n *\n * @template Schema - The schema definition for the entity\n * @template PrimaryKeyNames - Array of property names that form the primary key\n */\nexport class SupabaseTabularRepository<\n Schema extends DataPortSchemaObject,\n PrimaryKeyNames extends ReadonlyArray<keyof Schema[\"properties\"]>,\n // computed types\n Entity = FromSchema<Schema>,\n PrimaryKey = Pick<Entity, PrimaryKeyNames[number] & keyof Entity>,\n Value = Omit<Entity, PrimaryKeyNames[number] & keyof Entity>,\n> extends BaseSqlTabularRepository<Schema, PrimaryKeyNames, Entity, PrimaryKey, Value> {\n private client: SupabaseClient;\n\n /**\n * Creates a new SupabaseTabularRepository instance.\n *\n * @param client - Supabase client instance\n * @param table - Name of the table to store data (defaults to \"tabular_store\")\n * @param schema - Schema defining the structure of the entity\n * @param primaryKeyNames - Array of property names that form the primary key\n * @param indexes - Array of columns or column arrays to make searchable. Each string or single column creates a single-column index,\n * while each array creates a compound index with columns in the specified order.\n */\n constructor(\n client: SupabaseClient,\n table: string = \"tabular_store\",\n schema: Schema,\n primaryKeyNames: PrimaryKeyNames,\n indexes: Array<keyof Entity | Array<keyof Entity>> = []\n ) {\n super(table, schema, primaryKeyNames, indexes);\n this.client = client;\n }\n\n protected isSetup = true;\n\n /**\n * Initializes the database table with the required schema.\n * Creates the table if it doesn't exist with primary key and value columns.\n */\n public async setupDatabase(): Promise<SupabaseClient> {\n if (this.isSetup) {\n return this.client;\n }\n const sql = `\n CREATE TABLE IF NOT EXISTS \"${this.table}\" (\n ${this.constructPrimaryKeyColumns('\"')} ${this.constructValueColumns('\"')},\n PRIMARY KEY (${this.primaryKeyColumnList()}) \n )\n `;\n const { error } = await this.client.rpc(\"exec_sql\", { query: sql });\n if (error && !error.message.includes(\"already exists\")) {\n throw error;\n }\n\n // Get primary key columns to avoid creating redundant indexes\n const pkColumns = this.primaryKeyColumns();\n\n // Track created indexes to avoid duplicates and redundant indexes\n const createdIndexes = new Set<string>();\n\n for (const columns of this.indexes) {\n // Skip if this is just the primary key or a prefix of it\n if (columns.length <= pkColumns.length) {\n // @ts-ignore\n const isPkPrefix = columns.every((col, idx) => col === pkColumns[idx]);\n if (isPkPrefix) continue;\n }\n\n // Create index name and column list\n const indexName = `${this.table}_${columns.join(\"_\")}`;\n const columnList = columns.map((col) => `\"${String(col)}\"`).join(\", \");\n\n // Skip if we've already created this index or if it's redundant\n const columnKey = columns.join(\",\");\n if (createdIndexes.has(columnKey)) continue;\n\n // Check if this index would be redundant with an existing one\n const isRedundant = Array.from(createdIndexes).some((existing) => {\n const existingCols = existing.split(\",\");\n return (\n existingCols.length >= columns.length &&\n columns.every((col, idx) => col === existingCols[idx])\n );\n });\n\n if (!isRedundant) {\n const indexSql = `CREATE INDEX IF NOT EXISTS \"${indexName}\" ON \"${this.table}\" (${columnList})`;\n const { error: indexError } = await this.client.rpc(\"exec_sql\", { query: indexSql });\n if (indexError && !indexError.message.includes(\"already exists\")) {\n // Index creation errors are not critical, log and continue\n console.warn(`Failed to create index ${indexName}:`, indexError);\n }\n createdIndexes.add(columnKey);\n }\n }\n this.isSetup = true;\n return this.client;\n }\n\n /**\n * Maps TypeScript/JavaScript types to corresponding PostgreSQL data types.\n * Uses additional schema information like minimum/maximum values, nullable status,\n * and string lengths to create more optimized column types.\n *\n * @param typeDef - The TypeScript/JavaScript type to map\n * @returns The corresponding PostgreSQL data type\n */\n protected mapTypeToSQL(typeDef: JsonSchema): string {\n // Extract the actual non-null type using base helper\n const actualType = this.getNonNullType(typeDef);\n if (typeof actualType === \"boolean\") {\n return \"TEXT /* boolean schema */\";\n }\n\n // Handle BLOB type\n if (actualType.contentEncoding === \"blob\") return \"BYTEA\";\n\n switch (actualType.type) {\n case \"string\":\n // Handle special string formats\n if (actualType.format === \"date-time\") return \"TIMESTAMP\";\n if (actualType.format === \"date\") return \"DATE\";\n if (actualType.format === \"email\") return \"VARCHAR(255)\";\n if (actualType.format === \"uri\") return \"VARCHAR(2048)\";\n if (actualType.format === \"uuid\") return \"UUID\";\n\n // Use a VARCHAR with maxLength if specified\n if (typeof actualType.maxLength === \"number\") {\n return `VARCHAR(${actualType.maxLength})`;\n }\n\n // Default to TEXT for strings without constraints\n return \"TEXT\";\n\n case \"number\":\n case \"integer\":\n // Handle integer vs floating point\n if (actualType.multipleOf === 1 || actualType.type === \"integer\") {\n // Use PostgreSQL's numeric range types based on min/max values\n if (typeof actualType.minimum === \"number\") {\n if (actualType.minimum >= 0) {\n // For unsigned integers\n if (typeof actualType.maximum === \"number\") {\n if (actualType.maximum <= 32767) return \"SMALLINT\";\n if (actualType.maximum <= 2147483647) return \"INTEGER\";\n }\n return \"BIGINT\";\n }\n }\n\n // Default integer type\n return \"INTEGER\";\n }\n\n // For floating point numbers with precision requirements\n if (actualType.format === \"float\") return \"REAL\";\n if (actualType.format === \"double\") return \"DOUBLE PRECISION\";\n\n // Use NUMERIC with precision/scale if specified\n if (typeof actualType.multipleOf === \"number\") {\n const decimalPlaces = String(actualType.multipleOf).split(\".\")[1]?.length || 0;\n if (decimalPlaces > 0) {\n return `NUMERIC(38, ${decimalPlaces})`;\n }\n }\n\n return \"NUMERIC\";\n\n case \"boolean\":\n return \"BOOLEAN\";\n\n case \"array\":\n // Handle array types (if items type is specified)\n if (\n actualType.items &&\n typeof actualType.items === \"object\" &&\n !Array.isArray(actualType.items)\n ) {\n const itemType = this.mapTypeToSQL(actualType.items as JsonSchema);\n\n // Only use native PostgreSQL arrays for simple scalar types\n // List of types that work well as native PostgreSQL arrays\n const supportedArrayElementTypes = [\n \"TEXT\",\n \"VARCHAR\",\n \"CHAR\",\n \"INTEGER\",\n \"SMALLINT\",\n \"BIGINT\",\n \"REAL\",\n \"DOUBLE PRECISION\",\n \"NUMERIC\",\n \"BOOLEAN\",\n \"UUID\",\n \"DATE\",\n \"TIMESTAMP\",\n ];\n\n // Check if the item type is in our supported list (either exact match or starts with for VARCHAR types)\n const isSupported = supportedArrayElementTypes.some(\n (type) => itemType === type || (itemType.startsWith(type + \"(\") && type !== \"VARCHAR\") // Handle things like VARCHAR(255)\n );\n\n if (isSupported) {\n return `${itemType}[]`;\n } else {\n return \"JSONB /* complex array */\";\n }\n }\n return \"JSONB /* generic array */\";\n\n case \"object\":\n return \"JSONB /* object */\";\n\n default:\n return \"TEXT /* unknown type */\";\n }\n }\n\n /**\n * Generates the SQL column definitions for primary key fields with constraints\n * @returns SQL string containing primary key column definitions\n */\n protected constructPrimaryKeyColumns($delimiter: string = \"\"): string {\n const cols = Object.entries<JsonSchema>(this.primaryKeySchema.properties)\n .map(([key, typeDef]) => {\n const sqlType = this.mapTypeToSQL(typeDef);\n let constraints = \"NOT NULL\";\n\n // Add CHECK constraint for unsigned numbers\n if (this.shouldBeUnsigned(typeDef)) {\n constraints += ` CHECK (${$delimiter}${key}${$delimiter} >= 0)`;\n }\n\n return `${$delimiter}${key}${$delimiter} ${sqlType} ${constraints}`;\n })\n .join(\", \");\n return cols;\n }\n\n /**\n * Generates the SQL column definitions for value fields with constraints\n * @returns SQL string containing value column definitions\n */\n protected constructValueColumns($delimiter: string = \"\"): string {\n const delimiter = $delimiter || '\"';\n const requiredSet = new Set(this.valueSchema.required ?? []);\n const cols = Object.entries<JsonSchema>(this.valueSchema.properties)\n .map(([key, typeDef]) => {\n const sqlType = this.mapTypeToSQL(typeDef);\n const isRequired = requiredSet.has(key);\n const nullable = !isRequired || this.isNullable(typeDef);\n let constraints = nullable ? \"NULL\" : \"NOT NULL\";\n\n // Add CHECK constraint for unsigned numbers\n if (this.shouldBeUnsigned(typeDef)) {\n constraints += ` CHECK (${delimiter}${key}${delimiter} >= 0)`;\n }\n\n return `${delimiter}${key}${delimiter} ${sqlType} ${constraints}`;\n })\n .join(\", \");\n if (cols.length > 0) {\n return `, ${cols}`;\n } else {\n return \"\";\n }\n }\n\n /**\n * Convert Supabase values to JS values. Ensures numeric strings become numbers where schema says number.\n */\n protected sqlToJsValue(column: string, value: ValueOptionType): Entity[keyof Entity] {\n const typeDef = this.schema.properties[column as keyof typeof this.schema.properties] as\n | JsonSchema\n | undefined;\n if (typeDef) {\n if (value === null && this.isNullable(typeDef)) {\n return null as any;\n }\n const actualType = this.getNonNullType(typeDef);\n\n // Handle numeric types - Supabase can return them as strings\n if (\n typeof actualType !== \"boolean\" &&\n (actualType.type === \"number\" || actualType.type === \"integer\")\n ) {\n const v: any = value;\n if (typeof v === \"number\") return v as any;\n if (typeof v === \"string\") {\n const parsed = Number(v);\n if (!isNaN(parsed)) return parsed as any;\n }\n }\n }\n return super.sqlToJsValue(column, value);\n }\n\n /**\n * Determines if a field should be treated as unsigned based on schema properties\n * @param typeDef - The schema type definition\n * @returns true if the field should be treated as unsigned\n */\n protected shouldBeUnsigned(typeDef: JsonSchema): boolean {\n // Extract the non-null type using the base class helper\n const actualType = this.getNonNullType(typeDef);\n if (typeof actualType === \"boolean\") {\n return false;\n }\n\n // Check if it's a number type with minimum >= 0\n if (\n (actualType.type === \"number\" || actualType.type === \"integer\") &&\n typeof actualType.minimum === \"number\" &&\n actualType.minimum >= 0\n ) {\n return true;\n }\n\n return false;\n }\n\n /**\n * Stores or updates a row in the database.\n * Uses UPSERT (INSERT ... ON CONFLICT DO UPDATE) for atomic operations.\n *\n * @param entity - The entity to store\n * @returns The entity with any server-generated fields updated\n * @emits \"put\" event with the updated entity when successful\n */\n async put(entity: Entity): Promise<Entity> {\n await this.setupDatabase();\n // Normalize optional fields: convert undefined to null for optional fields\n const normalizedEntity = { ...entity } as any;\n const requiredSet = new Set(this.valueSchema.required ?? []);\n for (const key in this.valueSchema.properties) {\n if (!(key in normalizedEntity) || normalizedEntity[key] === undefined) {\n if (!requiredSet.has(key)) {\n normalizedEntity[key] = null;\n }\n }\n }\n const { data, error } = await this.client\n .from(this.table)\n .upsert(normalizedEntity, { onConflict: this.primaryKeyColumnList() })\n .select()\n .single();\n\n if (error) throw error;\n const updatedEntity = data as Entity;\n\n // Convert all columns from SQL to JS values\n for (const key in this.schema.properties) {\n // @ts-ignore\n updatedEntity[key] = this.sqlToJsValue(key, updatedEntity[key]);\n }\n\n this.events.emit(\"put\", updatedEntity);\n return updatedEntity;\n }\n\n /**\n * Stores multiple rows in the database in a bulk operation.\n * Uses batch INSERT with ON CONFLICT for better performance.\n *\n * @param entities - Array of entities to store\n * @returns Array of entities with any server-generated fields updated\n * @emits \"put\" event for each entity stored\n */\n async putBulk(entities: Entity[]): Promise<Entity[]> {\n if (entities.length === 0) return [];\n\n await this.setupDatabase();\n // Normalize optional fields: convert undefined to null for optional fields\n const requiredSet = new Set(this.valueSchema.required ?? []);\n const normalizedEntities = entities.map((entity) => {\n const normalized = { ...entity } as any;\n for (const key in this.valueSchema.properties) {\n if (!(key in normalized) || normalized[key] === undefined) {\n if (!requiredSet.has(key)) {\n normalized[key] = null;\n }\n }\n }\n return normalized;\n });\n const { data, error } = await this.client\n .from(this.table)\n .upsert(normalizedEntities, { onConflict: this.primaryKeyColumnList() })\n .select();\n\n if (error) throw error;\n const updatedEntities = data as Entity[];\n\n // Convert all columns from SQL to JS values and emit events\n for (const entity of updatedEntities) {\n for (const key in this.schema.properties) {\n // @ts-ignore\n entity[key] = this.sqlToJsValue(key, entity[key]);\n }\n this.events.emit(\"put\", entity);\n }\n\n return updatedEntities;\n }\n\n /**\n * Retrieves a value from the database by its primary key.\n *\n * @param key - The primary key object to look up\n * @returns The stored entity or undefined if not found\n * @emits \"get\" event with the key when successful\n */\n async get(key: PrimaryKey): Promise<Entity | undefined> {\n await this.setupDatabase();\n\n let query = this.client.from(this.table).select(\"*\");\n\n // Build the where clause from primary key\n for (const pkName of this.primaryKeyNames) {\n query = query.eq(String(pkName), (key as any)[pkName]);\n }\n\n const { data, error } = await query.single();\n\n if (error) {\n if (error.code === \"PGRST116\") {\n // Not found\n this.events.emit(\"get\", key, undefined);\n return undefined;\n }\n throw error;\n }\n\n const val = data as Entity | undefined;\n if (val) {\n // Convert all columns from SQL to JS values\n for (const key in this.schema.properties) {\n // @ts-ignore\n val[key] = this.sqlToJsValue(key, val[key]);\n }\n }\n this.events.emit(\"get\", key, val);\n return val;\n }\n\n /**\n * Method to search for rows based on a partial key.\n *\n * @param searchCriteria - Partial entity to search for\n * @returns Promise resolving to an array of entities or undefined if not found\n */\n public async search(searchCriteria: Partial<Entity>): Promise<Entity[] | undefined> {\n await this.setupDatabase();\n const searchKeys = Object.keys(searchCriteria);\n if (searchKeys.length === 0) {\n return undefined;\n }\n\n // Find the best matching index for the search\n const bestIndex = this.findBestMatchingIndex(searchKeys as Array<keyof Entity>);\n if (!bestIndex) {\n throw new Error(\n `No suitable index found for the search criteria, searching for ['${searchKeys.join(\n \"', '\"\n )}'] with pk ['${this.primaryKeyNames.join(\"', '\")}'] and indexes ['${this.indexes.join(\n \"', '\"\n )}']`\n );\n }\n\n // Verify columns in primary key or value schema\n const validColumns = [...this.primaryKeyColumns(), ...this.valueColumns()];\n // @ts-expect-error\n const invalidColumns = searchKeys.filter((key) => !validColumns.includes(key));\n if (invalidColumns.length > 0) {\n throw new Error(`Invalid columns in search criteria: ${invalidColumns.join(\", \")}`);\n }\n\n let query = this.client.from(this.table).select(\"*\");\n\n // Build the where clause from search criteria\n for (const [key, value] of Object.entries(searchCriteria)) {\n query = query.eq(key, value);\n }\n\n const { data, error } = await query;\n\n if (error) throw error;\n\n if (data && data.length > 0) {\n // Convert all columns from SQL to JS values\n for (const row of data) {\n for (const key in this.schema.properties) {\n // @ts-ignore\n row[key] = this.sqlToJsValue(key, row[key]);\n }\n }\n this.events.emit(\"search\", searchCriteria, data as Entity[]);\n return data as Entity[];\n } else {\n this.events.emit(\"search\", searchCriteria, undefined);\n return undefined;\n }\n }\n\n /**\n * Deletes a row from the database.\n *\n * @param value - The primary key object or entity to delete\n * @emits \"delete\" event with the key when successful\n */\n async delete(value: PrimaryKey | Entity): Promise<void> {\n await this.setupDatabase();\n const { key } = this.separateKeyValueFromCombined(value as Entity);\n\n let query = this.client.from(this.table).delete();\n\n // Build the where clause from primary key\n for (const pkName of this.primaryKeyNames) {\n query = query.eq(String(pkName), (key as any)[pkName]);\n }\n\n const { error } = await query;\n\n if (error) throw error;\n this.events.emit(\"delete\", key as keyof Entity);\n }\n\n /**\n * Retrieves all entries from the database table\n * @returns Promise resolving to an array of entries or undefined if not found\n */\n async getAll(): Promise<Entity[] | undefined> {\n await this.setupDatabase();\n const { data, error } = await this.client.from(this.table).select(\"*\");\n\n if (error) throw error;\n\n if (data && data.length) {\n // Convert all columns from SQL to JS values\n for (const row of data) {\n for (const key in this.schema.properties) {\n // @ts-ignore\n row[key] = this.sqlToJsValue(key, row[key]);\n }\n }\n return data as Entity[];\n }\n return undefined;\n }\n\n /**\n * Deletes all rows from the database table.\n * @emits \"clearall\" event when successful\n */\n async deleteAll(): Promise<void> {\n await this.setupDatabase();\n\n // Use the first primary key column for the delete condition\n const firstPkColumn = this.primaryKeyNames[0];\n const { error } = await this.client.from(this.table).delete().neq(String(firstPkColumn), null); // Delete all rows by using a condition that's always true\n\n if (error) throw error;\n this.events.emit(\"clearall\");\n }\n\n /**\n * Returns the total number of rows in the database.\n *\n * @returns Promise resolving to the count of stored items\n */\n async size(): Promise<number> {\n await this.setupDatabase();\n const { count, error } = await this.client\n .from(this.table)\n .select(\"*\", { count: \"exact\", head: true });\n\n if (error) throw error;\n return count ?? 0;\n }\n\n protected generateWhereClause(\n column: keyof Entity,\n operator: \"=\" | \"<\" | \"<=\" | \">\" | \">=\" = \"=\"\n ): string {\n if (!(column in this.schema.properties)) {\n throw new Error(`Schema must have a ${String(column)} field to use deleteSearch`);\n }\n return `${String(column)} ${operator} $1`;\n }\n\n /**\n * Deletes all entries matching a search criteria\n * @param column - The column name to compare against\n * @param value - The value to compare against\n * @param operator - The operator to use for comparison\n */\n async deleteSearch(\n column: keyof Entity,\n value: Entity[keyof Entity],\n operator: \"=\" | \"<\" | \"<=\" | \">\" | \">=\" = \"=\"\n ): Promise<void> {\n await this.setupDatabase();\n\n let query = this.client.from(this.table).delete();\n\n switch (operator) {\n case \"=\":\n query = query.eq(String(column), value);\n break;\n case \"<\":\n query = query.lt(String(column), value);\n break;\n case \"<=\":\n query = query.lte(String(column), value);\n break;\n case \">\":\n query = query.gt(String(column), value);\n break;\n case \">=\":\n query = query.gte(String(column), value);\n break;\n }\n\n const { error } = await query;\n\n if (error) throw error;\n this.events.emit(\"delete\", column as keyof Entity);\n }\n}\n",
19
+ "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type { RealtimeChannel, SupabaseClient } from \"@supabase/supabase-js\";\nimport { createServiceToken, DataPortSchemaObject, FromSchema, JsonSchema } from \"@workglow/util\";\nimport { BaseSqlTabularRepository } from \"./BaseSqlTabularRepository\";\nimport {\n ITabularRepository,\n TabularChangePayload,\n TabularChangeType,\n ValueOptionType,\n} from \"./ITabularRepository\";\n\nexport const SUPABASE_TABULAR_REPOSITORY = createServiceToken<\n ITabularRepository<any, any, any, any, any>\n>(\"storage.tabularRepository.supabase\");\n\n/**\n * A Supabase-based tabular repository implementation that extends BaseSqlTabularRepository.\n * This class provides persistent storage for data in a Supabase database,\n * making it suitable for multi-user scenarios.\n *\n * @template Schema - The schema definition for the entity\n * @template PrimaryKeyNames - Array of property names that form the primary key\n */\nexport class SupabaseTabularRepository<\n Schema extends DataPortSchemaObject,\n PrimaryKeyNames extends ReadonlyArray<keyof Schema[\"properties\"]>,\n // computed types\n Entity = FromSchema<Schema>,\n PrimaryKey = Pick<Entity, PrimaryKeyNames[number] & keyof Entity>,\n Value = Omit<Entity, PrimaryKeyNames[number] & keyof Entity>,\n> extends BaseSqlTabularRepository<Schema, PrimaryKeyNames, Entity, PrimaryKey, Value> {\n private client: SupabaseClient;\n private realtimeChannel: RealtimeChannel | null = null;\n\n /**\n * Creates a new SupabaseTabularRepository instance.\n *\n * @param client - Supabase client instance\n * @param table - Name of the table to store data (defaults to \"tabular_store\")\n * @param schema - Schema defining the structure of the entity\n * @param primaryKeyNames - Array of property names that form the primary key\n * @param indexes - Array of columns or column arrays to make searchable. Each string or single column creates a single-column index,\n * while each array creates a compound index with columns in the specified order.\n */\n constructor(\n client: SupabaseClient,\n table: string = \"tabular_store\",\n schema: Schema,\n primaryKeyNames: PrimaryKeyNames,\n indexes: Array<keyof Entity | Array<keyof Entity>> = []\n ) {\n super(table, schema, primaryKeyNames, indexes);\n this.client = client;\n }\n\n protected isSetup = true;\n\n /**\n * Initializes the database table with the required schema.\n * Creates the table if it doesn't exist with primary key and value columns.\n */\n public async setupDatabase(): Promise<SupabaseClient> {\n if (this.isSetup) {\n return this.client;\n }\n const sql = `\n CREATE TABLE IF NOT EXISTS \"${this.table}\" (\n ${this.constructPrimaryKeyColumns('\"')} ${this.constructValueColumns('\"')},\n PRIMARY KEY (${this.primaryKeyColumnList()}) \n )\n `;\n const { error } = await this.client.rpc(\"exec_sql\", { query: sql });\n if (error && !error.message.includes(\"already exists\")) {\n throw error;\n }\n\n // Get primary key columns to avoid creating redundant indexes\n const pkColumns = this.primaryKeyColumns();\n\n // Track created indexes to avoid duplicates and redundant indexes\n const createdIndexes = new Set<string>();\n\n for (const columns of this.indexes) {\n // Skip if this is just the primary key or a prefix of it\n if (columns.length <= pkColumns.length) {\n // @ts-ignore\n const isPkPrefix = columns.every((col, idx) => col === pkColumns[idx]);\n if (isPkPrefix) continue;\n }\n\n // Create index name and column list\n const indexName = `${this.table}_${columns.join(\"_\")}`;\n const columnList = columns.map((col) => `\"${String(col)}\"`).join(\", \");\n\n // Skip if we've already created this index or if it's redundant\n const columnKey = columns.join(\",\");\n if (createdIndexes.has(columnKey)) continue;\n\n // Check if this index would be redundant with an existing one\n const isRedundant = Array.from(createdIndexes).some((existing) => {\n const existingCols = existing.split(\",\");\n return (\n existingCols.length >= columns.length &&\n columns.every((col, idx) => col === existingCols[idx])\n );\n });\n\n if (!isRedundant) {\n const indexSql = `CREATE INDEX IF NOT EXISTS \"${indexName}\" ON \"${this.table}\" (${columnList})`;\n const { error: indexError } = await this.client.rpc(\"exec_sql\", { query: indexSql });\n if (indexError && !indexError.message.includes(\"already exists\")) {\n // Index creation errors are not critical, log and continue\n console.warn(`Failed to create index ${indexName}:`, indexError);\n }\n createdIndexes.add(columnKey);\n }\n }\n this.isSetup = true;\n return this.client;\n }\n\n /**\n * Maps TypeScript/JavaScript types to corresponding PostgreSQL data types.\n * Uses additional schema information like minimum/maximum values, nullable status,\n * and string lengths to create more optimized column types.\n *\n * @param typeDef - The TypeScript/JavaScript type to map\n * @returns The corresponding PostgreSQL data type\n */\n protected mapTypeToSQL(typeDef: JsonSchema): string {\n // Extract the actual non-null type using base helper\n const actualType = this.getNonNullType(typeDef);\n if (typeof actualType === \"boolean\") {\n return \"TEXT /* boolean schema */\";\n }\n\n // Handle BLOB type\n if (actualType.contentEncoding === \"blob\") return \"BYTEA\";\n\n switch (actualType.type) {\n case \"string\":\n // Handle special string formats\n if (actualType.format === \"date-time\") return \"TIMESTAMP\";\n if (actualType.format === \"date\") return \"DATE\";\n if (actualType.format === \"email\") return \"VARCHAR(255)\";\n if (actualType.format === \"uri\") return \"VARCHAR(2048)\";\n if (actualType.format === \"uuid\") return \"UUID\";\n\n // Use a VARCHAR with maxLength if specified\n if (typeof actualType.maxLength === \"number\") {\n return `VARCHAR(${actualType.maxLength})`;\n }\n\n // Default to TEXT for strings without constraints\n return \"TEXT\";\n\n case \"number\":\n case \"integer\":\n // Handle integer vs floating point\n if (actualType.multipleOf === 1 || actualType.type === \"integer\") {\n // Use PostgreSQL's numeric range types based on min/max values\n if (typeof actualType.minimum === \"number\") {\n if (actualType.minimum >= 0) {\n // For unsigned integers\n if (typeof actualType.maximum === \"number\") {\n if (actualType.maximum <= 32767) return \"SMALLINT\";\n if (actualType.maximum <= 2147483647) return \"INTEGER\";\n }\n return \"BIGINT\";\n }\n }\n\n // Default integer type\n return \"INTEGER\";\n }\n\n // For floating point numbers with precision requirements\n if (actualType.format === \"float\") return \"REAL\";\n if (actualType.format === \"double\") return \"DOUBLE PRECISION\";\n\n // Use NUMERIC with precision/scale if specified\n if (typeof actualType.multipleOf === \"number\") {\n const decimalPlaces = String(actualType.multipleOf).split(\".\")[1]?.length || 0;\n if (decimalPlaces > 0) {\n return `NUMERIC(38, ${decimalPlaces})`;\n }\n }\n\n return \"NUMERIC\";\n\n case \"boolean\":\n return \"BOOLEAN\";\n\n case \"array\":\n // Handle array types (if items type is specified)\n if (\n actualType.items &&\n typeof actualType.items === \"object\" &&\n !Array.isArray(actualType.items)\n ) {\n const itemType = this.mapTypeToSQL(actualType.items as JsonSchema);\n\n // Only use native PostgreSQL arrays for simple scalar types\n // List of types that work well as native PostgreSQL arrays\n const supportedArrayElementTypes = [\n \"TEXT\",\n \"VARCHAR\",\n \"CHAR\",\n \"INTEGER\",\n \"SMALLINT\",\n \"BIGINT\",\n \"REAL\",\n \"DOUBLE PRECISION\",\n \"NUMERIC\",\n \"BOOLEAN\",\n \"UUID\",\n \"DATE\",\n \"TIMESTAMP\",\n ];\n\n // Check if the item type is in our supported list (either exact match or starts with for VARCHAR types)\n const isSupported = supportedArrayElementTypes.some(\n (type) => itemType === type || (itemType.startsWith(type + \"(\") && type !== \"VARCHAR\") // Handle things like VARCHAR(255)\n );\n\n if (isSupported) {\n return `${itemType}[]`;\n } else {\n return \"JSONB /* complex array */\";\n }\n }\n return \"JSONB /* generic array */\";\n\n case \"object\":\n return \"JSONB /* object */\";\n\n default:\n return \"TEXT /* unknown type */\";\n }\n }\n\n /**\n * Generates the SQL column definitions for primary key fields with constraints\n * @returns SQL string containing primary key column definitions\n */\n protected constructPrimaryKeyColumns($delimiter: string = \"\"): string {\n const cols = Object.entries<JsonSchema>(this.primaryKeySchema.properties)\n .map(([key, typeDef]) => {\n const sqlType = this.mapTypeToSQL(typeDef);\n let constraints = \"NOT NULL\";\n\n // Add CHECK constraint for unsigned numbers\n if (this.shouldBeUnsigned(typeDef)) {\n constraints += ` CHECK (${$delimiter}${key}${$delimiter} >= 0)`;\n }\n\n return `${$delimiter}${key}${$delimiter} ${sqlType} ${constraints}`;\n })\n .join(\", \");\n return cols;\n }\n\n /**\n * Generates the SQL column definitions for value fields with constraints\n * @returns SQL string containing value column definitions\n */\n protected constructValueColumns($delimiter: string = \"\"): string {\n const delimiter = $delimiter || '\"';\n const requiredSet = new Set(this.valueSchema.required ?? []);\n const cols = Object.entries<JsonSchema>(this.valueSchema.properties)\n .map(([key, typeDef]) => {\n const sqlType = this.mapTypeToSQL(typeDef);\n const isRequired = requiredSet.has(key);\n const nullable = !isRequired || this.isNullable(typeDef);\n let constraints = nullable ? \"NULL\" : \"NOT NULL\";\n\n // Add CHECK constraint for unsigned numbers\n if (this.shouldBeUnsigned(typeDef)) {\n constraints += ` CHECK (${delimiter}${key}${delimiter} >= 0)`;\n }\n\n return `${delimiter}${key}${delimiter} ${sqlType} ${constraints}`;\n })\n .join(\", \");\n if (cols.length > 0) {\n return `, ${cols}`;\n } else {\n return \"\";\n }\n }\n\n /**\n * Convert Supabase values to JS values. Ensures numeric strings become numbers where schema says number.\n */\n protected sqlToJsValue(column: string, value: ValueOptionType): Entity[keyof Entity] {\n const typeDef = this.schema.properties[column as keyof typeof this.schema.properties] as\n | JsonSchema\n | undefined;\n if (typeDef) {\n if (value === null && this.isNullable(typeDef)) {\n return null as any;\n }\n const actualType = this.getNonNullType(typeDef);\n\n // Handle numeric types - Supabase can return them as strings\n if (\n typeof actualType !== \"boolean\" &&\n (actualType.type === \"number\" || actualType.type === \"integer\")\n ) {\n const v: any = value;\n if (typeof v === \"number\") return v as any;\n if (typeof v === \"string\") {\n const parsed = Number(v);\n if (!isNaN(parsed)) return parsed as any;\n }\n }\n }\n return super.sqlToJsValue(column, value);\n }\n\n /**\n * Determines if a field should be treated as unsigned based on schema properties\n * @param typeDef - The schema type definition\n * @returns true if the field should be treated as unsigned\n */\n protected shouldBeUnsigned(typeDef: JsonSchema): boolean {\n // Extract the non-null type using the base class helper\n const actualType = this.getNonNullType(typeDef);\n if (typeof actualType === \"boolean\") {\n return false;\n }\n\n // Check if it's a number type with minimum >= 0\n if (\n (actualType.type === \"number\" || actualType.type === \"integer\") &&\n typeof actualType.minimum === \"number\" &&\n actualType.minimum >= 0\n ) {\n return true;\n }\n\n return false;\n }\n\n /**\n * Stores or updates a row in the database.\n * Uses UPSERT (INSERT ... ON CONFLICT DO UPDATE) for atomic operations.\n *\n * @param entity - The entity to store\n * @returns The entity with any server-generated fields updated\n * @emits \"put\" event with the updated entity when successful\n */\n async put(entity: Entity): Promise<Entity> {\n await this.setupDatabase();\n // Normalize optional fields: convert undefined to null for optional fields\n const normalizedEntity = { ...entity } as any;\n const requiredSet = new Set(this.valueSchema.required ?? []);\n for (const key in this.valueSchema.properties) {\n if (!(key in normalizedEntity) || normalizedEntity[key] === undefined) {\n if (!requiredSet.has(key)) {\n normalizedEntity[key] = null;\n }\n }\n }\n const { data, error } = await this.client\n .from(this.table)\n .upsert(normalizedEntity, { onConflict: this.primaryKeyColumnList() })\n .select()\n .single();\n\n if (error) throw error;\n const updatedEntity = data as Entity;\n\n // Convert all columns from SQL to JS values\n for (const key in this.schema.properties) {\n // @ts-ignore\n updatedEntity[key] = this.sqlToJsValue(key, updatedEntity[key]);\n }\n\n this.events.emit(\"put\", updatedEntity);\n return updatedEntity;\n }\n\n /**\n * Stores multiple rows in the database in a bulk operation.\n * Uses batch INSERT with ON CONFLICT for better performance.\n *\n * @param entities - Array of entities to store\n * @returns Array of entities with any server-generated fields updated\n * @emits \"put\" event for each entity stored\n */\n async putBulk(entities: Entity[]): Promise<Entity[]> {\n if (entities.length === 0) return [];\n\n await this.setupDatabase();\n // Normalize optional fields: convert undefined to null for optional fields\n const requiredSet = new Set(this.valueSchema.required ?? []);\n const normalizedEntities = entities.map((entity) => {\n const normalized = { ...entity } as any;\n for (const key in this.valueSchema.properties) {\n if (!(key in normalized) || normalized[key] === undefined) {\n if (!requiredSet.has(key)) {\n normalized[key] = null;\n }\n }\n }\n return normalized;\n });\n const { data, error } = await this.client\n .from(this.table)\n .upsert(normalizedEntities, { onConflict: this.primaryKeyColumnList() })\n .select();\n\n if (error) throw error;\n const updatedEntities = data as Entity[];\n\n // Convert all columns from SQL to JS values and emit events\n for (const entity of updatedEntities) {\n for (const key in this.schema.properties) {\n // @ts-ignore\n entity[key] = this.sqlToJsValue(key, entity[key]);\n }\n this.events.emit(\"put\", entity);\n }\n\n return updatedEntities;\n }\n\n /**\n * Retrieves a value from the database by its primary key.\n *\n * @param key - The primary key object to look up\n * @returns The stored entity or undefined if not found\n * @emits \"get\" event with the key when successful\n */\n async get(key: PrimaryKey): Promise<Entity | undefined> {\n await this.setupDatabase();\n\n let query = this.client.from(this.table).select(\"*\");\n\n // Build the where clause from primary key\n for (const pkName of this.primaryKeyNames) {\n query = query.eq(String(pkName), (key as any)[pkName]);\n }\n\n const { data, error } = await query.single();\n\n if (error) {\n if (error.code === \"PGRST116\") {\n // Not found\n this.events.emit(\"get\", key, undefined);\n return undefined;\n }\n throw error;\n }\n\n const val = data as Entity | undefined;\n if (val) {\n // Convert all columns from SQL to JS values\n for (const key in this.schema.properties) {\n // @ts-ignore\n val[key] = this.sqlToJsValue(key, val[key]);\n }\n }\n this.events.emit(\"get\", key, val);\n return val;\n }\n\n /**\n * Method to search for rows based on a partial key.\n *\n * @param searchCriteria - Partial entity to search for\n * @returns Promise resolving to an array of entities or undefined if not found\n */\n public async search(searchCriteria: Partial<Entity>): Promise<Entity[] | undefined> {\n await this.setupDatabase();\n const searchKeys = Object.keys(searchCriteria);\n if (searchKeys.length === 0) {\n return undefined;\n }\n\n // Find the best matching index for the search\n const bestIndex = this.findBestMatchingIndex(searchKeys as Array<keyof Entity>);\n if (!bestIndex) {\n throw new Error(\n `No suitable index found for the search criteria, searching for ['${searchKeys.join(\n \"', '\"\n )}'] with pk ['${this.primaryKeyNames.join(\"', '\")}'] and indexes ['${this.indexes.join(\n \"', '\"\n )}']`\n );\n }\n\n // Verify columns in primary key or value schema\n const validColumns = [...this.primaryKeyColumns(), ...this.valueColumns()];\n // @ts-expect-error\n const invalidColumns = searchKeys.filter((key) => !validColumns.includes(key));\n if (invalidColumns.length > 0) {\n throw new Error(`Invalid columns in search criteria: ${invalidColumns.join(\", \")}`);\n }\n\n let query = this.client.from(this.table).select(\"*\");\n\n // Build the where clause from search criteria\n for (const [key, value] of Object.entries(searchCriteria)) {\n query = query.eq(key, value);\n }\n\n const { data, error } = await query;\n\n if (error) throw error;\n\n if (data && data.length > 0) {\n // Convert all columns from SQL to JS values\n for (const row of data) {\n for (const key in this.schema.properties) {\n // @ts-ignore\n row[key] = this.sqlToJsValue(key, row[key]);\n }\n }\n this.events.emit(\"search\", searchCriteria, data as Entity[]);\n return data as Entity[];\n } else {\n this.events.emit(\"search\", searchCriteria, undefined);\n return undefined;\n }\n }\n\n /**\n * Deletes a row from the database.\n *\n * @param value - The primary key object or entity to delete\n * @emits \"delete\" event with the key when successful\n */\n async delete(value: PrimaryKey | Entity): Promise<void> {\n await this.setupDatabase();\n const { key } = this.separateKeyValueFromCombined(value as Entity);\n\n let query = this.client.from(this.table).delete();\n\n // Build the where clause from primary key\n for (const pkName of this.primaryKeyNames) {\n query = query.eq(String(pkName), (key as any)[pkName]);\n }\n\n const { error } = await query;\n\n if (error) throw error;\n this.events.emit(\"delete\", key as keyof Entity);\n }\n\n /**\n * Retrieves all entries from the database table\n * @returns Promise resolving to an array of entries or undefined if not found\n */\n async getAll(): Promise<Entity[] | undefined> {\n await this.setupDatabase();\n const { data, error } = await this.client.from(this.table).select(\"*\");\n\n if (error) throw error;\n\n if (data && data.length) {\n // Convert all columns from SQL to JS values\n for (const row of data) {\n for (const key in this.schema.properties) {\n // @ts-ignore\n row[key] = this.sqlToJsValue(key, row[key]);\n }\n }\n return data as Entity[];\n }\n return undefined;\n }\n\n /**\n * Deletes all rows from the database table.\n * @emits \"clearall\" event when successful\n */\n async deleteAll(): Promise<void> {\n await this.setupDatabase();\n\n // Use the first primary key column for the delete condition\n const firstPkColumn = this.primaryKeyNames[0];\n const { error } = await this.client.from(this.table).delete().neq(String(firstPkColumn), null); // Delete all rows by using a condition that's always true\n\n if (error) throw error;\n this.events.emit(\"clearall\");\n }\n\n /**\n * Returns the total number of rows in the database.\n *\n * @returns Promise resolving to the count of stored items\n */\n async size(): Promise<number> {\n await this.setupDatabase();\n const { count, error } = await this.client\n .from(this.table)\n .select(\"*\", { count: \"exact\", head: true });\n\n if (error) throw error;\n return count ?? 0;\n }\n\n protected generateWhereClause(\n column: keyof Entity,\n operator: \"=\" | \"<\" | \"<=\" | \">\" | \">=\" = \"=\"\n ): string {\n if (!(column in this.schema.properties)) {\n throw new Error(`Schema must have a ${String(column)} field to use deleteSearch`);\n }\n return `${String(column)} ${operator} $1`;\n }\n\n /**\n * Deletes all entries matching a search criteria\n * @param column - The column name to compare against\n * @param value - The value to compare against\n * @param operator - The operator to use for comparison\n */\n async deleteSearch(\n column: keyof Entity,\n value: Entity[keyof Entity],\n operator: \"=\" | \"<\" | \"<=\" | \">\" | \">=\" = \"=\"\n ): Promise<void> {\n await this.setupDatabase();\n\n let query = this.client.from(this.table).delete();\n\n switch (operator) {\n case \"=\":\n query = query.eq(String(column), value);\n break;\n case \"<\":\n query = query.lt(String(column), value);\n break;\n case \"<=\":\n query = query.lte(String(column), value);\n break;\n case \">\":\n query = query.gt(String(column), value);\n break;\n case \">=\":\n query = query.gte(String(column), value);\n break;\n }\n\n const { error } = await query;\n\n if (error) throw error;\n this.events.emit(\"delete\", column as keyof Entity);\n }\n\n /**\n * Converts a row from Supabase realtime payload to an Entity with proper type conversions.\n *\n * @param row - The raw row data from Supabase realtime\n * @returns The converted entity\n */\n private convertRealtimeRow(row: Record<string, unknown>): Entity {\n const entity = { ...row } as Entity;\n for (const key in this.schema.properties) {\n // @ts-ignore\n entity[key] = this.sqlToJsValue(key, row[key] as ValueOptionType);\n }\n return entity;\n }\n\n /**\n * Subscribes to changes in the repository using Supabase realtime.\n * Receives notifications for INSERT, UPDATE, and DELETE operations from any source.\n *\n * @param callback - Function called when a change occurs\n * @returns Unsubscribe function\n */\n subscribeToChanges(callback: (change: TabularChangePayload<Entity>) => void): () => void {\n // Create a unique channel name\n const channelName = `tabular-${this.table}-${Date.now()}`;\n\n this.realtimeChannel = this.client\n .channel(channelName)\n .on(\n \"postgres_changes\",\n {\n event: \"*\",\n schema: \"public\",\n table: this.table,\n },\n (payload) => {\n const change: TabularChangePayload<Entity> = {\n type: payload.eventType.toUpperCase() as TabularChangeType,\n old:\n payload.old && Object.keys(payload.old).length > 0\n ? this.convertRealtimeRow(payload.old)\n : undefined,\n new:\n payload.new && Object.keys(payload.new).length > 0\n ? this.convertRealtimeRow(payload.new)\n : undefined,\n };\n callback(change);\n }\n )\n .subscribe();\n\n return () => {\n if (this.realtimeChannel) {\n this.client.removeChannel(this.realtimeChannel);\n this.realtimeChannel = null;\n }\n };\n }\n\n /**\n * Destroys the repository and frees up resources.\n */\n destroy(): void {\n if (this.realtimeChannel) {\n this.client.removeChannel(this.realtimeChannel);\n this.realtimeChannel = null;\n }\n }\n}\n",
18
20
  "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { DataPortSchemaObject, FromSchema, JsonSchema } from \"@workglow/util\";\nimport { ValueOptionType } from \"./ITabularRepository\";\nimport { TabularRepository } from \"./TabularRepository\";\n\n// BaseTabularRepository is a tabular store that uses SQLite and Postgres use as common code\n\n/**\n * Base class for SQL-based tabular repositories that implements common functionality\n * for both SQLite and PostgreSQL database implementations.\n *\n * @template Schema - The schema definition for the entity using JSON Schema\n * @template PrimaryKeyNames - Array of property names that form the primary key\n */\nexport abstract class BaseSqlTabularRepository<\n Schema extends DataPortSchemaObject,\n PrimaryKeyNames extends ReadonlyArray<keyof Schema[\"properties\"]>,\n // computed types\n Entity = FromSchema<Schema>,\n PrimaryKey = Pick<Entity, PrimaryKeyNames[number] & keyof Entity>,\n Value = Omit<Entity, PrimaryKeyNames[number] & keyof Entity>,\n> extends TabularRepository<Schema, PrimaryKeyNames, Entity, PrimaryKey, Value> {\n /**\n * Creates a new instance of BaseSqlTabularRepository\n * @param table - The name of the database table to use for storage\n * @param schema - Schema defining the structure of the entity\n * @param primaryKeyNames - Array of property names that form the primary key\n * @param indexes - Array of columns or column arrays to make searchable. Each string or single column creates a single-column index,\n * while each array creates a compound index with columns in the specified order.\n */\n constructor(\n protected readonly table: string = \"tabular_store\",\n schema: Schema,\n primaryKeyNames: PrimaryKeyNames,\n indexes: Array<keyof Entity | Array<keyof Entity>> = []\n ) {\n super(schema, primaryKeyNames, indexes);\n this.validateTableAndSchema();\n }\n\n /**\n * Maps JavaScript/TypeScript types to their corresponding SQL type\n * Must be implemented by derived classes for specific SQL dialects\n */\n protected abstract mapTypeToSQL(typeDef: JsonSchema): string;\n\n /**\n * Generates the SQL column definitions for primary key fields\n * @returns SQL string containing primary key column definitions\n */\n protected constructPrimaryKeyColumns($delimiter: string = \"\"): string {\n const cols = Object.entries<JsonSchema>(this.primaryKeySchema.properties)\n .map(([key, typeDef]) => {\n const sqlType = this.mapTypeToSQL(typeDef);\n return `${$delimiter}${key}${$delimiter} ${sqlType} NOT NULL`;\n })\n .join(\", \");\n return cols;\n }\n\n /**\n * Generates the SQL column definitions for value fields\n * @returns SQL string containing value column definitions\n */\n protected constructValueColumns($delimiter: string = \"\"): string {\n const requiredSet = new Set(this.valueSchema.required ?? []);\n const cols = Object.entries<JsonSchema>(this.valueSchema.properties)\n .map(([key, typeDef]) => {\n const sqlType = this.mapTypeToSQL(typeDef);\n // Check if the property is nullable based on schema definition or if it's not required\n const isRequired = requiredSet.has(key);\n const nullable = !isRequired || this.isNullable(typeDef);\n return `${$delimiter}${key}${$delimiter} ${sqlType}${nullable ? \" NULL\" : \" NOT NULL\"}`;\n })\n .join(\", \");\n if (cols.length > 0) {\n return `, ${cols}`;\n } else {\n return \"\";\n }\n }\n\n /**\n * Determines if a schema type allows null values\n * @param typeDef - The schema type definition\n * @returns true if the type allows null values\n */\n protected isNullable(typeDef: JsonSchema): boolean {\n if (typeof typeDef === \"boolean\") return typeDef;\n\n // Check for direct null type\n if (typeDef.type === \"null\") {\n return true;\n }\n\n // Check for type as an array that includes null (e.g., type: [\"null\", \"string\"])\n if (Array.isArray(typeDef.type)) {\n return typeDef.type.includes(\"null\");\n }\n\n // Check for union types that include null (anyOf)\n if (typeDef.anyOf && Array.isArray(typeDef.anyOf)) {\n return typeDef.anyOf.some((type: any) => type.type === \"null\");\n }\n\n // Check for union types that include null (oneOf)\n if (typeDef.oneOf && Array.isArray(typeDef.oneOf)) {\n return typeDef.oneOf.some((type: any) => type.type === \"null\");\n }\n\n return false;\n }\n\n /**\n * Returns a comma-separated list of primary key column names\n * @returns Formatted string of primary key column names\n */\n protected primaryKeyColumnList($delimiter: string = \"\"): string {\n return $delimiter + this.primaryKeyColumns().join(`${$delimiter}, ${$delimiter}`) + $delimiter;\n }\n\n /**\n * Returns a comma-separated list of value column names\n * @returns Formatted string of value column names\n */\n protected valueColumnList($delimiter: string = \"\"): string {\n return $delimiter + this.valueColumns().join(`${$delimiter}, ${$delimiter}`) + $delimiter;\n }\n\n /**\n * Gets the real underlying type from possibly union types\n * For example, for a union with null, this extracts the non-null type\n * @param typeDef - The schema to extract from\n * @returns The non-null type from the schema\n */\n protected getNonNullType(typeDef: JsonSchema): JsonSchema {\n if (typeof typeDef === \"boolean\") return typeDef;\n\n if (typeDef.anyOf && Array.isArray(typeDef.anyOf)) {\n const nonNullType = typeDef.anyOf.find((t: any) => t.type !== \"null\");\n if (nonNullType) {\n return nonNullType;\n }\n }\n if (typeDef.oneOf && Array.isArray(typeDef.oneOf)) {\n const nonNullType = typeDef.oneOf.find((t: any) => t.type !== \"null\");\n if (nonNullType) {\n return nonNullType;\n }\n }\n return typeDef;\n }\n\n /**\n * Converts a value object into an ordered array based on the valueSchema\n * This ensures consistent parameter ordering for SQL queries\n * @param value - The value object to convert\n * @returns Array of values ordered according to the schema\n * @throws Error if a required field is missing\n */\n protected getValueAsOrderedArray(value: Value): ValueOptionType[] {\n const orderedParams: ValueOptionType[] = [];\n const valueAsRecord = value as Record<string, Entity[keyof Entity]>;\n const requiredSet = new Set(this.valueSchema.required ?? []);\n for (const key in this.valueSchema.properties) {\n if (Object.prototype.hasOwnProperty.call(valueAsRecord, key)) {\n const val = valueAsRecord[key];\n // Convert undefined to null for optional fields\n if (val === undefined && !requiredSet.has(key)) {\n orderedParams.push(null);\n } else {\n orderedParams.push(this.jsToSqlValue(key, val));\n }\n } else {\n // If the field is required, throw an error\n if (requiredSet.has(key)) {\n throw new Error(`Missing required value field: ${key}`);\n }\n // If the field is optional, use null\n orderedParams.push(null);\n }\n }\n return orderedParams;\n }\n\n /**\n * Converts a primary key object into an ordered array based on the schema\n * This ensures consistent parameter ordering for storage operations\n * @param key - The primary key object to convert\n * @returns Array of key values ordered according to the schema\n */\n protected getPrimaryKeyAsOrderedArray(key: PrimaryKey): ValueOptionType[] {\n const orderedParams: ValueOptionType[] = [];\n const keyObj = key as Record<string, Entity[keyof Entity]>;\n for (const k of Object.keys(this.primaryKeySchema.properties)) {\n if (k in keyObj) {\n const value = keyObj[k];\n if (value === null) {\n throw new Error(`Primary key field ${k} cannot be null`);\n }\n orderedParams.push(this.jsToSqlValue(k, value));\n } else {\n throw new Error(`Missing required primary key field: ${k}`);\n }\n }\n return orderedParams;\n }\n\n protected jsToSqlValue(column: string, value: Entity[keyof Entity]): ValueOptionType {\n const typeDef = this.schema.properties[column];\n if (!typeDef) {\n return value as ValueOptionType;\n }\n\n // Handle null values for nullable columns\n if (value === null && this.isNullable(typeDef)) {\n return null;\n }\n\n // Extract the non-null type for proper handling\n const actualType = this.getNonNullType(typeDef);\n if (typeof actualType === \"boolean\") {\n return value as ValueOptionType;\n }\n\n if (actualType.contentEncoding === \"blob\") {\n const v: any = value;\n if (v instanceof Uint8Array) {\n return v as unknown as ValueOptionType;\n }\n if (typeof Buffer !== \"undefined\" && v instanceof Buffer) {\n return new Uint8Array(v) as unknown as ValueOptionType;\n }\n if (Array.isArray(v)) {\n return new Uint8Array(v) as unknown as ValueOptionType;\n }\n return v as unknown as ValueOptionType;\n } else if (value instanceof Date) {\n // Convert all Date objects to ISO string regardless of type definition\n return value.toISOString();\n } else {\n return value as ValueOptionType;\n }\n }\n\n protected sqlToJsValue(column: string, value: ValueOptionType): Entity[keyof Entity] {\n // Get the type definition\n const typeDef = this.schema.properties[column];\n if (!typeDef) {\n return value as Entity[keyof Entity];\n }\n\n // Handle null values\n if (value === null && this.isNullable(typeDef)) {\n return null as any;\n }\n\n // Extract the non-null type for proper handling\n const actualType = this.getNonNullType(typeDef);\n if (typeof actualType === \"boolean\") {\n return value as Entity[keyof Entity];\n }\n\n if (actualType.contentEncoding === \"blob\") {\n const v: any = value;\n if (typeof Buffer !== \"undefined\" && v instanceof Buffer) {\n return new Uint8Array(v) as Entity[keyof Entity];\n }\n if (v instanceof Uint8Array) {\n return v as Entity[keyof Entity];\n }\n return v as Entity[keyof Entity];\n } else {\n return value as Entity[keyof Entity];\n }\n }\n\n /**\n * Validates table name and schema configurations\n * Checks for:\n * 1. Valid table name format\n * 2. Valid schema key names\n * 3. No duplicate keys between primary key and value schemas\n * This is a sanity check to make sure the table and schema are valid,\n * and to prevent dumb mistakes and mischevious behavior.\n * @throws Error if validation fails\n */\n protected validateTableAndSchema(): void {\n // Validate table name\n if (!/^[a-zA-Z][a-zA-Z0-9_]*$/.test(this.table)) {\n throw new Error(\n \"Table name must start with a letter and contain only letters, digits, and underscores, got: \" +\n this.table\n );\n }\n\n // Validate schema keys\n const validateSchemaKeys = (schema: DataPortSchemaObject) => {\n for (const key in schema.properties) {\n if (!/^[a-zA-Z][a-zA-Z0-9_]*$/.test(key)) {\n throw new Error(\n \"Schema keys must start with a letter and contain only letters, digits, and underscores, got: \" +\n key\n );\n }\n }\n };\n\n validateSchemaKeys(this.primaryKeySchema);\n validateSchemaKeys(this.valueSchema);\n\n // Check for key name collisions between schemas\n const primaryKeys = new Set(Object.keys(this.primaryKeySchema.properties));\n const valueKeys = Object.keys(this.valueSchema.properties);\n const duplicates = valueKeys.filter((key) => primaryKeys.has(key));\n if (duplicates.length > 0) {\n throw new Error(`Duplicate keys found in schemas: ${duplicates.join(\", \")}`);\n }\n }\n}\n",
19
21
  "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { createServiceToken, JsonSchema } from \"@workglow/util\";\nimport { IndexedDbTabularRepository } from \"../tabular/IndexedDbTabularRepository\";\nimport { DefaultKeyValueKey, DefaultKeyValueSchema, IKvRepository } from \"./IKvRepository\";\nimport { KvViaTabularRepository } from \"./KvViaTabularRepository\";\n\nexport const IDB_KV_REPOSITORY = createServiceToken<IKvRepository<string, any, any>>(\n \"storage.kvRepository.indexedDb\"\n);\n\n/**\n * A key-value repository implementation that uses IndexedDB for persistent storage in the browser.\n * Leverages a tabular repository abstraction for IndexedDB operations.\n *\n * @template Key - The type of the primary key\n * @template Value - The type of the value being stored\n * @template Combined - Combined type of Key & Value\n */\nexport class IndexedDbKvRepository extends KvViaTabularRepository {\n public tabularRepository: IndexedDbTabularRepository<\n typeof DefaultKeyValueSchema,\n typeof DefaultKeyValueKey\n >;\n\n /**\n * Creates a new KvRepository instance\n */\n constructor(\n public dbName: string,\n keySchema: JsonSchema = { type: \"string\" },\n valueSchema: JsonSchema = {}\n ) {\n super(keySchema, valueSchema);\n this.tabularRepository = new IndexedDbTabularRepository(\n dbName,\n DefaultKeyValueSchema,\n DefaultKeyValueKey\n );\n }\n}\n",
20
22
  "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type { SupabaseClient } from \"@supabase/supabase-js\";\nimport { createServiceToken, JsonSchema } from \"@workglow/util\";\nimport { SupabaseTabularRepository } from \"../tabular/SupabaseTabularRepository\";\nimport { DefaultKeyValueKey, DefaultKeyValueSchema, IKvRepository } from \"./IKvRepository\";\nimport { KvViaTabularRepository } from \"./KvViaTabularRepository\";\n\nexport const SUPABASE_KV_REPOSITORY = createServiceToken<IKvRepository<string, any, any>>(\n \"storage.kvRepository.supabase\"\n);\n\n/**\n * A key-value repository implementation that uses Supabase for persistent storage.\n * Leverages a tabular repository abstraction for Supabase operations.\n *\n * @template Key - The type of the primary key\n * @template Value - The type of the value being stored\n * @template Combined - Combined type of Key & Value\n */\nexport class SupabaseKvRepository extends KvViaTabularRepository {\n public tabularRepository: SupabaseTabularRepository<\n typeof DefaultKeyValueSchema,\n typeof DefaultKeyValueKey\n >;\n\n /**\n * Creates a new SupabaseKvRepository instance\n *\n * @param client - Supabase client instance\n * @param tableName - Name of the table to store data\n * @param keySchema - Schema for the key type (defaults to string)\n * @param valueSchema - Schema for the value type (defaults to any)\n */\n constructor(\n public client: SupabaseClient,\n public tableName: string,\n keySchema: JsonSchema = { type: \"string\" },\n valueSchema: JsonSchema = {},\n tabularRepository?: SupabaseTabularRepository<\n typeof DefaultKeyValueSchema,\n typeof DefaultKeyValueKey\n >\n ) {\n super(keySchema, valueSchema);\n this.tabularRepository =\n tabularRepository ??\n new SupabaseTabularRepository(client, tableName, DefaultKeyValueSchema, DefaultKeyValueKey);\n }\n}\n",
21
- "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { createServiceToken, makeFingerprint, uuid4 } from \"@workglow/util\";\nimport {\n ensureIndexedDbTable,\n ExpectedIndexDefinition,\n MigrationOptions,\n} from \"../util/IndexedDbTable\";\nimport { IQueueStorage, JobStatus, JobStorageFormat } from \"./IQueueStorage\";\n\nexport const INDEXED_DB_QUEUE_STORAGE = createServiceToken<IQueueStorage<any, any>>(\n \"jobqueue.storage.indexedDb\"\n);\n\n/**\n * IndexedDB implementation of a job queue storage.\n * Provides storage and retrieval for job execution states using IndexedDB.\n */\nexport class IndexedDbQueueStorage<Input, Output> implements IQueueStorage<Input, Output> {\n private db: IDBDatabase | undefined;\n private tableName: string;\n private migrationOptions: MigrationOptions;\n\n constructor(\n public readonly queueName: string,\n migrationOptions: MigrationOptions = {}\n ) {\n this.tableName = `jobs_${queueName}`;\n this.migrationOptions = migrationOptions;\n }\n\n private async getDb(): Promise<IDBDatabase> {\n if (this.db) return this.db;\n await this.setupDatabase();\n return this.db!;\n }\n\n /**\n * Sets up the IndexedDB database table with the required schema and indexes.\n * Must be called before using any other methods.\n */\n public async setupDatabase(): Promise<void> {\n const expectedIndexes: ExpectedIndexDefinition[] = [\n {\n name: \"status\",\n keyPath: `status`,\n options: { unique: false },\n },\n {\n name: \"status_run_after\",\n keyPath: [\"status\", \"run_after\"],\n options: { unique: false },\n },\n {\n name: \"job_run_id\",\n keyPath: `job_run_id`,\n options: { unique: false },\n },\n {\n name: \"fingerprint_status\",\n keyPath: [\"fingerprint\", \"status\"],\n options: { unique: false },\n },\n ];\n\n // Now initialize the database\n this.db = await ensureIndexedDbTable(\n this.tableName,\n \"id\",\n expectedIndexes,\n this.migrationOptions\n );\n }\n\n /**\n * Adds a job to the queue.\n * @param job - The job to add to the queue.\n * @returns A promise that resolves to the job id.\n */\n public async add(job: JobStorageFormat<Input, Output>): Promise<unknown> {\n const db = await this.getDb();\n const now = new Date().toISOString();\n job.id = job.id ?? uuid4();\n job.job_run_id = job.job_run_id ?? uuid4();\n job.queue = this.queueName;\n job.fingerprint = await makeFingerprint(job.input);\n job.status = JobStatus.PENDING;\n job.progress = 0;\n job.progress_message = \"\";\n job.progress_details = null;\n job.created_at = now;\n job.run_after = now;\n\n const tx = db.transaction(this.tableName, \"readwrite\");\n const store = tx.objectStore(this.tableName);\n\n return new Promise((resolve, reject) => {\n const request = store.add(job);\n\n // Don't resolve until transaction is complete\n tx.oncomplete = () => resolve(job.id);\n tx.onerror = () => reject(tx.error);\n request.onerror = () => reject(request.error);\n });\n }\n\n /**\n * Retrieves a job from the queue by its id.\n * @param id - The id of the job to retrieve.\n * @returns A promise that resolves to the job or undefined if the job is not found.\n */\n async get(id: unknown): Promise<JobStorageFormat<Input, Output> | undefined> {\n const db = await this.getDb();\n const tx = db.transaction(this.tableName, \"readonly\");\n const store = tx.objectStore(this.tableName);\n const request = store.get(id as string);\n return new Promise((resolve, reject) => {\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error);\n tx.onerror = () => reject(tx.error);\n });\n }\n\n /**\n * Retrieves a slice of jobs from the queue.\n * @param status - The status of the jobs to retrieve.\n * @param num - The number of jobs to retrieve.\n * @returns A promise that resolves to an array of jobs.\n */\n public async peek(\n status: JobStatus = JobStatus.PENDING,\n num: number = 100\n ): Promise<JobStorageFormat<Input, Output>[]> {\n const db = await this.getDb();\n const tx = db.transaction(this.tableName, \"readonly\");\n const store = tx.objectStore(this.tableName);\n const index = store.index(\"status_run_after\");\n\n return new Promise((resolve, reject) => {\n const ret = new Map<unknown, JobStorageFormat<Input, Output>>();\n // Create a key range for the compound index: from [status, \"\"] to [status, \"\\uffff\"]\n const keyRange = IDBKeyRange.bound([status, \"\"], [status, \"\\uffff\"]);\n const cursorRequest = index.openCursor(keyRange);\n\n const handleCursor = (e: Event) => {\n const cursor = (e.target as IDBRequest<IDBCursorWithValue>).result;\n if (!cursor || ret.size >= num) {\n resolve(Array.from(ret.values()));\n return;\n }\n // Use Map to ensure no duplicates by job ID\n ret.set(cursor.value.id, cursor.value);\n cursor.continue();\n };\n\n cursorRequest.onsuccess = handleCursor;\n cursorRequest.onerror = () => reject(cursorRequest.error);\n tx.onerror = () => reject(tx.error);\n });\n }\n\n /**\n * Retrieves the next job from the queue.\n * @returns A promise that resolves to the next job or undefined if the queue is empty.\n */\n public async next(): Promise<JobStorageFormat<Input, Output> | undefined> {\n const db = await this.getDb();\n const tx = db.transaction(this.tableName, \"readwrite\");\n const store = tx.objectStore(this.tableName);\n const index = store.index(\"status_run_after\");\n const now = new Date().toISOString();\n\n return new Promise((resolve, reject) => {\n const cursorRequest = index.openCursor(\n IDBKeyRange.bound([JobStatus.PENDING, \"\"], [JobStatus.PENDING, now], false, true)\n );\n\n let jobToReturn: JobStorageFormat<Input, Output> | undefined;\n\n cursorRequest.onsuccess = (e) => {\n const cursor = (e.target as IDBRequest<IDBCursorWithValue>).result;\n if (!cursor) {\n if (jobToReturn) {\n resolve(jobToReturn);\n } else {\n resolve(undefined);\n }\n return;\n }\n\n const job = cursor.value;\n // Verify the job is still in PENDING state\n if (job.status !== JobStatus.PENDING) {\n cursor.continue();\n return;\n }\n\n job.status = JobStatus.PROCESSING;\n job.last_ran_at = now;\n\n try {\n const updateRequest = store.put(job);\n updateRequest.onsuccess = () => {\n jobToReturn = job;\n // Don't resolve here - wait for transaction to complete\n };\n updateRequest.onerror = (err) => {\n console.error(\"Failed to update job status:\", err);\n cursor.continue();\n };\n } catch (err) {\n console.error(\"Error updating job:\", err);\n cursor.continue();\n }\n };\n\n cursorRequest.onerror = () => reject(cursorRequest.error);\n\n // Wait for transaction to complete before resolving\n tx.oncomplete = () => {\n resolve(jobToReturn);\n };\n tx.onerror = () => reject(tx.error);\n });\n }\n\n /**\n * Retrieves the number of jobs in the queue.\n * Returns the count of jobs in the queue.\n */\n public async size(status = JobStatus.PENDING): Promise<number> {\n const db = await this.getDb();\n return new Promise((resolve, reject) => {\n const tx = db.transaction(this.tableName, \"readonly\");\n const store = tx.objectStore(this.tableName);\n const index = store.index(\"status\");\n const request = index.count(status);\n\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error);\n tx.onerror = () => reject(tx.error);\n });\n }\n\n /**\n * Marks a job as complete with its output or error.\n */\n public async complete(job: JobStorageFormat<Input, Output>): Promise<void> {\n const db = await this.getDb();\n const tx = db.transaction(this.tableName, \"readwrite\");\n const store = tx.objectStore(this.tableName);\n\n return new Promise((resolve, reject) => {\n const getReq = store.get(job.id as string);\n getReq.onsuccess = () => {\n const existing = getReq.result as JobStorageFormat<Input, Output> | undefined;\n const currentAttempts = existing?.run_attempts ?? 0;\n job.run_attempts = currentAttempts + 1;\n const putReq = store.put(job);\n putReq.onsuccess = () => {};\n putReq.onerror = () => reject(putReq.error);\n };\n getReq.onerror = () => reject(getReq.error);\n\n // Don't resolve until transaction is complete\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n });\n }\n\n /**\n * Aborts a job in the queue.\n */\n public async abort(id: unknown): Promise<void> {\n const job = await this.get(id);\n if (!job) return;\n\n job.status = JobStatus.ABORTING;\n await this.complete(job);\n }\n\n /**\n * Gets jobs by their run ID.\n */\n public async getByRunId(job_run_id: string): Promise<JobStorageFormat<Input, Output>[]> {\n const db = await this.getDb();\n const tx = db.transaction(this.tableName, \"readonly\");\n const store = tx.objectStore(this.tableName);\n const index = store.index(\"job_run_id\");\n const request = index.getAll(job_run_id);\n\n return new Promise((resolve, reject) => {\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error);\n tx.onerror = () => reject(tx.error);\n });\n }\n\n /**\n * Deletes all jobs from the queue.\n */\n public async deleteAll(): Promise<void> {\n const db = await this.getDb();\n const tx = db.transaction(this.tableName, \"readwrite\");\n const store = tx.objectStore(this.tableName);\n const request = store.clear();\n\n return new Promise((resolve, reject) => {\n request.onsuccess = () => resolve();\n request.onerror = () => reject(request.error);\n tx.onerror = () => reject(tx.error);\n });\n }\n\n /**\n * Gets the output for a given input.\n */\n public async outputForInput(input: Input): Promise<Output | null> {\n const fingerprint = await makeFingerprint(input);\n const db = await this.getDb();\n const tx = db.transaction(this.tableName, \"readonly\");\n const store = tx.objectStore(this.tableName);\n const index = store.index(\"fingerprint_status\");\n const request = index.get([fingerprint, JobStatus.COMPLETED]);\n\n return new Promise((resolve, reject) => {\n request.onsuccess = () => resolve(request.result?.output ?? null);\n request.onerror = () => reject(request.error);\n tx.onerror = () => reject(tx.error);\n });\n }\n\n /**\n * Saves progress updates for a job.\n */\n public async saveProgress(\n id: unknown,\n progress: number,\n message: string,\n details: Record<string, any> | null\n ): Promise<void> {\n const job = await this.get(id);\n if (!job) throw new Error(`Job ${id} not found`);\n\n job.progress = progress;\n job.progress_message = message;\n job.progress_details = details;\n\n await this.complete(job);\n }\n\n /**\n * Deletes a job by its ID.\n */\n public async delete(id: unknown): Promise<void> {\n const db = await this.getDb();\n const tx = db.transaction(this.tableName, \"readwrite\");\n const store = tx.objectStore(this.tableName);\n const request = store.delete(id as string);\n\n return new Promise((resolve, reject) => {\n request.onsuccess = () => resolve();\n request.onerror = () => reject(request.error);\n tx.onerror = () => reject(tx.error);\n });\n }\n\n /**\n * Delete jobs with a specific status older than a cutoff date\n * @param status - Status of jobs to delete\n * @param olderThanMs - Delete jobs completed more than this many milliseconds ago\n */\n public async deleteJobsByStatusAndAge(status: JobStatus, olderThanMs: number): Promise<void> {\n const db = await this.getDb();\n const tx = db.transaction(this.tableName, \"readwrite\");\n const store = tx.objectStore(this.tableName);\n const index = store.index(\"status\");\n const cutoffDate = new Date(Date.now() - olderThanMs).toISOString();\n\n return new Promise((resolve, reject) => {\n const request = index.openCursor();\n\n request.onsuccess = (event) => {\n const cursor = (event.target as IDBRequest<IDBCursorWithValue>).result;\n if (cursor) {\n const job = cursor.value;\n if (job.status === status && job.completed_at && job.completed_at <= cutoffDate) {\n cursor.delete();\n }\n cursor.continue();\n }\n };\n\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n request.onerror = () => reject(request.error);\n });\n }\n}\n",
22
- "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type { SupabaseClient } from \"@supabase/supabase-js\";\nimport { createServiceToken, makeFingerprint, uuid4 } from \"@workglow/util\";\nimport { IQueueStorage, JobStatus, JobStorageFormat } from \"./IQueueStorage\";\n\nexport const SUPABASE_QUEUE_STORAGE = createServiceToken<IQueueStorage<any, any>>(\n \"jobqueue.storage.supabase\"\n);\n\n/**\n * Supabase implementation of a job queue.\n * Provides storage and retrieval for job execution states using Supabase.\n */\nexport class SupabaseQueueStorage<Input, Output> implements IQueueStorage<Input, Output> {\n constructor(\n protected readonly client: SupabaseClient,\n protected readonly queueName: string\n ) {}\n\n public async setupDatabase(): Promise<void> {\n // Note: For Supabase, table creation should typically be done through migrations\n // This setup assumes the table already exists or uses exec_sql RPC function\n const createTypeSql = `CREATE TYPE job_status AS ENUM (${Object.values(JobStatus)\n .map((v) => `'${v}'`)\n .join(\",\")})`;\n\n const { error: typeError } = await this.client.rpc(\"exec_sql\", { query: createTypeSql });\n // Ignore error if type already exists (code 42710)\n if (typeError && typeError.code !== \"42710\") {\n throw typeError;\n }\n\n const createTableSql = `\n CREATE TABLE IF NOT EXISTS job_queue (\n id SERIAL NOT NULL,\n fingerprint text NOT NULL,\n queue text NOT NULL,\n job_run_id text NOT NULL,\n status job_status NOT NULL default 'PENDING',\n input jsonb NOT NULL,\n output jsonb,\n run_attempts integer default 0,\n max_retries integer default 20,\n run_after timestamp with time zone DEFAULT now(),\n last_ran_at timestamp with time zone,\n created_at timestamp with time zone DEFAULT now(),\n deadline_at timestamp with time zone,\n completed_at timestamp with time zone,\n error text,\n error_code text,\n progress real DEFAULT 0,\n progress_message text DEFAULT '',\n progress_details jsonb\n )`;\n\n const { error: tableError } = await this.client.rpc(\"exec_sql\", { query: createTableSql });\n if (tableError) {\n // Ignore error if table already exists (code 42P07)\n if (tableError.code !== \"42P07\") {\n throw tableError;\n }\n }\n\n // Create indexes\n const indexes = [\n `CREATE INDEX IF NOT EXISTS job_fetcher_idx ON job_queue (id, status, run_after)`,\n `CREATE INDEX IF NOT EXISTS job_queue_fetcher_idx ON job_queue (queue, status, run_after)`,\n `CREATE INDEX IF NOT EXISTS jobs_fingerprint_unique_idx ON job_queue (queue, fingerprint, status)`,\n ];\n\n for (const indexSql of indexes) {\n const { error: indexError } = await this.client.rpc(\"exec_sql\", { query: indexSql });\n // Ignore index creation errors\n }\n }\n\n /**\n * Adds a new job to the queue.\n * @param job - The job to add\n * @returns The ID of the added job\n */\n public async add(job: JobStorageFormat<Input, Output>): Promise<unknown> {\n const now = new Date().toISOString();\n job.queue = this.queueName;\n job.job_run_id = job.job_run_id ?? uuid4();\n job.fingerprint = await makeFingerprint(job.input);\n job.status = JobStatus.PENDING;\n job.progress = 0;\n job.progress_message = \"\";\n job.progress_details = null;\n job.created_at = now;\n job.run_after = now;\n\n const { data, error } = await this.client\n .from(\"job_queue\")\n .insert({\n queue: job.queue,\n fingerprint: job.fingerprint,\n input: job.input,\n run_after: job.run_after,\n created_at: job.created_at,\n deadline_at: job.deadline_at,\n max_retries: job.max_retries,\n job_run_id: job.job_run_id,\n progress: job.progress,\n progress_message: job.progress_message,\n progress_details: job.progress_details,\n })\n .select(\"id\")\n .single();\n\n if (error) throw error;\n if (!data) throw new Error(\"Failed to add to queue\");\n\n job.id = data.id;\n return job.id;\n }\n\n /**\n * Retrieves a job by its ID.\n * @param id - The ID of the job to retrieve\n * @returns The job if found, undefined otherwise\n */\n public async get(id: number): Promise<JobStorageFormat<Input, Output> | undefined> {\n const { data, error } = await this.client\n .from(\"job_queue\")\n .select(\"*\")\n .eq(\"id\", id)\n .eq(\"queue\", this.queueName)\n .single();\n\n if (error) {\n if (error.code === \"PGRST116\") return undefined; // Not found\n throw error;\n }\n\n return data as JobStorageFormat<Input, Output> | undefined;\n }\n\n /**\n * Retrieves a slice of jobs from the queue.\n * @param status - The status to filter by\n * @param num - Maximum number of jobs to return\n * @returns An array of jobs\n */\n public async peek(\n status: JobStatus = JobStatus.PENDING,\n num: number = 100\n ): Promise<JobStorageFormat<Input, Output>[]> {\n num = Number(num) || 100;\n\n const { data, error } = await this.client\n .from(\"job_queue\")\n .select(\"*\")\n .eq(\"queue\", this.queueName)\n .eq(\"status\", status)\n .order(\"run_after\", { ascending: true })\n .limit(num);\n\n if (error) throw error;\n return (data as JobStorageFormat<Input, Output>[]) ?? [];\n }\n\n /**\n * Retrieves the next available job that is ready to be processed.\n * @returns The next job or undefined if no job is available\n */\n public async next(): Promise<JobStorageFormat<Input, Output> | undefined> {\n // First, find the next job\n const { data: jobs, error: selectError } = await this.client\n .from(\"job_queue\")\n .select(\"*\")\n .eq(\"queue\", this.queueName)\n .eq(\"status\", JobStatus.PENDING)\n .lte(\"run_after\", new Date().toISOString())\n .order(\"run_after\", { ascending: true })\n .limit(1);\n\n if (selectError) throw selectError;\n if (!jobs || jobs.length === 0) return undefined;\n\n const job = jobs[0];\n\n // Update its status\n const { data: updatedJob, error: updateError } = await this.client\n .from(\"job_queue\")\n .update({\n status: JobStatus.PROCESSING,\n last_ran_at: new Date().toISOString(),\n })\n .eq(\"id\", job.id)\n .eq(\"queue\", this.queueName)\n .select()\n .single();\n\n if (updateError) throw updateError;\n return updatedJob as JobStorageFormat<Input, Output>;\n }\n\n /**\n * Retrieves the number of jobs in the queue with a specific status.\n * @param status - The status of the jobs to count\n * @returns The count of jobs with the specified status\n */\n public async size(status = JobStatus.PENDING): Promise<number> {\n const { count, error } = await this.client\n .from(\"job_queue\")\n .select(\"*\", { count: \"exact\", head: true })\n .eq(\"queue\", this.queueName)\n .eq(\"status\", status);\n\n if (error) throw error;\n return count ?? 0;\n }\n\n /**\n * Marks a job as complete with its output or error.\n * Enhanced error handling:\n * - For a retryable error, increments run_attempts and updates run_after.\n * - Marks a job as FAILED immediately for permanent or generic errors.\n */\n public async complete(jobDetails: JobStorageFormat<Input, Output>): Promise<void> {\n const now = new Date().toISOString();\n\n // Handle disabled without changing attempts\n if (jobDetails.status === JobStatus.DISABLED) {\n const { error } = await this.client\n .from(\"job_queue\")\n .update({\n status: jobDetails.status,\n progress: 100,\n progress_message: \"\",\n progress_details: null,\n completed_at: now,\n last_ran_at: now,\n })\n .eq(\"id\", jobDetails.id)\n .eq(\"queue\", this.queueName);\n if (error) throw error;\n return;\n }\n\n // Read current attempts to compute next value deterministically\n const { data: current, error: getError } = await this.client\n .from(\"job_queue\")\n .select(\"run_attempts\")\n .eq(\"id\", jobDetails.id as number)\n .eq(\"queue\", this.queueName)\n .single();\n if (getError) throw getError;\n const nextAttempts = ((current?.run_attempts as number | undefined) ?? 0) + 1;\n\n if (jobDetails.status === JobStatus.PENDING) {\n const { error } = await this.client\n .from(\"job_queue\")\n .update({\n error: jobDetails.error ?? null,\n error_code: jobDetails.error_code ?? null,\n status: jobDetails.status,\n run_after: jobDetails.run_after!,\n progress: 0,\n progress_message: \"\",\n progress_details: null,\n run_attempts: nextAttempts,\n last_ran_at: now,\n })\n .eq(\"id\", jobDetails.id)\n .eq(\"queue\", this.queueName);\n if (error) throw error;\n return;\n }\n\n if (jobDetails.status === JobStatus.COMPLETED || jobDetails.status === JobStatus.FAILED) {\n const { error } = await this.client\n .from(\"job_queue\")\n .update({\n output: jobDetails.output ?? null,\n error: jobDetails.error ?? null,\n error_code: jobDetails.error_code ?? null,\n status: jobDetails.status,\n progress: 100,\n progress_message: \"\",\n progress_details: null,\n run_attempts: nextAttempts,\n completed_at: now,\n last_ran_at: now,\n })\n .eq(\"id\", jobDetails.id)\n .eq(\"queue\", this.queueName);\n if (error) throw error;\n return;\n }\n\n // Transitional states: PROCESSING/ABORTING etc - increment attempts like other stores\n const { error } = await this.client\n .from(\"job_queue\")\n .update({\n status: jobDetails.status,\n output: jobDetails.output ?? null,\n error: jobDetails.error ?? null,\n error_code: jobDetails.error_code ?? null,\n run_after: jobDetails.run_after ?? null,\n run_attempts: nextAttempts,\n last_ran_at: now,\n })\n .eq(\"id\", jobDetails.id)\n .eq(\"queue\", this.queueName);\n if (error) throw error;\n }\n\n /**\n * Clears all jobs from the queue.\n */\n public async deleteAll(): Promise<void> {\n const { error } = await this.client.from(\"job_queue\").delete().eq(\"queue\", this.queueName);\n\n if (error) throw error;\n }\n\n /**\n * Looks up cached output for a given input\n * Uses input fingerprinting for efficient matching\n * @returns The cached output or null if not found\n */\n public async outputForInput(input: Input): Promise<Output | null> {\n const fingerprint = await makeFingerprint(input);\n\n const { data, error } = await this.client\n .from(\"job_queue\")\n .select(\"output\")\n .eq(\"fingerprint\", fingerprint)\n .eq(\"queue\", this.queueName)\n .eq(\"status\", JobStatus.COMPLETED)\n .single();\n\n if (error) {\n if (error.code === \"PGRST116\") return null; // Not found\n throw error;\n }\n\n return data?.output ?? null;\n }\n\n /**\n * Aborts a job by setting its status to \"ABORTING\".\n * This method will signal the corresponding AbortController so that\n * the job's execute() method (if it supports an AbortSignal parameter)\n * can clean up and exit.\n */\n public async abort(jobId: unknown): Promise<void> {\n const { error } = await this.client\n .from(\"job_queue\")\n .update({ status: JobStatus.ABORTING })\n .eq(\"id\", jobId)\n .eq(\"queue\", this.queueName);\n\n if (error) throw error;\n }\n\n /**\n * Retrieves all jobs for a given job run ID.\n * @param job_run_id - The ID of the job run to retrieve\n * @returns An array of jobs\n */\n public async getByRunId(job_run_id: string): Promise<Array<JobStorageFormat<Input, Output>>> {\n const { data, error } = await this.client\n .from(\"job_queue\")\n .select(\"*\")\n .eq(\"job_run_id\", job_run_id)\n .eq(\"queue\", this.queueName);\n\n if (error) throw error;\n return (data as Array<JobStorageFormat<Input, Output>>) ?? [];\n }\n\n /**\n * Implements the saveProgress method\n */\n public async saveProgress(\n jobId: unknown,\n progress: number,\n message: string,\n details: Record<string, any>\n ): Promise<void> {\n const { error } = await this.client\n .from(\"job_queue\")\n .update({\n progress,\n progress_message: message,\n progress_details: details,\n })\n .eq(\"id\", jobId)\n .eq(\"queue\", this.queueName);\n\n if (error) throw error;\n }\n\n /**\n * Deletes a job by its ID\n */\n public async delete(jobId: unknown): Promise<void> {\n const { error } = await this.client\n .from(\"job_queue\")\n .delete()\n .eq(\"id\", jobId)\n .eq(\"queue\", this.queueName);\n\n if (error) throw error;\n }\n\n /**\n * Delete jobs with a specific status older than a cutoff date\n * @param status - Status of jobs to delete\n * @param olderThanMs - Delete jobs completed more than this many milliseconds ago\n */\n public async deleteJobsByStatusAndAge(status: JobStatus, olderThanMs: number): Promise<void> {\n const cutoffDate = new Date(Date.now() - olderThanMs).toISOString();\n\n const { error } = await this.client\n .from(\"job_queue\")\n .delete()\n .eq(\"queue\", this.queueName)\n .eq(\"status\", status)\n .not(\"completed_at\", \"is\", null)\n .lte(\"completed_at\", cutoffDate);\n\n if (error) throw error;\n }\n}\n"
23
+ "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { createServiceToken, makeFingerprint, uuid4 } from \"@workglow/util\";\nimport {\n ensureIndexedDbTable,\n ExpectedIndexDefinition,\n MigrationOptions,\n} from \"../util/IndexedDbTable\";\nimport { PollingSubscriptionManager } from \"../util/PollingSubscriptionManager\";\nimport {\n IQueueStorage,\n JobStatus,\n JobStorageFormat,\n PrefixColumn,\n QueueChangePayload,\n QueueStorageOptions,\n QueueSubscribeOptions,\n} from \"./IQueueStorage\";\n\nexport const INDEXED_DB_QUEUE_STORAGE = createServiceToken<IQueueStorage<any, any>>(\n \"jobqueue.storage.indexedDb\"\n);\n\n/**\n * Extended options for IndexedDB queue storage including prefix support\n */\nexport interface IndexedDbQueueStorageOptions extends QueueStorageOptions, MigrationOptions {}\n\n/**\n * IndexedDB implementation of a job queue storage.\n * Provides storage and retrieval for job execution states using IndexedDB.\n */\nexport class IndexedDbQueueStorage<Input, Output> implements IQueueStorage<Input, Output> {\n private db: IDBDatabase | undefined;\n private readonly tableName: string;\n private readonly migrationOptions: MigrationOptions;\n /** The prefix column definitions */\n protected readonly prefixes: readonly PrefixColumn[];\n /** The prefix values for filtering */\n protected readonly prefixValues: Readonly<Record<string, string | number>>;\n /** Shared polling subscription manager */\n private pollingManager: PollingSubscriptionManager<\n JobStorageFormat<Input, Output>,\n unknown,\n QueueChangePayload<Input, Output>\n > | null = null;\n\n constructor(\n public readonly queueName: string,\n options: IndexedDbQueueStorageOptions = {}\n ) {\n this.migrationOptions = options;\n this.prefixes = options.prefixes ?? [];\n this.prefixValues = options.prefixValues ?? {};\n // Generate table name based on prefix configuration to avoid conflicts\n if (this.prefixes.length > 0) {\n const prefixNames = this.prefixes.map((p) => p.name).join(\"_\");\n this.tableName = `jobs_${prefixNames}`;\n } else {\n this.tableName = \"jobs\";\n }\n }\n\n /**\n * Gets prefix column names for use in indexes\n */\n private getPrefixColumnNames(): string[] {\n return this.prefixes.map((p) => p.name);\n }\n\n /**\n * Checks if a job matches the current prefix values\n */\n private matchesPrefixes(job: JobStorageFormat<Input, Output> & Record<string, unknown>): boolean {\n for (const [key, value] of Object.entries(this.prefixValues)) {\n if (job[key] !== value) {\n return false;\n }\n }\n return true;\n }\n\n /**\n * Gets prefix values as an array in column order for index key construction\n */\n private getPrefixKeyValues(): Array<string | number> {\n return this.prefixes.map((p) => this.prefixValues[p.name]);\n }\n\n private async getDb(): Promise<IDBDatabase> {\n if (this.db) return this.db;\n await this.setupDatabase();\n return this.db!;\n }\n\n /**\n * Sets up the IndexedDB database table with the required schema and indexes.\n * Must be called before using any other methods.\n */\n public async setupDatabase(): Promise<void> {\n const prefixColumnNames = this.getPrefixColumnNames();\n\n // Build index key paths with prefixes prepended\n const buildKeyPath = (basePath: string[]): string[] => {\n return [...prefixColumnNames, ...basePath];\n };\n\n const expectedIndexes: ExpectedIndexDefinition[] = [\n {\n name: \"queue_status\",\n keyPath: buildKeyPath([\"queue\", \"status\"]),\n options: { unique: false },\n },\n {\n name: \"queue_status_run_after\",\n keyPath: buildKeyPath([\"queue\", \"status\", \"run_after\"]),\n options: { unique: false },\n },\n {\n name: \"queue_job_run_id\",\n keyPath: buildKeyPath([\"queue\", \"job_run_id\"]),\n options: { unique: false },\n },\n {\n name: \"queue_fingerprint_status\",\n keyPath: buildKeyPath([\"queue\", \"fingerprint\", \"status\"]),\n options: { unique: false },\n },\n ];\n\n // Now initialize the database\n this.db = await ensureIndexedDbTable(\n this.tableName,\n \"id\",\n expectedIndexes,\n this.migrationOptions\n );\n }\n\n /**\n * Adds a job to the queue.\n * @param job - The job to add to the queue.\n * @returns A promise that resolves to the job id.\n */\n public async add(job: JobStorageFormat<Input, Output>): Promise<unknown> {\n const db = await this.getDb();\n const now = new Date().toISOString();\n const jobWithPrefixes = job as JobStorageFormat<Input, Output> & Record<string, unknown>;\n jobWithPrefixes.id = jobWithPrefixes.id ?? uuid4();\n jobWithPrefixes.job_run_id = jobWithPrefixes.job_run_id ?? uuid4();\n jobWithPrefixes.queue = this.queueName;\n jobWithPrefixes.fingerprint = await makeFingerprint(jobWithPrefixes.input);\n jobWithPrefixes.status = JobStatus.PENDING;\n jobWithPrefixes.progress = 0;\n jobWithPrefixes.progress_message = \"\";\n jobWithPrefixes.progress_details = null;\n jobWithPrefixes.created_at = now;\n jobWithPrefixes.run_after = now;\n\n // Add prefix values to the job\n for (const [key, value] of Object.entries(this.prefixValues)) {\n jobWithPrefixes[key] = value;\n }\n\n const tx = db.transaction(this.tableName, \"readwrite\");\n const store = tx.objectStore(this.tableName);\n\n return new Promise((resolve, reject) => {\n const request = store.add(jobWithPrefixes);\n\n // Don't resolve until transaction is complete\n tx.oncomplete = () => resolve(jobWithPrefixes.id);\n tx.onerror = () => reject(tx.error);\n request.onerror = () => reject(request.error);\n });\n }\n\n /**\n * Retrieves a job from the queue by its id.\n * @param id - The id of the job to retrieve.\n * @returns A promise that resolves to the job or undefined if the job is not found.\n */\n async get(id: unknown): Promise<JobStorageFormat<Input, Output> | undefined> {\n const db = await this.getDb();\n const tx = db.transaction(this.tableName, \"readonly\");\n const store = tx.objectStore(this.tableName);\n const request = store.get(id as string);\n return new Promise((resolve, reject) => {\n request.onsuccess = () => {\n const job = request.result as\n | (JobStorageFormat<Input, Output> & Record<string, unknown>)\n | undefined;\n // Filter by queue name and prefix values to ensure job belongs to this queue\n if (job && job.queue === this.queueName && this.matchesPrefixes(job)) {\n resolve(job);\n } else {\n resolve(undefined);\n }\n };\n request.onerror = () => reject(request.error);\n tx.onerror = () => reject(tx.error);\n });\n }\n\n /**\n * Retrieves a slice of jobs from the queue.\n * @param status - The status of the jobs to retrieve.\n * @param num - The number of jobs to retrieve.\n * @returns A promise that resolves to an array of jobs.\n */\n public async peek(\n status: JobStatus = JobStatus.PENDING,\n num: number = 100\n ): Promise<JobStorageFormat<Input, Output>[]> {\n const db = await this.getDb();\n const tx = db.transaction(this.tableName, \"readonly\");\n const store = tx.objectStore(this.tableName);\n const index = store.index(\"queue_status_run_after\");\n const prefixKeyValues = this.getPrefixKeyValues();\n\n return new Promise((resolve, reject) => {\n const ret = new Map<unknown, JobStorageFormat<Input, Output>>();\n // Create a key range for the compound index: from [prefixes..., queue, status, \"\"] to [prefixes..., queue, status, \"\\uffff\"]\n const keyRange = IDBKeyRange.bound(\n [...prefixKeyValues, this.queueName, status, \"\"],\n [...prefixKeyValues, this.queueName, status, \"\\uffff\"]\n );\n const cursorRequest = index.openCursor(keyRange);\n\n const handleCursor = (e: Event) => {\n const cursor = (e.target as IDBRequest<IDBCursorWithValue>).result;\n if (!cursor || ret.size >= num) {\n resolve(Array.from(ret.values()));\n return;\n }\n const job = cursor.value as JobStorageFormat<Input, Output> & Record<string, unknown>;\n // Verify prefix match and use Map to ensure no duplicates by job ID\n if (this.matchesPrefixes(job)) {\n ret.set(cursor.value.id, cursor.value);\n }\n cursor.continue();\n };\n\n cursorRequest.onsuccess = handleCursor;\n cursorRequest.onerror = () => reject(cursorRequest.error);\n tx.onerror = () => reject(tx.error);\n });\n }\n\n /**\n * Retrieves the next job from the queue.\n * @param workerId - Optional worker ID to associate with the job\n * @returns A promise that resolves to the next job or undefined if the queue is empty.\n */\n public async next(workerId?: string): Promise<JobStorageFormat<Input, Output> | undefined> {\n const db = await this.getDb();\n const tx = db.transaction(this.tableName, \"readwrite\");\n const store = tx.objectStore(this.tableName);\n const index = store.index(\"queue_status_run_after\");\n const now = new Date().toISOString();\n const prefixKeyValues = this.getPrefixKeyValues();\n\n return new Promise((resolve, reject) => {\n const cursorRequest = index.openCursor(\n IDBKeyRange.bound(\n [...prefixKeyValues, this.queueName, JobStatus.PENDING, \"\"],\n [...prefixKeyValues, this.queueName, JobStatus.PENDING, now],\n false,\n true\n )\n );\n\n let jobToReturn: JobStorageFormat<Input, Output> | undefined;\n\n cursorRequest.onsuccess = (e) => {\n const cursor = (e.target as IDBRequest<IDBCursorWithValue>).result;\n if (!cursor) {\n if (jobToReturn) {\n resolve(jobToReturn);\n } else {\n resolve(undefined);\n }\n return;\n }\n\n const job = cursor.value as JobStorageFormat<Input, Output> & Record<string, unknown>;\n // Verify the job belongs to this queue, matches prefixes, and is still in PENDING state\n if (\n job.queue !== this.queueName ||\n job.status !== JobStatus.PENDING ||\n !this.matchesPrefixes(job)\n ) {\n cursor.continue();\n return;\n }\n\n job.status = JobStatus.PROCESSING;\n job.last_ran_at = now;\n job.worker_id = workerId ?? null;\n\n try {\n const updateRequest = store.put(job);\n updateRequest.onsuccess = () => {\n jobToReturn = job;\n // Don't resolve here - wait for transaction to complete\n };\n updateRequest.onerror = (err) => {\n console.error(\"Failed to update job status:\", err);\n cursor.continue();\n };\n } catch (err) {\n console.error(\"Error updating job:\", err);\n cursor.continue();\n }\n };\n\n cursorRequest.onerror = () => reject(cursorRequest.error);\n\n // Wait for transaction to complete before resolving\n tx.oncomplete = () => {\n resolve(jobToReturn);\n };\n tx.onerror = () => reject(tx.error);\n });\n }\n\n /**\n * Retrieves the number of jobs in the queue.\n * Returns the count of jobs in the queue.\n */\n public async size(status = JobStatus.PENDING): Promise<number> {\n const db = await this.getDb();\n const prefixKeyValues = this.getPrefixKeyValues();\n return new Promise((resolve, reject) => {\n const tx = db.transaction(this.tableName, \"readonly\");\n const store = tx.objectStore(this.tableName);\n const index = store.index(\"queue_status\");\n const keyRange = IDBKeyRange.only([...prefixKeyValues, this.queueName, status]);\n const request = index.count(keyRange);\n\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error);\n tx.onerror = () => reject(tx.error);\n });\n }\n\n /**\n * Marks a job as complete with its output or error.\n */\n public async complete(job: JobStorageFormat<Input, Output>): Promise<void> {\n const db = await this.getDb();\n const tx = db.transaction(this.tableName, \"readwrite\");\n const store = tx.objectStore(this.tableName);\n\n return new Promise((resolve, reject) => {\n const getReq = store.get(job.id as string);\n getReq.onsuccess = () => {\n const existing = getReq.result as\n | (JobStorageFormat<Input, Output> & Record<string, unknown>)\n | undefined;\n // Verify job belongs to this queue and matches prefixes\n if (!existing || existing.queue !== this.queueName || !this.matchesPrefixes(existing)) {\n reject(\n new Error(`Job ${job.id} not found or does not belong to queue ${this.queueName}`)\n );\n return;\n }\n const currentAttempts = existing.run_attempts ?? 0;\n job.run_attempts = currentAttempts + 1;\n // Ensure queue is set correctly\n job.queue = this.queueName;\n\n // Ensure prefix values are preserved\n const jobWithPrefixes = job as JobStorageFormat<Input, Output> & Record<string, unknown>;\n for (const [key, value] of Object.entries(this.prefixValues)) {\n jobWithPrefixes[key] = value;\n }\n\n const putReq = store.put(jobWithPrefixes);\n putReq.onsuccess = () => {};\n putReq.onerror = () => reject(putReq.error);\n };\n getReq.onerror = () => reject(getReq.error);\n\n // Don't resolve until transaction is complete\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n });\n }\n\n /**\n * Aborts a job in the queue.\n */\n public async abort(id: unknown): Promise<void> {\n const job = await this.get(id);\n if (!job) return;\n\n job.status = JobStatus.ABORTING;\n await this.complete(job);\n }\n\n /**\n * Gets jobs by their run ID.\n */\n public async getByRunId(job_run_id: string): Promise<JobStorageFormat<Input, Output>[]> {\n const db = await this.getDb();\n const tx = db.transaction(this.tableName, \"readonly\");\n const store = tx.objectStore(this.tableName);\n const index = store.index(\"queue_job_run_id\");\n const prefixKeyValues = this.getPrefixKeyValues();\n const keyRange = IDBKeyRange.only([...prefixKeyValues, this.queueName, job_run_id]);\n const request = index.getAll(keyRange);\n\n return new Promise((resolve, reject) => {\n request.onsuccess = () => {\n // Filter results to ensure they match prefixes\n const results = (request.result || []).filter(\n (job: JobStorageFormat<Input, Output> & Record<string, unknown>) =>\n this.matchesPrefixes(job)\n );\n resolve(results);\n };\n request.onerror = () => reject(request.error);\n tx.onerror = () => reject(tx.error);\n });\n }\n\n /**\n * Deletes all jobs from the queue.\n */\n public async deleteAll(): Promise<void> {\n const db = await this.getDb();\n const tx = db.transaction(this.tableName, \"readwrite\");\n const store = tx.objectStore(this.tableName);\n const index = store.index(\"queue_status\");\n const prefixKeyValues = this.getPrefixKeyValues();\n\n return new Promise((resolve, reject) => {\n // Use a cursor to iterate through all jobs for this queue with prefix\n const keyRange = IDBKeyRange.bound(\n [...prefixKeyValues, this.queueName, \"\"],\n [...prefixKeyValues, this.queueName, \"\\uffff\"]\n );\n const request = index.openCursor(keyRange);\n\n request.onsuccess = (event) => {\n const cursor = (event.target as IDBRequest<IDBCursorWithValue>).result;\n if (cursor) {\n const job = cursor.value as JobStorageFormat<Input, Output> & Record<string, unknown>;\n // Verify job belongs to this queue and matches prefixes before deleting\n if (job.queue === this.queueName && this.matchesPrefixes(job)) {\n const deleteRequest = cursor.delete();\n deleteRequest.onsuccess = () => {\n cursor.continue();\n };\n deleteRequest.onerror = () => {\n // Continue even if delete fails\n cursor.continue();\n };\n } else {\n cursor.continue();\n }\n }\n };\n\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n request.onerror = () => reject(request.error);\n });\n }\n\n /**\n * Gets the output for a given input.\n */\n public async outputForInput(input: Input): Promise<Output | null> {\n const fingerprint = await makeFingerprint(input);\n const db = await this.getDb();\n const tx = db.transaction(this.tableName, \"readonly\");\n const store = tx.objectStore(this.tableName);\n const index = store.index(\"queue_fingerprint_status\");\n const prefixKeyValues = this.getPrefixKeyValues();\n const request = index.get([\n ...prefixKeyValues,\n this.queueName,\n fingerprint,\n JobStatus.COMPLETED,\n ]);\n\n return new Promise((resolve, reject) => {\n request.onsuccess = () => {\n const job = request.result as\n | (JobStorageFormat<Input, Output> & Record<string, unknown>)\n | undefined;\n if (job && this.matchesPrefixes(job)) {\n resolve(job.output ?? null);\n } else {\n resolve(null);\n }\n };\n request.onerror = () => reject(request.error);\n tx.onerror = () => reject(tx.error);\n });\n }\n\n /**\n * Saves progress updates for a job.\n */\n public async saveProgress(\n id: unknown,\n progress: number,\n message: string,\n details: Record<string, any> | null\n ): Promise<void> {\n const job = await this.get(id);\n if (!job) throw new Error(`Job ${id} not found`);\n\n job.progress = progress;\n job.progress_message = message;\n job.progress_details = details;\n\n await this.complete(job);\n }\n\n /**\n * Deletes a job by its ID.\n */\n public async delete(id: unknown): Promise<void> {\n const job = await this.get(id);\n if (!job) return;\n\n const db = await this.getDb();\n const tx = db.transaction(this.tableName, \"readwrite\");\n const store = tx.objectStore(this.tableName);\n const request = store.delete(id as string);\n\n return new Promise((resolve, reject) => {\n request.onsuccess = () => resolve();\n request.onerror = () => reject(request.error);\n tx.onerror = () => reject(tx.error);\n });\n }\n\n /**\n * Delete jobs with a specific status older than a cutoff date\n * @param status - Status of jobs to delete\n * @param olderThanMs - Delete jobs completed more than this many milliseconds ago\n */\n public async deleteJobsByStatusAndAge(status: JobStatus, olderThanMs: number): Promise<void> {\n const db = await this.getDb();\n const tx = db.transaction(this.tableName, \"readwrite\");\n const store = tx.objectStore(this.tableName);\n const index = store.index(\"queue_status\");\n const cutoffDate = new Date(Date.now() - olderThanMs).toISOString();\n const prefixKeyValues = this.getPrefixKeyValues();\n const keyRange = IDBKeyRange.only([...prefixKeyValues, this.queueName, status]);\n\n return new Promise((resolve, reject) => {\n const request = index.openCursor(keyRange);\n\n request.onsuccess = (event) => {\n const cursor = (event.target as IDBRequest<IDBCursorWithValue>).result;\n if (cursor) {\n const job = cursor.value as JobStorageFormat<Input, Output> & Record<string, unknown>;\n // Verify job belongs to this queue, matches prefixes, and matches criteria\n if (\n job.queue === this.queueName &&\n this.matchesPrefixes(job) &&\n job.status === status &&\n job.completed_at &&\n job.completed_at <= cutoffDate\n ) {\n cursor.delete();\n }\n cursor.continue();\n }\n };\n\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n request.onerror = () => reject(request.error);\n });\n }\n\n /**\n * Gets all jobs from the queue that match the current prefix values.\n * Used internally for normal polling-based subscriptions (efficient - filters at DB level).\n *\n * @returns A promise that resolves to an array of jobs\n */\n private async getAllJobs(): Promise<Array<JobStorageFormat<Input, Output>>> {\n const db = await this.getDb();\n const tx = db.transaction(this.tableName, \"readonly\");\n const store = tx.objectStore(this.tableName);\n const index = store.index(\"queue_status\");\n const prefixKeyValues = this.getPrefixKeyValues();\n\n return new Promise((resolve, reject) => {\n const jobs: Array<JobStorageFormat<Input, Output>> = [];\n // Use a key range that covers all statuses for this queue with prefixes\n const keyRange = IDBKeyRange.bound(\n [...prefixKeyValues, this.queueName, \"\"],\n [...prefixKeyValues, this.queueName, \"\\uffff\"]\n );\n const request = index.openCursor(keyRange);\n\n request.onsuccess = (event) => {\n const cursor = (event.target as IDBRequest<IDBCursorWithValue>).result;\n if (cursor) {\n const job = cursor.value as JobStorageFormat<Input, Output> & Record<string, unknown>;\n if (job.queue === this.queueName && this.matchesPrefixes(job)) {\n jobs.push(job);\n }\n cursor.continue();\n }\n };\n\n tx.oncomplete = () => resolve(jobs);\n tx.onerror = () => reject(tx.error);\n request.onerror = () => reject(request.error);\n });\n }\n\n /**\n * Gets all jobs from the queue with a custom prefix filter.\n * Used for subscriptions with custom prefix filters (filters at DB level where possible).\n *\n * @param prefixFilter - The prefix values to filter by (empty object = all jobs)\n * @returns A promise that resolves to an array of jobs\n */\n private async getAllJobsWithFilter(\n prefixFilter: Readonly<Record<string, string | number>>\n ): Promise<Array<JobStorageFormat<Input, Output>>> {\n const db = await this.getDb();\n const tx = db.transaction(this.tableName, \"readonly\");\n const store = tx.objectStore(this.tableName);\n\n return new Promise((resolve, reject) => {\n const jobs: Array<JobStorageFormat<Input, Output>> = [];\n const request = store.openCursor();\n\n request.onsuccess = (event) => {\n const cursor = (event.target as IDBRequest<IDBCursorWithValue>).result;\n if (cursor) {\n const job = cursor.value as JobStorageFormat<Input, Output> & Record<string, unknown>;\n // Filter by queue name\n if (job.queue !== this.queueName) {\n cursor.continue();\n return;\n }\n // If empty filter, include all jobs for this queue\n if (Object.keys(prefixFilter).length === 0) {\n jobs.push(job);\n } else {\n // Check each filter value\n let matches = true;\n for (const [key, value] of Object.entries(prefixFilter)) {\n if (job[key] !== value) {\n matches = false;\n break;\n }\n }\n if (matches) {\n jobs.push(job);\n }\n }\n cursor.continue();\n }\n };\n\n tx.oncomplete = () => resolve(jobs);\n tx.onerror = () => reject(tx.error);\n request.onerror = () => reject(request.error);\n });\n }\n\n /**\n * Checks if a prefix filter is custom (different from instance's prefixes).\n */\n private isCustomPrefixFilter(prefixFilter?: Readonly<Record<string, string | number>>): boolean {\n // No filter specified - use instance prefixes (not custom)\n if (prefixFilter === undefined) {\n return false;\n }\n // Empty filter - receive all (custom)\n if (Object.keys(prefixFilter).length === 0) {\n return true;\n }\n // Check if filter matches instance prefixes exactly\n const instanceKeys = Object.keys(this.prefixValues);\n const filterKeys = Object.keys(prefixFilter);\n if (instanceKeys.length !== filterKeys.length) {\n return true; // Different number of keys = custom\n }\n for (const key of instanceKeys) {\n if (this.prefixValues[key] !== prefixFilter[key]) {\n return true; // Different value = custom\n }\n }\n return false; // Matches instance prefixes exactly\n }\n\n /**\n * Gets or creates the shared polling subscription manager for normal subscriptions.\n * This ensures all normal subscriptions share a single polling loop per interval.\n */\n private getPollingManager(): PollingSubscriptionManager<\n JobStorageFormat<Input, Output>,\n unknown,\n QueueChangePayload<Input, Output>\n > {\n if (!this.pollingManager) {\n this.pollingManager = new PollingSubscriptionManager<\n JobStorageFormat<Input, Output>,\n unknown,\n QueueChangePayload<Input, Output>\n >(\n async () => {\n // Fetch jobs with instance's prefix filter (efficient DB-level filtering)\n const jobs = await this.getAllJobs();\n return new Map(jobs.map((j) => [j.id, j]));\n },\n (a, b) => JSON.stringify(a) === JSON.stringify(b),\n {\n insert: (item) => ({ type: \"INSERT\" as const, new: item }),\n update: (oldItem, newItem) => ({ type: \"UPDATE\" as const, old: oldItem, new: newItem }),\n delete: (item) => ({ type: \"DELETE\" as const, old: item }),\n }\n );\n }\n return this.pollingManager;\n }\n\n /**\n * Creates a dedicated polling subscription for custom prefix filters.\n * This runs separately from the normal polling manager.\n */\n private subscribeWithCustomPrefixFilter(\n callback: (change: QueueChangePayload<Input, Output>) => void,\n prefixFilter: Readonly<Record<string, string | number>>,\n intervalMs: number\n ): () => void {\n let lastKnownJobs = new Map<unknown, JobStorageFormat<Input, Output>>();\n let cancelled = false;\n\n const poll = async () => {\n if (cancelled) return;\n try {\n const currentJobs = await this.getAllJobsWithFilter(prefixFilter);\n if (cancelled) return;\n const currentMap = new Map(currentJobs.map((j) => [j.id, j]));\n\n // Detect changes\n for (const [id, job] of currentMap) {\n const old = lastKnownJobs.get(id);\n if (!old) {\n callback({ type: \"INSERT\", new: job });\n } else if (JSON.stringify(old) !== JSON.stringify(job)) {\n callback({ type: \"UPDATE\", old, new: job });\n }\n }\n\n for (const [id, job] of lastKnownJobs) {\n if (!currentMap.has(id)) {\n callback({ type: \"DELETE\", old: job });\n }\n }\n\n lastKnownJobs = currentMap;\n } catch {\n // Ignore polling errors\n }\n };\n\n const intervalId = setInterval(poll, intervalMs);\n poll(); // Initial poll\n\n return () => {\n cancelled = true;\n clearInterval(intervalId);\n };\n }\n\n /**\n * Subscribes to changes in the queue.\n * Uses polling since IndexedDB has no native cross-tab change notifications.\n *\n * Normal subscriptions (no custom prefix filter) share a single polling loop for efficiency.\n * Custom prefix filter subscriptions get their own dedicated polling loop with DB-level filtering.\n *\n * @param callback - Function called when a change occurs\n * @param options - Subscription options including polling interval and prefix filter\n * @returns Unsubscribe function\n */\n public subscribeToChanges(\n callback: (change: QueueChangePayload<Input, Output>) => void,\n options?: QueueSubscribeOptions\n ): () => void {\n const intervalMs = options?.pollingIntervalMs ?? 1000;\n\n // Check if this is a custom prefix filter subscription\n if (this.isCustomPrefixFilter(options?.prefixFilter)) {\n // Custom prefix filter - use dedicated polling with DB-level filtering\n return this.subscribeWithCustomPrefixFilter(callback, options!.prefixFilter!, intervalMs);\n }\n\n // Normal subscription - use shared polling manager (efficient)\n const manager = this.getPollingManager();\n return manager.subscribe(callback, { intervalMs });\n }\n}\n",
24
+ "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Configuration options for the polling subscription manager\n */\nexport interface PollingManagerOptions {\n /** Default polling interval in milliseconds */\n readonly defaultIntervalMs?: number;\n}\n\n/**\n * A callback function that is called when changes are detected\n */\nexport type ChangeCallback<T> = (change: T) => void;\n\n/**\n * A function that fetches the current state for comparison\n */\nexport type StateFetcher<Item, Key> = () => Promise<Map<Key, Item>>;\n\n/**\n * A function that compares two items for equality\n */\nexport type ItemComparator<Item> = (a: Item, b: Item) => boolean;\n\n/**\n * A factory function that creates change payloads\n */\nexport interface ChangePayloadFactory<Item, ChangePayload> {\n /** Create an INSERT change payload */\n readonly insert: (item: Item) => ChangePayload;\n /** Create an UPDATE change payload */\n readonly update: (oldItem: Item, newItem: Item) => ChangePayload;\n /** Create a DELETE change payload */\n readonly delete: (item: Item) => ChangePayload;\n}\n\n/**\n * Options for subscribing to changes\n */\nexport interface PollingSubscriptionOptions {\n /** Polling interval in milliseconds */\n readonly intervalMs?: number;\n}\n\n/**\n * Internal subscription record\n */\ninterface Subscription<ChangePayload> {\n readonly callback: ChangeCallback<ChangePayload>;\n readonly intervalMs: number;\n}\n\n/**\n * Manages polling-based subscriptions efficiently by consolidating multiple\n * subscribers into a single polling loop per interval tier.\n *\n * Instead of each subscription creating its own polling interval, this manager\n * groups subscriptions by their requested polling interval and runs a single\n * poll for each group, broadcasting changes to all subscribers in that group.\n *\n * @template Item - The type of items being tracked\n * @template Key - The type of key used to identify items\n * @template ChangePayload - The type of change payload sent to subscribers\n */\nexport class PollingSubscriptionManager<Item, Key, ChangePayload> {\n /** Map of interval (ms) to interval ID and subscriber list */\n private readonly intervals = new Map<\n number,\n {\n intervalId: ReturnType<typeof setInterval>;\n subscribers: Set<Subscription<ChangePayload>>;\n }\n >();\n\n /** Current known state from last poll */\n private lastKnownState = new Map<Key, Item>();\n\n /** Whether the manager has been initialized with a state fetch */\n private initialized = false;\n\n /** Function to fetch current state */\n private readonly fetchState: StateFetcher<Item, Key>;\n\n /** Function to compare items for equality */\n private readonly compareItems: ItemComparator<Item>;\n\n /** Factory for creating change payloads */\n private readonly payloadFactory: ChangePayloadFactory<Item, ChangePayload>;\n\n /** Default polling interval */\n private readonly defaultIntervalMs: number;\n\n /**\n * Creates a new PollingSubscriptionManager\n *\n * @param fetchState - Function that returns the current state as a Map\n * @param compareItems - Function that compares two items for equality\n * @param payloadFactory - Factory for creating INSERT/UPDATE/DELETE payloads\n * @param options - Configuration options\n */\n constructor(\n fetchState: StateFetcher<Item, Key>,\n compareItems: ItemComparator<Item>,\n payloadFactory: ChangePayloadFactory<Item, ChangePayload>,\n options?: PollingManagerOptions\n ) {\n this.fetchState = fetchState;\n this.compareItems = compareItems;\n this.payloadFactory = payloadFactory;\n this.defaultIntervalMs = options?.defaultIntervalMs ?? 1000;\n }\n\n /**\n * Subscribe to changes with a specific polling interval\n *\n * @param callback - Function called when changes are detected\n * @param options - Subscription options including interval\n * @returns Unsubscribe function\n */\n subscribe(\n callback: ChangeCallback<ChangePayload>,\n options?: PollingSubscriptionOptions\n ): () => void {\n const interval = options?.intervalMs ?? this.defaultIntervalMs;\n const subscription: Subscription<ChangePayload> = {\n callback,\n intervalMs: interval,\n };\n\n // Get or create interval group\n let intervalGroup = this.intervals.get(interval);\n if (!intervalGroup) {\n // First subscriber for this interval - create the polling loop\n const subscribers = new Set<Subscription<ChangePayload>>();\n const intervalId = setInterval(() => this.poll(subscribers), interval);\n\n intervalGroup = { intervalId, subscribers };\n this.intervals.set(interval, intervalGroup);\n\n // Run initial poll if this is the first subscriber ever\n if (!this.initialized) {\n this.initialized = true;\n this.initAndPoll(subscribers, subscription);\n } else {\n // Run immediate poll for new subscriber\n this.pollForNewSubscriber(subscription);\n }\n } else {\n // New subscriber joining existing interval - send them current state\n this.pollForNewSubscriber(subscription);\n }\n\n intervalGroup.subscribers.add(subscription);\n\n // Return unsubscribe function\n return () => {\n const group = this.intervals.get(interval);\n if (group) {\n group.subscribers.delete(subscription);\n\n // If no more subscribers for this interval, clean up\n if (group.subscribers.size === 0) {\n clearInterval(group.intervalId);\n this.intervals.delete(interval);\n }\n }\n };\n }\n\n /**\n * Initialize state and run first poll\n */\n private async initAndPoll(\n subscribers: Set<Subscription<ChangePayload>>,\n newSubscription: Subscription<ChangePayload>\n ): Promise<void> {\n try {\n this.lastKnownState = await this.fetchState();\n // Notify the new subscriber of initial state as INSERTs\n for (const [, item] of this.lastKnownState) {\n const payload = this.payloadFactory.insert(item);\n try {\n newSubscription.callback(payload);\n } catch {\n // Ignore callback errors\n }\n }\n } catch {\n // Ignore fetch errors during initialization\n }\n }\n\n /**\n * Send current state to a new subscriber\n */\n private pollForNewSubscriber(subscription: Subscription<ChangePayload>): void {\n // Send current state as INSERTs to the new subscriber\n for (const [, item] of this.lastKnownState) {\n const payload = this.payloadFactory.insert(item);\n try {\n subscription.callback(payload);\n } catch {\n // Ignore callback errors\n }\n }\n }\n\n /**\n * Poll for changes and notify all subscribers in the given set\n */\n private async poll(subscribers: Set<Subscription<ChangePayload>>): Promise<void> {\n if (subscribers.size === 0) return;\n\n try {\n const currentState = await this.fetchState();\n const changes: ChangePayload[] = [];\n\n // Detect new and updated items\n for (const [key, item] of currentState) {\n const oldItem = this.lastKnownState.get(key);\n if (!oldItem) {\n changes.push(this.payloadFactory.insert(item));\n } else if (!this.compareItems(oldItem, item)) {\n changes.push(this.payloadFactory.update(oldItem, item));\n }\n }\n\n // Detect deleted items\n for (const [key, item] of this.lastKnownState) {\n if (!currentState.has(key)) {\n changes.push(this.payloadFactory.delete(item));\n }\n }\n\n // Update state\n this.lastKnownState = currentState;\n\n // Broadcast changes to all subscribers\n for (const change of changes) {\n for (const sub of subscribers) {\n try {\n sub.callback(change);\n } catch {\n // Ignore callback errors\n }\n }\n }\n } catch {\n // Ignore polling errors\n }\n }\n\n /**\n * Get the number of active subscriptions across all intervals\n */\n get subscriptionCount(): number {\n let count = 0;\n for (const group of this.intervals.values()) {\n count += group.subscribers.size;\n }\n return count;\n }\n\n /**\n * Check if there are any active subscriptions\n */\n get hasSubscriptions(): boolean {\n return this.intervals.size > 0;\n }\n\n /**\n * Destroy the manager and clean up all intervals\n */\n destroy(): void {\n for (const group of this.intervals.values()) {\n clearInterval(group.intervalId);\n }\n this.intervals.clear();\n this.lastKnownState.clear();\n this.initialized = false;\n }\n}\n",
25
+ "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type { RealtimeChannel, SupabaseClient } from \"@supabase/supabase-js\";\nimport { createServiceToken, makeFingerprint, uuid4 } from \"@workglow/util\";\nimport { PollingSubscriptionManager } from \"../util/PollingSubscriptionManager\";\nimport {\n IQueueStorage,\n JobStatus,\n JobStorageFormat,\n PrefixColumn,\n QueueChangePayload,\n QueueChangeType,\n QueueStorageOptions,\n QueueSubscribeOptions,\n} from \"./IQueueStorage\";\n\nexport const SUPABASE_QUEUE_STORAGE = createServiceToken<IQueueStorage<any, any>>(\n \"jobqueue.storage.supabase\"\n);\n\n/**\n * Supabase implementation of a job queue.\n * Provides storage and retrieval for job execution states using Supabase.\n */\nexport class SupabaseQueueStorage<Input, Output> implements IQueueStorage<Input, Output> {\n /** The prefix column definitions */\n protected readonly prefixes: readonly PrefixColumn[];\n /** The prefix values for filtering */\n protected readonly prefixValues: Readonly<Record<string, string | number>>;\n /** The table name for the job queue */\n protected readonly tableName: string;\n /** Realtime channel for subscriptions */\n private realtimeChannel: RealtimeChannel | null = null;\n /** Shared polling subscription manager (fallback) */\n private pollingManager: PollingSubscriptionManager<\n JobStorageFormat<Input, Output>,\n unknown,\n QueueChangePayload<Input, Output>\n > | null = null;\n\n constructor(\n protected readonly client: SupabaseClient,\n protected readonly queueName: string,\n options?: QueueStorageOptions\n ) {\n this.prefixes = options?.prefixes ?? [];\n this.prefixValues = options?.prefixValues ?? {};\n // Generate table name based on prefix configuration to avoid column conflicts\n if (this.prefixes.length > 0) {\n const prefixNames = this.prefixes.map((p) => p.name).join(\"_\");\n this.tableName = `job_queue_${prefixNames}`;\n } else {\n this.tableName = \"job_queue\";\n }\n }\n\n /**\n * Gets the SQL column type for a prefix column (Supabase supports UUID natively)\n */\n private getPrefixColumnType(type: PrefixColumn[\"type\"]): string {\n return type === \"uuid\" ? \"UUID\" : \"INTEGER\";\n }\n\n /**\n * Builds the prefix columns SQL for CREATE TABLE\n */\n private buildPrefixColumnsSql(): string {\n if (this.prefixes.length === 0) return \"\";\n return (\n this.prefixes\n .map((p) => `${p.name} ${this.getPrefixColumnType(p.type)} NOT NULL`)\n .join(\",\\n \") + \",\\n \"\n );\n }\n\n /**\n * Builds prefix column names for use in queries\n */\n private getPrefixColumnNames(): string[] {\n return this.prefixes.map((p) => p.name);\n }\n\n /**\n * Applies prefix filters to a Supabase query builder\n */\n private applyPrefixFilters<T>(query: T): T {\n let result = query as any;\n for (const prefix of this.prefixes) {\n result = result.eq(prefix.name, this.prefixValues[prefix.name]);\n }\n return result as T;\n }\n\n /**\n * Gets prefix values as an object for inserts\n */\n private getPrefixInsertValues(): Record<string, string | number> {\n const values: Record<string, string | number> = {};\n for (const prefix of this.prefixes) {\n values[prefix.name] = this.prefixValues[prefix.name];\n }\n return values;\n }\n\n public async setupDatabase(): Promise<void> {\n // Note: For Supabase, table creation should typically be done through migrations\n // This setup assumes the table already exists or uses exec_sql RPC function\n const createTypeSql = `CREATE TYPE job_status AS ENUM (${Object.values(JobStatus)\n .map((v) => `'${v}'`)\n .join(\",\")})`;\n\n const { error: typeError } = await this.client.rpc(\"exec_sql\", { query: createTypeSql });\n // Ignore error if type already exists (code 42710)\n if (typeError && typeError.code !== \"42710\") {\n throw typeError;\n }\n\n const prefixColumnsSql = this.buildPrefixColumnsSql();\n const prefixColumnNames = this.getPrefixColumnNames();\n const prefixIndexPrefix =\n prefixColumnNames.length > 0 ? prefixColumnNames.join(\", \") + \", \" : \"\";\n const indexSuffix = prefixColumnNames.length > 0 ? \"_\" + prefixColumnNames.join(\"_\") : \"\";\n\n const createTableSql = `\n CREATE TABLE IF NOT EXISTS ${this.tableName} (\n id SERIAL NOT NULL,\n ${prefixColumnsSql}fingerprint text NOT NULL,\n queue text NOT NULL,\n job_run_id text NOT NULL,\n status job_status NOT NULL default 'PENDING',\n input jsonb NOT NULL,\n output jsonb,\n run_attempts integer default 0,\n max_retries integer default 20,\n run_after timestamp with time zone DEFAULT now(),\n last_ran_at timestamp with time zone,\n created_at timestamp with time zone DEFAULT now(),\n deadline_at timestamp with time zone,\n completed_at timestamp with time zone,\n error text,\n error_code text,\n progress real DEFAULT 0,\n progress_message text DEFAULT '',\n progress_details jsonb,\n worker_id text\n )`;\n\n const { error: tableError } = await this.client.rpc(\"exec_sql\", { query: createTableSql });\n if (tableError) {\n // Ignore error if table already exists (code 42P07)\n if (tableError.code !== \"42P07\") {\n throw tableError;\n }\n }\n\n // Create indexes with prefix columns prepended\n const indexes = [\n `CREATE INDEX IF NOT EXISTS job_fetcher${indexSuffix}_idx ON ${this.tableName} (${prefixIndexPrefix}id, status, run_after)`,\n `CREATE INDEX IF NOT EXISTS job_queue_fetcher${indexSuffix}_idx ON ${this.tableName} (${prefixIndexPrefix}queue, status, run_after)`,\n `CREATE INDEX IF NOT EXISTS jobs_fingerprint${indexSuffix}_unique_idx ON ${this.tableName} (${prefixIndexPrefix}queue, fingerprint, status)`,\n ];\n\n for (const indexSql of indexes) {\n await this.client.rpc(\"exec_sql\", { query: indexSql });\n // Ignore index creation errors\n }\n }\n\n /**\n * Adds a new job to the queue.\n * @param job - The job to add\n * @returns The ID of the added job\n */\n public async add(job: JobStorageFormat<Input, Output>): Promise<unknown> {\n const now = new Date().toISOString();\n job.queue = this.queueName;\n job.job_run_id = job.job_run_id ?? uuid4();\n job.fingerprint = await makeFingerprint(job.input);\n job.status = JobStatus.PENDING;\n job.progress = 0;\n job.progress_message = \"\";\n job.progress_details = null;\n job.created_at = now;\n job.run_after = now;\n\n const prefixInsertValues = this.getPrefixInsertValues();\n\n const { data, error } = await this.client\n .from(this.tableName)\n .insert({\n ...prefixInsertValues,\n queue: job.queue,\n fingerprint: job.fingerprint,\n input: job.input,\n run_after: job.run_after,\n created_at: job.created_at,\n deadline_at: job.deadline_at,\n max_retries: job.max_retries,\n job_run_id: job.job_run_id,\n progress: job.progress,\n progress_message: job.progress_message,\n progress_details: job.progress_details,\n })\n .select(\"id\")\n .single();\n\n if (error) throw error;\n if (!data) throw new Error(\"Failed to add to queue\");\n\n job.id = data.id;\n return job.id;\n }\n\n /**\n * Retrieves a job by its ID.\n * @param id - The ID of the job to retrieve\n * @returns The job if found, undefined otherwise\n */\n public async get(id: unknown): Promise<JobStorageFormat<Input, Output> | undefined> {\n let query = this.client\n .from(this.tableName)\n .select(\"*\")\n .eq(\"id\", id)\n .eq(\"queue\", this.queueName);\n\n query = this.applyPrefixFilters(query);\n\n const { data, error } = await query.single();\n\n if (error) {\n if (error.code === \"PGRST116\") return undefined; // Not found\n throw error;\n }\n\n return data as JobStorageFormat<Input, Output> | undefined;\n }\n\n /**\n * Retrieves a slice of jobs from the queue.\n * @param status - The status to filter by\n * @param num - Maximum number of jobs to return\n * @returns An array of jobs\n */\n public async peek(\n status: JobStatus = JobStatus.PENDING,\n num: number = 100\n ): Promise<JobStorageFormat<Input, Output>[]> {\n num = Number(num) || 100;\n\n let query = this.client\n .from(this.tableName)\n .select(\"*\")\n .eq(\"queue\", this.queueName)\n .eq(\"status\", status);\n\n query = this.applyPrefixFilters(query);\n\n const { data, error } = await query.order(\"run_after\", { ascending: true }).limit(num);\n\n if (error) throw error;\n return (data as JobStorageFormat<Input, Output>[]) ?? [];\n }\n\n /**\n * Retrieves the next available job that is ready to be processed.\n * @param workerId - Optional worker ID to associate with the job\n * @returns The next job or undefined if no job is available\n */\n public async next(workerId?: string): Promise<JobStorageFormat<Input, Output> | undefined> {\n // First, find the next job\n let selectQuery = this.client\n .from(this.tableName)\n .select(\"*\")\n .eq(\"queue\", this.queueName)\n .eq(\"status\", JobStatus.PENDING)\n .lte(\"run_after\", new Date().toISOString());\n\n selectQuery = this.applyPrefixFilters(selectQuery);\n\n const { data: jobs, error: selectError } = await selectQuery\n .order(\"run_after\", { ascending: true })\n .limit(1);\n\n if (selectError) throw selectError;\n if (!jobs || jobs.length === 0) return undefined;\n\n const job = jobs[0];\n\n // Update its status\n let updateQuery = this.client\n .from(this.tableName)\n .update({\n status: JobStatus.PROCESSING,\n last_ran_at: new Date().toISOString(),\n worker_id: workerId ?? null,\n })\n .eq(\"id\", job.id)\n .eq(\"queue\", this.queueName);\n\n updateQuery = this.applyPrefixFilters(updateQuery);\n\n const { data: updatedJob, error: updateError } = await updateQuery.select().single();\n\n if (updateError) throw updateError;\n return updatedJob as JobStorageFormat<Input, Output>;\n }\n\n /**\n * Retrieves the number of jobs in the queue with a specific status.\n * @param status - The status of the jobs to count\n * @returns The count of jobs with the specified status\n */\n public async size(status = JobStatus.PENDING): Promise<number> {\n let query = this.client\n .from(this.tableName)\n .select(\"*\", { count: \"exact\", head: true })\n .eq(\"queue\", this.queueName)\n .eq(\"status\", status);\n\n query = this.applyPrefixFilters(query);\n\n const { count, error } = await query;\n\n if (error) throw error;\n return count ?? 0;\n }\n\n /**\n * Gets all jobs from the queue that match the current prefix values.\n * Used internally for polling-based subscriptions.\n *\n * @returns An array of jobs\n */\n private async getAllJobs(): Promise<Array<JobStorageFormat<Input, Output>>> {\n let query = this.client.from(this.tableName).select(\"*\").eq(\"queue\", this.queueName);\n\n query = this.applyPrefixFilters(query);\n\n const { data, error } = await query;\n\n if (error) throw error;\n return (data ?? []) as Array<JobStorageFormat<Input, Output>>;\n }\n\n /**\n * Marks a job as complete with its output or error.\n * Enhanced error handling:\n * - For a retryable error, increments run_attempts and updates run_after.\n * - Marks a job as FAILED immediately for permanent or generic errors.\n */\n public async complete(jobDetails: JobStorageFormat<Input, Output>): Promise<void> {\n const now = new Date().toISOString();\n\n // Handle disabled without changing attempts\n if (jobDetails.status === JobStatus.DISABLED) {\n let query = this.client\n .from(this.tableName)\n .update({\n status: jobDetails.status,\n progress: 100,\n progress_message: \"\",\n progress_details: null,\n completed_at: now,\n last_ran_at: now,\n })\n .eq(\"id\", jobDetails.id)\n .eq(\"queue\", this.queueName);\n query = this.applyPrefixFilters(query);\n const { error } = await query;\n if (error) throw error;\n return;\n }\n\n // Read current attempts to compute next value deterministically\n let getQuery = this.client\n .from(this.tableName)\n .select(\"run_attempts\")\n .eq(\"id\", jobDetails.id as number)\n .eq(\"queue\", this.queueName);\n getQuery = this.applyPrefixFilters(getQuery);\n const { data: current, error: getError } = await getQuery.single();\n if (getError) throw getError;\n const nextAttempts = ((current?.run_attempts as number | undefined) ?? 0) + 1;\n\n if (jobDetails.status === JobStatus.PENDING) {\n let query = this.client\n .from(this.tableName)\n .update({\n error: jobDetails.error ?? null,\n error_code: jobDetails.error_code ?? null,\n status: jobDetails.status,\n run_after: jobDetails.run_after!,\n progress: 0,\n progress_message: \"\",\n progress_details: null,\n run_attempts: nextAttempts,\n last_ran_at: now,\n })\n .eq(\"id\", jobDetails.id)\n .eq(\"queue\", this.queueName);\n query = this.applyPrefixFilters(query);\n const { error } = await query;\n if (error) throw error;\n return;\n }\n\n if (jobDetails.status === JobStatus.COMPLETED || jobDetails.status === JobStatus.FAILED) {\n let query = this.client\n .from(this.tableName)\n .update({\n output: jobDetails.output ?? null,\n error: jobDetails.error ?? null,\n error_code: jobDetails.error_code ?? null,\n status: jobDetails.status,\n progress: 100,\n progress_message: \"\",\n progress_details: null,\n run_attempts: nextAttempts,\n completed_at: now,\n last_ran_at: now,\n })\n .eq(\"id\", jobDetails.id)\n .eq(\"queue\", this.queueName);\n query = this.applyPrefixFilters(query);\n const { error } = await query;\n if (error) throw error;\n return;\n }\n\n // Transitional states: PROCESSING/ABORTING etc - increment attempts like other stores\n let query = this.client\n .from(this.tableName)\n .update({\n status: jobDetails.status,\n output: jobDetails.output ?? null,\n error: jobDetails.error ?? null,\n error_code: jobDetails.error_code ?? null,\n run_after: jobDetails.run_after ?? null,\n run_attempts: nextAttempts,\n last_ran_at: now,\n })\n .eq(\"id\", jobDetails.id)\n .eq(\"queue\", this.queueName);\n query = this.applyPrefixFilters(query);\n const { error } = await query;\n if (error) throw error;\n }\n\n /**\n * Clears all jobs from the queue.\n */\n public async deleteAll(): Promise<void> {\n let query = this.client.from(this.tableName).delete().eq(\"queue\", this.queueName);\n query = this.applyPrefixFilters(query);\n const { error } = await query;\n\n if (error) throw error;\n }\n\n /**\n * Looks up cached output for a given input\n * Uses input fingerprinting for efficient matching\n * @returns The cached output or null if not found\n */\n public async outputForInput(input: Input): Promise<Output | null> {\n const fingerprint = await makeFingerprint(input);\n\n let query = this.client\n .from(this.tableName)\n .select(\"output\")\n .eq(\"fingerprint\", fingerprint)\n .eq(\"queue\", this.queueName)\n .eq(\"status\", JobStatus.COMPLETED);\n\n query = this.applyPrefixFilters(query);\n\n const { data, error } = await query.single();\n\n if (error) {\n if (error.code === \"PGRST116\") return null; // Not found\n throw error;\n }\n\n return data?.output ?? null;\n }\n\n /**\n * Aborts a job by setting its status to \"ABORTING\".\n * This method will signal the corresponding AbortController so that\n * the job's execute() method (if it supports an AbortSignal parameter)\n * can clean up and exit.\n */\n public async abort(jobId: unknown): Promise<void> {\n let query = this.client\n .from(this.tableName)\n .update({ status: JobStatus.ABORTING })\n .eq(\"id\", jobId)\n .eq(\"queue\", this.queueName);\n\n query = this.applyPrefixFilters(query);\n const { error } = await query;\n\n if (error) throw error;\n }\n\n /**\n * Retrieves all jobs for a given job run ID.\n * @param job_run_id - The ID of the job run to retrieve\n * @returns An array of jobs\n */\n public async getByRunId(job_run_id: string): Promise<Array<JobStorageFormat<Input, Output>>> {\n let query = this.client\n .from(this.tableName)\n .select(\"*\")\n .eq(\"job_run_id\", job_run_id)\n .eq(\"queue\", this.queueName);\n\n query = this.applyPrefixFilters(query);\n const { data, error } = await query;\n\n if (error) throw error;\n return (data as Array<JobStorageFormat<Input, Output>>) ?? [];\n }\n\n /**\n * Implements the saveProgress method\n */\n public async saveProgress(\n jobId: unknown,\n progress: number,\n message: string,\n details: Record<string, any>\n ): Promise<void> {\n let query = this.client\n .from(this.tableName)\n .update({\n progress,\n progress_message: message,\n progress_details: details,\n })\n .eq(\"id\", jobId)\n .eq(\"queue\", this.queueName);\n\n query = this.applyPrefixFilters(query);\n const { error } = await query;\n\n if (error) throw error;\n }\n\n /**\n * Deletes a job by its ID\n */\n public async delete(jobId: unknown): Promise<void> {\n let query = this.client\n .from(this.tableName)\n .delete()\n .eq(\"id\", jobId)\n .eq(\"queue\", this.queueName);\n\n query = this.applyPrefixFilters(query);\n const { error } = await query;\n\n if (error) throw error;\n }\n\n /**\n * Delete jobs with a specific status older than a cutoff date\n * @param status - Status of jobs to delete\n * @param olderThanMs - Delete jobs completed more than this many milliseconds ago\n */\n public async deleteJobsByStatusAndAge(status: JobStatus, olderThanMs: number): Promise<void> {\n const cutoffDate = new Date(Date.now() - olderThanMs).toISOString();\n\n let query = this.client\n .from(this.tableName)\n .delete()\n .eq(\"queue\", this.queueName)\n .eq(\"status\", status)\n .not(\"completed_at\", \"is\", null)\n .lte(\"completed_at\", cutoffDate);\n\n query = this.applyPrefixFilters(query);\n const { error } = await query;\n\n if (error) throw error;\n }\n\n /**\n * Checks if a job from a realtime payload matches the specified prefix filter\n * @param job - The job record from the realtime payload\n * @param prefixFilter - The prefix filter to match against (undefined = use instance prefixes, {} = no filter)\n */\n private matchesPrefixFilter(\n job: Record<string, unknown> | undefined,\n prefixFilter?: Readonly<Record<string, string | number>>\n ): boolean {\n if (!job) return false;\n\n // Check queue name first\n if (job.queue !== this.queueName) {\n return false;\n }\n\n // If prefixFilter is explicitly an empty object, no prefix filtering\n if (prefixFilter && Object.keys(prefixFilter).length === 0) {\n return true;\n }\n\n // Use provided prefixFilter or fall back to instance's prefixValues\n const filterValues = prefixFilter ?? this.prefixValues;\n\n // If no filter values, match all\n if (Object.keys(filterValues).length === 0) {\n return true;\n }\n\n // Check each filter value\n for (const [key, value] of Object.entries(filterValues)) {\n if (job[key] !== value) {\n return false;\n }\n }\n return true;\n }\n\n /**\n * Checks if a prefix filter is custom (different from instance's prefixes).\n */\n private isCustomPrefixFilter(prefixFilter?: Readonly<Record<string, string | number>>): boolean {\n // No filter specified - use instance prefixes (not custom)\n if (prefixFilter === undefined) {\n return false;\n }\n // Empty filter - receive all (custom)\n if (Object.keys(prefixFilter).length === 0) {\n return true;\n }\n // Check if filter matches instance prefixes exactly\n const instanceKeys = Object.keys(this.prefixValues);\n const filterKeys = Object.keys(prefixFilter);\n if (instanceKeys.length !== filterKeys.length) {\n return true; // Different number of keys = custom\n }\n for (const key of instanceKeys) {\n if (this.prefixValues[key] !== prefixFilter[key]) {\n return true; // Different value = custom\n }\n }\n return false; // Matches instance prefixes exactly\n }\n\n /**\n * Gets all jobs from the queue with a custom prefix filter.\n * Used for subscriptions with custom prefix filters (filters at DB level).\n *\n * @param prefixFilter - The prefix values to filter by (empty object = all jobs)\n * @returns A promise that resolves to an array of jobs\n */\n private async getAllJobsWithFilter(\n prefixFilter: Readonly<Record<string, string | number>>\n ): Promise<Array<JobStorageFormat<Input, Output>>> {\n let query = this.client.from(this.tableName).select(\"*\").eq(\"queue\", this.queueName);\n\n // Apply the custom prefix filter\n for (const [key, value] of Object.entries(prefixFilter)) {\n query = query.eq(key, value);\n }\n\n const { data, error } = await query;\n\n if (error) throw error;\n return (data ?? []) as Array<JobStorageFormat<Input, Output>>;\n }\n\n /**\n * Subscribes to changes in the queue.\n * Uses Supabase realtime by default.\n *\n * @param callback - Function called when a change occurs\n * @param options - Subscription options including prefix filter\n * @returns Unsubscribe function\n */\n public subscribeToChanges(\n callback: (change: QueueChangePayload<Input, Output>) => void,\n options?: QueueSubscribeOptions\n ): () => void {\n return this.subscribeToChangesWithRealtime(callback, options?.prefixFilter);\n }\n\n /**\n * Subscribe using Supabase realtime (protected).\n *\n * @param callback - Function called when a change occurs\n * @param prefixFilter - Optional prefix filter (undefined = use instance prefixes, {} = no filter)\n * @returns Unsubscribe function\n */\n protected subscribeToChangesWithRealtime(\n callback: (change: QueueChangePayload<Input, Output>) => void,\n prefixFilter?: Readonly<Record<string, string | number>>\n ): () => void {\n const channelName = `queue-${this.tableName}-${this.queueName}-${Date.now()}`;\n\n this.realtimeChannel = this.client\n .channel(channelName)\n .on(\n \"postgres_changes\",\n {\n event: \"*\",\n schema: \"public\",\n table: this.tableName,\n filter: `queue=eq.${this.queueName}`,\n },\n (payload) => {\n // Filter by prefix values\n const newJob = payload.new as Record<string, unknown> | undefined;\n const oldJob = payload.old as Record<string, unknown> | undefined;\n\n // Check if either old or new job matches the filter\n const newMatches = this.matchesPrefixFilter(newJob, prefixFilter);\n const oldMatches = this.matchesPrefixFilter(oldJob, prefixFilter);\n\n if (!newMatches && !oldMatches) {\n return;\n }\n\n callback({\n type: payload.eventType.toUpperCase() as QueueChangeType,\n old:\n oldJob && Object.keys(oldJob).length > 0\n ? (oldJob as JobStorageFormat<Input, Output>)\n : undefined,\n new:\n newJob && Object.keys(newJob).length > 0\n ? (newJob as JobStorageFormat<Input, Output>)\n : undefined,\n });\n }\n )\n .subscribe();\n\n return () => {\n if (this.realtimeChannel) {\n this.client.removeChannel(this.realtimeChannel);\n this.realtimeChannel = null;\n }\n };\n }\n\n /**\n * Gets or creates the shared polling subscription manager for normal subscriptions (fallback).\n * This ensures all normal subscriptions share a single polling loop per interval.\n */\n private getPollingManager(): PollingSubscriptionManager<\n JobStorageFormat<Input, Output>,\n unknown,\n QueueChangePayload<Input, Output>\n > {\n if (!this.pollingManager) {\n this.pollingManager = new PollingSubscriptionManager<\n JobStorageFormat<Input, Output>,\n unknown,\n QueueChangePayload<Input, Output>\n >(\n async () => {\n // Fetch jobs with instance's prefix filter (efficient DB-level filtering)\n const jobs = await this.getAllJobs();\n return new Map(jobs.map((j) => [j.id, j]));\n },\n (a, b) => JSON.stringify(a) === JSON.stringify(b),\n {\n insert: (item) => ({ type: \"INSERT\" as const, new: item }),\n update: (oldItem, newItem) => ({ type: \"UPDATE\" as const, old: oldItem, new: newItem }),\n delete: (item) => ({ type: \"DELETE\" as const, old: item }),\n }\n );\n }\n return this.pollingManager;\n }\n\n /**\n * Creates a dedicated polling subscription for custom prefix filters (fallback).\n * This runs separately from the normal polling manager with DB-level filtering.\n */\n private subscribeWithCustomPrefixFilterPolling(\n callback: (change: QueueChangePayload<Input, Output>) => void,\n prefixFilter: Readonly<Record<string, string | number>>,\n intervalMs: number\n ): () => void {\n let lastKnownJobs = new Map<unknown, JobStorageFormat<Input, Output>>();\n let cancelled = false;\n\n const poll = async () => {\n if (cancelled) return;\n try {\n const currentJobs = await this.getAllJobsWithFilter(prefixFilter);\n if (cancelled) return;\n const currentMap = new Map(currentJobs.map((j) => [j.id, j]));\n\n // Detect changes\n for (const [id, job] of currentMap) {\n const old = lastKnownJobs.get(id);\n if (!old) {\n callback({ type: \"INSERT\", new: job });\n } else if (JSON.stringify(old) !== JSON.stringify(job)) {\n callback({ type: \"UPDATE\", old, new: job });\n }\n }\n\n for (const [id, job] of lastKnownJobs) {\n if (!currentMap.has(id)) {\n callback({ type: \"DELETE\", old: job });\n }\n }\n\n lastKnownJobs = currentMap;\n } catch {\n // Ignore polling errors\n }\n };\n\n const intervalId = setInterval(poll, intervalMs);\n poll(); // Initial poll\n\n return () => {\n cancelled = true;\n clearInterval(intervalId);\n };\n }\n\n /**\n * Subscribe using polling (protected, available as fallback).\n *\n * Normal subscriptions (no custom prefix filter) share a single polling loop for efficiency.\n * Custom prefix filter subscriptions get their own dedicated polling loop with DB-level filtering.\n *\n * @param callback - Function called when a change occurs\n * @param options - Subscription options including interval and prefix filter\n * @returns Unsubscribe function\n */\n protected subscribeToChangesWithPolling(\n callback: (change: QueueChangePayload<Input, Output>) => void,\n options?: QueueSubscribeOptions\n ): () => void {\n const intervalMs = options?.pollingIntervalMs ?? 1000;\n\n // Check if this is a custom prefix filter subscription\n if (this.isCustomPrefixFilter(options?.prefixFilter)) {\n // Custom prefix filter - use dedicated polling with DB-level filtering\n return this.subscribeWithCustomPrefixFilterPolling(\n callback,\n options!.prefixFilter!,\n intervalMs\n );\n }\n\n // Normal subscription - use shared polling manager (efficient)\n const manager = this.getPollingManager();\n return manager.subscribe(callback, { intervalMs });\n }\n}\n",
26
+ "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { createServiceToken } from \"@workglow/util\";\nimport {\n ensureIndexedDbTable,\n ExpectedIndexDefinition,\n MigrationOptions,\n} from \"../util/IndexedDbTable\";\nimport type { PrefixColumn } from \"../queue/IQueueStorage\";\nimport { IRateLimiterStorage, RateLimiterStorageOptions } from \"./IRateLimiterStorage\";\n\nexport const INDEXED_DB_RATE_LIMITER_STORAGE = createServiceToken<IRateLimiterStorage>(\n \"ratelimiter.storage.indexedDb\"\n);\n\n/**\n * Extended options for IndexedDB rate limiter storage including prefix support.\n */\nexport interface IndexedDbRateLimiterStorageOptions\n extends RateLimiterStorageOptions, MigrationOptions {}\n\n/**\n * Execution record stored in IndexedDB.\n */\ninterface ExecutionRecord {\n readonly id?: string;\n readonly queue_name: string;\n readonly executed_at: string;\n readonly [key: string]: unknown;\n}\n\n/**\n * Next available record stored in IndexedDB.\n */\ninterface NextAvailableRecord {\n readonly queue_name: string;\n readonly next_available_at: string;\n readonly [key: string]: unknown;\n}\n\n/**\n * IndexedDB implementation of rate limiter storage.\n * Manages execution records and next available times for rate limiting.\n */\nexport class IndexedDbRateLimiterStorage implements IRateLimiterStorage {\n private executionDb: IDBDatabase | undefined;\n private nextAvailableDb: IDBDatabase | undefined;\n private readonly executionTableName: string;\n private readonly nextAvailableTableName: string;\n private readonly migrationOptions: MigrationOptions;\n /** The prefix column definitions */\n protected readonly prefixes: readonly PrefixColumn[];\n /** The prefix values for filtering */\n protected readonly prefixValues: Readonly<Record<string, string | number>>;\n\n constructor(options: IndexedDbRateLimiterStorageOptions = {}) {\n this.migrationOptions = options;\n this.prefixes = options.prefixes ?? [];\n this.prefixValues = options.prefixValues ?? {};\n\n // Generate table names based on prefix configuration\n if (this.prefixes.length > 0) {\n const prefixNames = this.prefixes.map((p) => p.name).join(\"_\");\n this.executionTableName = `rate_limit_executions_${prefixNames}`;\n this.nextAvailableTableName = `rate_limit_next_available_${prefixNames}`;\n } else {\n this.executionTableName = \"rate_limit_executions\";\n this.nextAvailableTableName = \"rate_limit_next_available\";\n }\n }\n\n /**\n * Gets prefix column names for use in indexes.\n */\n private getPrefixColumnNames(): string[] {\n return this.prefixes.map((p) => p.name);\n }\n\n /**\n * Checks if a record matches the current prefix values.\n */\n private matchesPrefixes(record: Record<string, unknown>): boolean {\n for (const [key, value] of Object.entries(this.prefixValues)) {\n if (record[key] !== value) {\n return false;\n }\n }\n return true;\n }\n\n /**\n * Gets prefix values as an array in column order for index key construction.\n */\n private getPrefixKeyValues(): Array<string | number> {\n return this.prefixes.map((p) => this.prefixValues[p.name]);\n }\n\n private async getExecutionDb(): Promise<IDBDatabase> {\n if (this.executionDb) return this.executionDb;\n await this.setupDatabase();\n return this.executionDb!;\n }\n\n private async getNextAvailableDb(): Promise<IDBDatabase> {\n if (this.nextAvailableDb) return this.nextAvailableDb;\n await this.setupDatabase();\n return this.nextAvailableDb!;\n }\n\n public async setupDatabase(): Promise<void> {\n const prefixColumnNames = this.getPrefixColumnNames();\n\n // Build index key paths with prefixes prepended\n const buildKeyPath = (basePath: string[]): string[] => {\n return [...prefixColumnNames, ...basePath];\n };\n\n // Set up execution tracking table\n const executionIndexes: ExpectedIndexDefinition[] = [\n {\n name: \"queue_executed_at\",\n keyPath: buildKeyPath([\"queue_name\", \"executed_at\"]),\n options: { unique: false },\n },\n ];\n\n this.executionDb = await ensureIndexedDbTable(\n this.executionTableName,\n \"id\",\n executionIndexes,\n this.migrationOptions\n );\n\n // Set up next available table\n const nextAvailableIndexes: ExpectedIndexDefinition[] = [\n {\n name: \"queue_name\",\n keyPath: buildKeyPath([\"queue_name\"]),\n options: { unique: true },\n },\n ];\n\n this.nextAvailableDb = await ensureIndexedDbTable(\n this.nextAvailableTableName,\n buildKeyPath([\"queue_name\"]).join(\"_\"),\n nextAvailableIndexes,\n this.migrationOptions\n );\n }\n\n public async recordExecution(queueName: string): Promise<void> {\n const db = await this.getExecutionDb();\n const tx = db.transaction(this.executionTableName, \"readwrite\");\n const store = tx.objectStore(this.executionTableName);\n\n const record: ExecutionRecord = {\n id: crypto.randomUUID(),\n queue_name: queueName,\n executed_at: new Date().toISOString(),\n };\n\n // Add prefix values to the record\n for (const [key, value] of Object.entries(this.prefixValues)) {\n (record as Record<string, unknown>)[key] = value;\n }\n\n return new Promise((resolve, reject) => {\n const request = store.add(record);\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n request.onerror = () => reject(request.error);\n });\n }\n\n public async getExecutionCount(queueName: string, windowStartTime: string): Promise<number> {\n const db = await this.getExecutionDb();\n const tx = db.transaction(this.executionTableName, \"readonly\");\n const store = tx.objectStore(this.executionTableName);\n const index = store.index(\"queue_executed_at\");\n const prefixKeyValues = this.getPrefixKeyValues();\n\n return new Promise((resolve, reject) => {\n let count = 0;\n const keyRange = IDBKeyRange.bound(\n [...prefixKeyValues, queueName, windowStartTime],\n [...prefixKeyValues, queueName, \"\\uffff\"],\n true, // exclude lower bound (windowStartTime)\n false\n );\n const request = index.openCursor(keyRange);\n\n request.onsuccess = (event) => {\n const cursor = (event.target as IDBRequest<IDBCursorWithValue>).result;\n if (cursor) {\n const record = cursor.value as ExecutionRecord;\n if (this.matchesPrefixes(record)) {\n count++;\n }\n cursor.continue();\n }\n };\n\n tx.oncomplete = () => resolve(count);\n tx.onerror = () => reject(tx.error);\n request.onerror = () => reject(request.error);\n });\n }\n\n public async getOldestExecutionAtOffset(\n queueName: string,\n offset: number\n ): Promise<string | undefined> {\n const db = await this.getExecutionDb();\n const tx = db.transaction(this.executionTableName, \"readonly\");\n const store = tx.objectStore(this.executionTableName);\n const index = store.index(\"queue_executed_at\");\n const prefixKeyValues = this.getPrefixKeyValues();\n\n return new Promise((resolve, reject) => {\n const executions: string[] = [];\n const keyRange = IDBKeyRange.bound(\n [...prefixKeyValues, queueName, \"\"],\n [...prefixKeyValues, queueName, \"\\uffff\"]\n );\n const request = index.openCursor(keyRange);\n\n request.onsuccess = (event) => {\n const cursor = (event.target as IDBRequest<IDBCursorWithValue>).result;\n if (cursor) {\n const record = cursor.value as ExecutionRecord;\n if (this.matchesPrefixes(record)) {\n executions.push(record.executed_at);\n }\n cursor.continue();\n }\n };\n\n tx.oncomplete = () => {\n // Sort by executed_at ascending\n executions.sort();\n resolve(executions[offset]);\n };\n tx.onerror = () => reject(tx.error);\n request.onerror = () => reject(request.error);\n });\n }\n\n public async getNextAvailableTime(queueName: string): Promise<string | undefined> {\n const db = await this.getNextAvailableDb();\n const tx = db.transaction(this.nextAvailableTableName, \"readonly\");\n const store = tx.objectStore(this.nextAvailableTableName);\n const prefixKeyValues = this.getPrefixKeyValues();\n const key = [...prefixKeyValues, queueName].join(\"_\");\n\n return new Promise((resolve, reject) => {\n const request = store.get(key);\n request.onsuccess = () => {\n const record = request.result as NextAvailableRecord | undefined;\n if (record && this.matchesPrefixes(record)) {\n resolve(record.next_available_at);\n } else {\n resolve(undefined);\n }\n };\n request.onerror = () => reject(request.error);\n tx.onerror = () => reject(tx.error);\n });\n }\n\n public async setNextAvailableTime(queueName: string, nextAvailableAt: string): Promise<void> {\n const db = await this.getNextAvailableDb();\n const tx = db.transaction(this.nextAvailableTableName, \"readwrite\");\n const store = tx.objectStore(this.nextAvailableTableName);\n const prefixKeyValues = this.getPrefixKeyValues();\n const key = [...prefixKeyValues, queueName].join(\"_\");\n\n const record: NextAvailableRecord & { [key: string]: unknown } = {\n queue_name: queueName,\n next_available_at: nextAvailableAt,\n };\n\n // Add prefix values to the record\n for (const [k, value] of Object.entries(this.prefixValues)) {\n record[k] = value;\n }\n\n // Set the key field\n (record as Record<string, unknown>)[\n this.getPrefixColumnNames().concat([\"queue_name\"]).join(\"_\")\n ] = key;\n\n return new Promise((resolve, reject) => {\n const request = store.put(record);\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n request.onerror = () => reject(request.error);\n });\n }\n\n public async clear(queueName: string): Promise<void> {\n // Clear executions\n const execDb = await this.getExecutionDb();\n const execTx = execDb.transaction(this.executionTableName, \"readwrite\");\n const execStore = execTx.objectStore(this.executionTableName);\n const execIndex = execStore.index(\"queue_executed_at\");\n const prefixKeyValues = this.getPrefixKeyValues();\n\n await new Promise<void>((resolve, reject) => {\n const keyRange = IDBKeyRange.bound(\n [...prefixKeyValues, queueName, \"\"],\n [...prefixKeyValues, queueName, \"\\uffff\"]\n );\n const request = execIndex.openCursor(keyRange);\n\n request.onsuccess = (event) => {\n const cursor = (event.target as IDBRequest<IDBCursorWithValue>).result;\n if (cursor) {\n const record = cursor.value as ExecutionRecord;\n if (this.matchesPrefixes(record)) {\n cursor.delete();\n }\n cursor.continue();\n }\n };\n\n execTx.oncomplete = () => resolve();\n execTx.onerror = () => reject(execTx.error);\n request.onerror = () => reject(request.error);\n });\n\n // Clear next available\n const nextDb = await this.getNextAvailableDb();\n const nextTx = nextDb.transaction(this.nextAvailableTableName, \"readwrite\");\n const nextStore = nextTx.objectStore(this.nextAvailableTableName);\n const key = [...prefixKeyValues, queueName].join(\"_\");\n\n await new Promise<void>((resolve, reject) => {\n const request = nextStore.delete(key);\n nextTx.oncomplete = () => resolve();\n nextTx.onerror = () => reject(nextTx.error);\n request.onerror = () => reject(request.error);\n });\n }\n}\n",
27
+ "/**\n * @license\n * Copyright 2025 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type { SupabaseClient } from \"@supabase/supabase-js\";\nimport { createServiceToken } from \"@workglow/util\";\nimport type { PrefixColumn } from \"../queue/IQueueStorage\";\nimport { IRateLimiterStorage, RateLimiterStorageOptions } from \"./IRateLimiterStorage\";\n\nexport const SUPABASE_RATE_LIMITER_STORAGE = createServiceToken<IRateLimiterStorage>(\n \"ratelimiter.storage.supabase\"\n);\n\n/**\n * Supabase implementation of rate limiter storage.\n * Manages execution records and next available times for rate limiting.\n */\nexport class SupabaseRateLimiterStorage implements IRateLimiterStorage {\n /** The prefix column definitions */\n protected readonly prefixes: readonly PrefixColumn[];\n /** The prefix values for filtering */\n protected readonly prefixValues: Readonly<Record<string, string | number>>;\n /** The table name for execution tracking */\n protected readonly executionTableName: string;\n /** The table name for next available times */\n protected readonly nextAvailableTableName: string;\n\n constructor(\n protected readonly client: SupabaseClient,\n options?: RateLimiterStorageOptions\n ) {\n this.prefixes = options?.prefixes ?? [];\n this.prefixValues = options?.prefixValues ?? {};\n\n // Generate table names based on prefix configuration\n if (this.prefixes.length > 0) {\n const prefixNames = this.prefixes.map((p) => p.name).join(\"_\");\n this.executionTableName = `rate_limit_executions_${prefixNames}`;\n this.nextAvailableTableName = `rate_limit_next_available_${prefixNames}`;\n } else {\n this.executionTableName = \"rate_limit_executions\";\n this.nextAvailableTableName = \"rate_limit_next_available\";\n }\n }\n\n /**\n * Gets the SQL column type for a prefix column (Supabase supports UUID natively).\n */\n private getPrefixColumnType(type: PrefixColumn[\"type\"]): string {\n return type === \"uuid\" ? \"UUID\" : \"INTEGER\";\n }\n\n /**\n * Builds the prefix columns SQL for CREATE TABLE.\n */\n private buildPrefixColumnsSql(): string {\n if (this.prefixes.length === 0) return \"\";\n return (\n this.prefixes\n .map((p) => `${p.name} ${this.getPrefixColumnType(p.type)} NOT NULL`)\n .join(\",\\n \") + \",\\n \"\n );\n }\n\n /**\n * Builds prefix column names for use in queries.\n */\n private getPrefixColumnNames(): string[] {\n return this.prefixes.map((p) => p.name);\n }\n\n /**\n * Applies prefix filters to a Supabase query builder.\n */\n private applyPrefixFilters<T>(query: T): T {\n let result = query as any;\n for (const prefix of this.prefixes) {\n result = result.eq(prefix.name, this.prefixValues[prefix.name]);\n }\n return result as T;\n }\n\n /**\n * Gets prefix values as an object for inserts.\n */\n private getPrefixInsertValues(): Record<string, string | number> {\n const values: Record<string, string | number> = {};\n for (const prefix of this.prefixes) {\n values[prefix.name] = this.prefixValues[prefix.name];\n }\n return values;\n }\n\n public async setupDatabase(): Promise<void> {\n const prefixColumnsSql = this.buildPrefixColumnsSql();\n const prefixColumnNames = this.getPrefixColumnNames();\n const prefixIndexPrefix =\n prefixColumnNames.length > 0 ? prefixColumnNames.join(\", \") + \", \" : \"\";\n const indexSuffix = prefixColumnNames.length > 0 ? \"_\" + prefixColumnNames.join(\"_\") : \"\";\n\n // Create execution tracking table\n const createExecTableSql = `\n CREATE TABLE IF NOT EXISTS ${this.executionTableName} (\n id SERIAL PRIMARY KEY,\n ${prefixColumnsSql}queue_name TEXT NOT NULL,\n executed_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()\n )\n `;\n\n const { error: execTableError } = await this.client.rpc(\"exec_sql\", {\n query: createExecTableSql,\n });\n if (execTableError && execTableError.code !== \"42P07\") {\n throw execTableError;\n }\n\n // Create index on execution table\n const createExecIndexSql = `\n CREATE INDEX IF NOT EXISTS rate_limit_exec_queue${indexSuffix}_idx \n ON ${this.executionTableName} (${prefixIndexPrefix}queue_name, executed_at)\n `;\n await this.client.rpc(\"exec_sql\", { query: createExecIndexSql });\n\n // Build primary key columns\n const primaryKeyColumns =\n prefixColumnNames.length > 0 ? `${prefixColumnNames.join(\", \")}, queue_name` : \"queue_name\";\n\n // Create next available table\n const createNextTableSql = `\n CREATE TABLE IF NOT EXISTS ${this.nextAvailableTableName} (\n ${prefixColumnsSql}queue_name TEXT NOT NULL,\n next_available_at TIMESTAMP WITH TIME ZONE,\n PRIMARY KEY (${primaryKeyColumns})\n )\n `;\n\n const { error: nextTableError } = await this.client.rpc(\"exec_sql\", {\n query: createNextTableSql,\n });\n if (nextTableError && nextTableError.code !== \"42P07\") {\n throw nextTableError;\n }\n }\n\n public async recordExecution(queueName: string): Promise<void> {\n const prefixInsertValues = this.getPrefixInsertValues();\n\n const { error } = await this.client.from(this.executionTableName).insert({\n ...prefixInsertValues,\n queue_name: queueName,\n });\n\n if (error) throw error;\n }\n\n public async getExecutionCount(queueName: string, windowStartTime: string): Promise<number> {\n let query = this.client\n .from(this.executionTableName)\n .select(\"*\", { count: \"exact\", head: true })\n .eq(\"queue_name\", queueName)\n .gt(\"executed_at\", windowStartTime);\n\n query = this.applyPrefixFilters(query);\n\n const { count, error } = await query;\n\n if (error) throw error;\n return count ?? 0;\n }\n\n public async getOldestExecutionAtOffset(\n queueName: string,\n offset: number\n ): Promise<string | undefined> {\n let query = this.client\n .from(this.executionTableName)\n .select(\"executed_at\")\n .eq(\"queue_name\", queueName);\n\n query = this.applyPrefixFilters(query);\n\n const { data, error } = await query\n .order(\"executed_at\", { ascending: true })\n .range(offset, offset);\n\n if (error) throw error;\n if (!data || data.length === 0) return undefined;\n return new Date(data[0].executed_at).toISOString();\n }\n\n public async getNextAvailableTime(queueName: string): Promise<string | undefined> {\n let query = this.client\n .from(this.nextAvailableTableName)\n .select(\"next_available_at\")\n .eq(\"queue_name\", queueName);\n\n query = this.applyPrefixFilters(query);\n\n const { data, error } = await query.single();\n\n if (error) {\n if (error.code === \"PGRST116\") return undefined; // Not found\n throw error;\n }\n\n if (!data?.next_available_at) return undefined;\n return new Date(data.next_available_at).toISOString();\n }\n\n public async setNextAvailableTime(queueName: string, nextAvailableAt: string): Promise<void> {\n const prefixInsertValues = this.getPrefixInsertValues();\n\n const { error } = await this.client.from(this.nextAvailableTableName).upsert(\n {\n ...prefixInsertValues,\n queue_name: queueName,\n next_available_at: nextAvailableAt,\n },\n {\n onConflict:\n this.prefixes.length > 0\n ? `${this.getPrefixColumnNames().join(\",\")},queue_name`\n : \"queue_name\",\n }\n );\n\n if (error) throw error;\n }\n\n public async clear(queueName: string): Promise<void> {\n let execQuery = this.client.from(this.executionTableName).delete().eq(\"queue_name\", queueName);\n execQuery = this.applyPrefixFilters(execQuery);\n const { error: execError } = await execQuery;\n if (execError) throw execError;\n\n let nextQuery = this.client\n .from(this.nextAvailableTableName)\n .delete()\n .eq(\"queue_name\", queueName);\n nextQuery = this.applyPrefixFilters(nextQuery);\n const { error: nextError } = await nextQuery;\n if (nextError) throw nextError;\n }\n}\n"
23
28
  ],
24
- "mappings": "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",
25
- "debugId": "706648683071DA7B64756E2164756E21",
29
+ "mappings": "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",
30
+ "debugId": "91B1809D834FE66964756E2164756E21",
26
31
  "names": []
27
32
  }