dinah 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +228 -0
- package/package.json +14 -46
- package/dist/index.cjs +0 -1081
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -454
- package/dist/index.d.cts.map +0 -1
- package/dist/index.d.mts +0 -454
- package/dist/index.d.mts.map +0 -1
- package/dist/index.mjs +0 -1045
- package/dist/index.mjs.map +0 -1
- package/dist/streams.cjs +0 -84
- package/dist/streams.cjs.map +0 -1
- package/dist/streams.d.cts +0 -21
- package/dist/streams.d.cts.map +0 -1
- package/dist/streams.d.mts +0 -21
- package/dist/streams.d.mts.map +0 -1
- package/dist/streams.mjs +0 -82
- package/dist/streams.mjs.map +0 -1
package/dist/index.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/expression-builder.ts","../src/repo.ts","../src/util.ts","../src/db.ts","../src/table.ts"],"sourcesContent":["import type { Obj } from \"./types\";\n\nconst attrTypes = [\"S\", \"SS\", \"N\", \"NS\", \"B\", \"BS\", \"BOOL\", \"NULL\", \"L\", \"M\"] as const;\nconst comparatorOps: Obj<string> = {\n $eq: \"=\",\n $ne: \"<>\",\n $gt: \">\",\n $gte: \">=\",\n $lt: \"<\",\n $lte: \"<=\",\n};\n\n// TODO should { $eq: undefined } and { $ne: undefined } be converted into attribute_not_exists and attribute_exists ?\n\nexport const isOperation = (val: unknown): val is Obj => {\n if (!val || typeof val !== \"object\" || Array.isArray(val)) return false;\n return Object.keys(val).every((op) => op.startsWith(\"$\"));\n};\n\nexport class ExpressionBuilder {\n protected readonly attrNames = new Map<string, string>();\n protected readonly attrValues = new Map<unknown, string>();\n\n get attributeNames(): Obj<string> | undefined {\n return this.attrNames.size\n ? Object.fromEntries(\n [...this.attrNames.entries()].map(([name, placeholder]) => [placeholder, name]),\n )\n : undefined;\n }\n\n get attributeValues(): Obj | undefined {\n return this.attrValues.size\n ? Object.fromEntries(\n [...this.attrValues.entries()].map(([value, placeholder]) => [placeholder, value]),\n )\n : undefined;\n }\n\n reset(): void {\n this.attrNames.clear();\n this.attrValues.clear();\n }\n\n getPathSub(path: string): string {\n const segments = path.split(\".\");\n const placeholders: string[] = [];\n\n for (const segment of segments) {\n let placeholder = this.attrNames.get(segment);\n if (!placeholder) {\n placeholder = `#${this.attrNames.size}`;\n this.attrNames.set(segment, placeholder);\n }\n\n placeholders.push(placeholder);\n }\n\n return placeholders.join(\".\");\n }\n\n getValueSub(value: unknown): string {\n let placeholder = this.attrValues.get(value);\n if (!placeholder) {\n placeholder = `:${this.attrValues.size}`;\n this.attrValues.set(value, placeholder);\n }\n\n return placeholder;\n }\n\n getValueOrPathSub(pathOrValue: unknown): string {\n if (isOperation(pathOrValue)) {\n if (pathOrValue.$path) return this.getPathSub(pathOrValue.$path);\n throw new Error(\"Expected $path or operand.\");\n }\n\n return this.getValueSub(pathOrValue);\n }\n\n projection<T extends string[] | undefined>(paths: T): T extends undefined ? undefined : string {\n return paths?.map((path) => this.getPathSub(path)).join(\", \") as never;\n }\n\n condition<T extends Obj | undefined>(expression: T): T extends undefined ? undefined : string {\n if (!expression) return undefined as never;\n\n const expressions: string[] = [];\n for (const [key, val] of Object.entries(expression)) {\n if (key === \"$and\") {\n if (!Array.isArray(val)) throw new Error(\"$and expects an array operand.\");\n expressions.push(`(${val.map((v) => this.condition(v)).join(\" AND \")})`);\n } else if (key === \"$or\" && Array.isArray(val)) {\n if (!Array.isArray(val)) throw new Error(\"$or expects an array operand.\");\n expressions.push(`(${val.map((v) => this.condition(v)).join(\" OR \")})`);\n } else if (key === \"$not\") {\n if (!val || typeof val !== \"object\") throw new Error(\"$not expects an object operand.\");\n expressions.push(`NOT ${this.condition(val)}`);\n } else if (!key.startsWith(\"$\")) {\n expressions.push(this.resolveCondition(key, val));\n } else {\n throw new Error(\n `Unexpected operator \"${key}\". Expected attribute path or compound operator.`,\n );\n }\n }\n\n return expressions.join(\" AND \") as never;\n }\n\n update<T extends Obj | undefined>(expression: T): T extends undefined ? undefined : string {\n if (expression === undefined) return undefined as never;\n\n const setOperations: string[] = [];\n const removeOperations: string[] = [];\n const setAddOperations: string[] = [];\n const setDelOperations: string[] = [];\n\n for (const [path, valOrOperation] of Object.entries(expression)) {\n const placeholder = this.getPathSub(path);\n\n if (valOrOperation === undefined) {\n removeOperations.push(placeholder);\n } else if (isOperation(valOrOperation)) {\n if (valOrOperation.$remove === true) {\n removeOperations.push(placeholder);\n } else if (valOrOperation.$setAdd !== undefined || valOrOperation.$setDel !== undefined) {\n const operations = valOrOperation.$setAdd ? setAddOperations : setDelOperations;\n const operand = valOrOperation.$setAdd ?? valOrOperation.$setDel;\n operations.push(\n `${placeholder} ${this.getValueSub(operand instanceof Set ? operand : new Set([operand].flat()))}`,\n );\n } else {\n setOperations.push(`${placeholder} = ${this.resolveSetOperand(path, valOrOperation)}`);\n }\n } else {\n setOperations.push(`${placeholder} = ${this.getValueSub(valOrOperation)}`);\n }\n }\n\n let updateExpression = \"\";\n if (setOperations.length) {\n updateExpression += `SET ${setOperations.join(\", \")} `;\n }\n\n if (removeOperations.length) {\n updateExpression += `REMOVE ${removeOperations.join(\", \")} `;\n }\n\n if (setAddOperations.length) {\n updateExpression += `ADD ${setAddOperations.join(\", \")} `;\n }\n\n if (setDelOperations.length) {\n updateExpression += `DELETE ${setDelOperations.join(\", \")} `;\n }\n\n return updateExpression as never;\n }\n\n protected resolveCondition(path: string, exp: unknown): string {\n if (!exp || typeof exp !== \"object\" || !Object.keys(exp).at(0)?.startsWith(\"$\")) {\n return this.resolveCondition(path, { $eq: exp });\n }\n\n const placeholder = this.getPathSub(path);\n const [operator, operand] = Object.entries(exp).at(0) ?? [\"\"];\n\n if (comparatorOps[operator]) {\n if (isOperation(operand) && operand.$path) {\n return `${placeholder} ${comparatorOps[operator]} ${this.getPathSub(operand.$path)}`;\n }\n\n if (![\"string\", \"boolean\", \"number\"].includes(typeof operand)) {\n throw new Error(`${operator} expects a primitive/scalar operand.`);\n }\n return `${placeholder} ${comparatorOps[operator]} ${this.getValueSub(operand)}`;\n }\n\n switch (operator) {\n case \"$in\":\n if (!Array.isArray(operand)) {\n throw new Error(\"$in operator expects an array operand.\");\n }\n return `${placeholder} IN (${operand.map((op) => this.getValueSub(op)).join(\", \")})`;\n\n case \"$nin\":\n if (!Array.isArray(operand)) {\n throw new Error(\"$nin operator expects an array operand.\");\n }\n return `${placeholder} NOT IN (${operand.map((op) => this.getValueSub(op)).join(\", \")})`;\n\n case \"$prefix\":\n if (typeof operand !== \"string\") {\n throw new Error(\"$prefix operator expects a string operand.\");\n }\n return `begins_with(${placeholder}, ${this.getValueSub(operand)})`;\n\n case \"$includes\":\n return `contains(${placeholder}, ${this.getValueSub(operand)})`;\n\n case \"$between\":\n if (!Array.isArray(operand) || operand.length !== 2) {\n throw new Error(\"$between operator expects an array operand of exactly two values.\");\n }\n return `${placeholder} BETWEEN ${this.getValueSub(operand[0])} AND ${this.getValueSub(operand[1])}`;\n\n case \"$exists\":\n if (typeof operand !== \"boolean\") {\n throw new Error(\"$exists operator expects a boolean operand.\");\n }\n return operand\n ? `attribute_exists(${placeholder})`\n : `attribute_not_exists(${placeholder})`;\n\n case \"$type\":\n if (!attrTypes.includes(operand as any)) {\n throw new Error(`$type operator expects operand to be one of ${attrTypes.join(\",\")}`);\n }\n return `attribute_type(${placeholder})`;\n\n case \"$size\": {\n const sizeExp = typeof operand === \"number\" ? { $eq: operand } : operand;\n if (\n !sizeExp ||\n typeof sizeExp !== \"object\" ||\n !Object.keys(sizeExp).at(0)?.startsWith(\"$\")\n ) {\n throw new Error(\"$size operator expects a number operand or a comparator object.\");\n }\n const [sizeOperator, sizeOperand] = Object.entries(sizeExp).at(0) ?? [\"\"];\n\n if (!comparatorOps[sizeOperator]) {\n throw new Error(\"$size operator expects a number operand or a comparator object.\");\n }\n\n if (![\"string\", \"boolean\", \"number\"].includes(typeof sizeOperand)) {\n throw new Error(`${operator} expects a primitive/scalar operand.`);\n }\n\n return `size(${placeholder}) ${comparatorOps[sizeOperator]} ${this.getValueSub(sizeOperand)}`;\n }\n\n default:\n throw new Error(`Invalid operator \"${operator}\"`);\n }\n }\n\n protected resolveSetOperand(path: string, operand: unknown): string {\n if (isOperation(operand)) {\n if (operand.$ifNotExists) {\n const params = Array.isArray(operand.$ifNotExists)\n ? operand.$ifNotExists\n : [path, operand.$ifNotExists];\n return `if_not_exists(${this.getPathSub(params[0])}, ${this.resolveSetOperand(path, params[1])})`;\n }\n\n if (operand.$set) {\n return this.resolveSetOperand(path, operand.$set);\n }\n\n if (operand.$plus !== undefined || operand.$minus !== undefined) {\n const mathOperator = operand.$plus ? \"+\" : \"-\";\n const mathOperand = operand.$plus ?? operand.$minus;\n const params = Array.isArray(mathOperand) ? mathOperand : [{ $path: path }, mathOperand];\n return `${params.map((param) => this.resolveSetOperand(path, param)).join(` ${mathOperator} `)}`;\n }\n\n if (operand.$append !== undefined || operand.$prepend !== undefined) {\n const listOperand = operand.$append ?? operand.$prepend;\n const resolvedListOperand = isOperation(listOperand) ? listOperand : [listOperand].flat();\n const params = operand.$append\n ? [{ $path: path }, resolvedListOperand]\n : [resolvedListOperand, { $path: path }];\n return `list_append(${params.map((param) => this.resolveSetOperand(path, param)).join(\", \")})`;\n }\n }\n\n return this.getValueOrPathSub(operand);\n }\n}\n","import type { TSchema } from \"typebox\";\nimport type { Db } from \"./db\";\nimport type { Table } from \"./table\";\nimport type {\n DbTrxGetRequest,\n DbTrxWriteRequest,\n ExtractTableDef,\n GsiNames,\n Obj,\n RepoBatchGet,\n RepoBatchGetOrThrowResult,\n RepoBatchGetResult,\n RepoBatchWrite,\n RepoBatchWriteResult,\n RepoCreate,\n RepoCreateItem,\n RepoCreateResult,\n RepoDelete,\n RepoDeleteOrThrowResult,\n RepoDeleteResult,\n RepoExists,\n RepoGet,\n RepoGetOrThrowResult,\n RepoGetResult,\n RepoKey,\n RepoPut,\n RepoPutItem,\n RepoPutResult,\n RepoQuery,\n RepoQueryGsi,\n RepoQueryGsiPagedResult,\n RepoQueryGsiResult,\n RepoQueryPagedResult,\n RepoQueryResult,\n RepoScan,\n RepoScanGsi,\n RepoScanGsiPagedResult,\n RepoScanGsiResult,\n RepoScanPagedResult,\n RepoScanResult,\n RepoTrxGet,\n RepoTrxGetOrThrowResult,\n RepoTrxGetResult,\n RepoTrxWriteRequest,\n RepoUpdate,\n RepoUpdateData,\n RepoUpdateResult,\n RepoUpsert,\n RepoUpsertResult,\n} from \"./types\";\n\n// TODO: query/queryGsi needs strongly typed \"key\" argument\n// allows =,>,>=,<,<=,begins_with, between on sort key\n// util -> extractExclusiveStartKey(item)\n\nexport class Repository<T extends Table<any, any>> {\n readonly table: Table<TSchema, any>;\n readonly db: Db;\n constructor(table: T, db: Db) {\n this.table = table;\n this.db = db;\n }\n\n get tableName(): string {\n return `${this.db.config?.tableNamePrefix ?? \"\"}${this.table.def.name}`;\n }\n\n get def(): ExtractTableDef<T> {\n return this.table.def as never;\n }\n\n extractKey(item: RepoKey<T>): RepoKey<T> {\n const { partitionKey, sortKey } = this.table.def;\n if (sortKey) {\n return {\n [partitionKey]: item[partitionKey],\n [sortKey]: item[sortKey],\n } as RepoKey<T>;\n }\n\n return { [partitionKey]: item[partitionKey] } as RepoKey<T>;\n }\n\n async get<O extends RepoGet<T>>(key: RepoKey<T>, options?: O): Promise<RepoGetResult<T, O>> {\n return this.db.get({ table: this.tableName, key: this.extractKey(key), ...options });\n }\n\n async getOrThrow<O extends RepoGet<T>>(\n key: RepoKey<T>,\n options?: O,\n ): Promise<RepoGetOrThrowResult<T, O>> {\n return this.db.getOrThrow({ table: this.tableName, key: this.extractKey(key), ...options });\n }\n\n async put<O extends RepoPut<T>>(item: RepoPutItem<T>, options?: O): Promise<RepoPutResult<T, O>> {\n const itemWithDefaults = this.table.def.beforePut\n ? { ...this.table.def.beforePut(item), ...item }\n : item;\n return this.db.put({ table: this.tableName, item: itemWithDefaults, ...options });\n }\n\n async update<O extends RepoUpdate<T>>(\n key: RepoKey<T>,\n update: RepoUpdateData<T>,\n options?: O,\n ): Promise<RepoUpdateResult<T, O>> {\n const updateWithDefaults = this.table.def.beforeUpdate\n ? { ...this.table.def.beforeUpdate(update), ...update }\n : update;\n return this.db.update({\n table: this.tableName,\n key: this.extractKey(key),\n update: updateWithDefaults,\n ...options,\n }) as RepoUpdateResult<T, O>;\n }\n\n async create<O extends RepoCreate<T>>(\n item: RepoCreateItem<T>,\n options?: O,\n ): Promise<RepoCreateResult<T, O>> {\n const itemWithDefaults = this.table.def.beforePut\n ? { ...this.table.def.beforePut(item), ...item }\n : item;\n return this.db.create({\n table: this.tableName,\n item: itemWithDefaults,\n partitionKeyName: this.table.def.partitionKey,\n ...options,\n });\n }\n\n async upsert(data: RepoUpsert<T>): Promise<RepoUpsertResult<T>> {\n const { update, item, key, ...options } = data;\n const updateWithDefaults = this.table.def.beforeUpdate\n ? { ...this.table.def.beforeUpdate(update), ...update }\n : update;\n const itemWithDefaults = this.table.def.beforePut\n ? { ...this.table.def.beforePut(item), ...item }\n : item;\n return this.db.upsert({\n table: this.tableName,\n key: this.extractKey(key),\n update: updateWithDefaults,\n item: itemWithDefaults,\n ...options,\n });\n }\n\n async delete(key: RepoKey<T>, options?: RepoDelete<T>): Promise<RepoDeleteResult<T>> {\n return this.db.delete({ table: this.tableName, key: this.extractKey(key), ...options });\n }\n\n async deleteOrThrow(\n key: RepoKey<T>,\n options?: Omit<RepoDelete<T>, \"return\">,\n ): Promise<RepoDeleteOrThrowResult<T>> {\n return this.db.deleteOrThrow({ table: this.tableName, key: this.extractKey(key), ...options });\n }\n\n async query<O extends RepoQuery<T>>(query: Obj, options?: O): Promise<RepoQueryResult<T, O>> {\n return this.db.query({ table: this.tableName, query, ...options });\n }\n\n async *queryPaged<O extends RepoQuery<T>>(query: Obj, options?: O): RepoQueryPagedResult<T, O> {\n yield* this.db.queryPaged({ table: this.tableName, query, ...options }) as RepoQueryPagedResult<\n T,\n O\n >;\n }\n\n async queryGsi<G extends GsiNames<T>, O extends RepoQueryGsi<T>>(\n gsi: G,\n query: Obj,\n options?: O,\n ): Promise<RepoQueryGsiResult<T, O>> {\n return this.db.query({ table: this.tableName, index: gsi, query, ...options });\n }\n\n async *queryGsiPaged<G extends GsiNames<T>, O extends RepoQueryGsi<T>>(\n gsi: G,\n query: Obj,\n options?: O,\n ): RepoQueryGsiPagedResult<T, O> {\n yield* this.db.queryPaged({\n table: this.tableName,\n index: gsi,\n query,\n ...options,\n }) as RepoQueryGsiPagedResult<T, O>;\n }\n\n async scan<O extends RepoScan<T>>(options?: O): Promise<RepoScanResult<T, O>> {\n return this.db.scan({ table: this.tableName, ...options });\n }\n\n async *scanPaged<O extends RepoScan<T>>(options?: O): RepoScanPagedResult<T, O> {\n yield* this.db.scanPaged({ table: this.tableName, ...options }) as RepoScanPagedResult<T, O>;\n }\n\n async scanGsi<G extends GsiNames<T>, O extends RepoScanGsi<T>>(\n gsi: G,\n options?: O,\n ): Promise<RepoScanGsiResult<T, O>> {\n return this.db.scan({ table: this.tableName, index: gsi, ...options });\n }\n\n async *scanGsiPaged<G extends GsiNames<T>, O extends RepoScanGsi<T>>(\n gsi: G,\n options?: O,\n ): RepoScanGsiPagedResult<T, O> {\n yield* this.db.scanPaged({\n table: this.tableName,\n index: gsi,\n ...options,\n }) as RepoScanGsiPagedResult<T, O>;\n }\n\n async exists(options?: RepoExists<T>): Promise<boolean> {\n return this.db.exists({\n table: this.tableName,\n projection: [this.table.def.partitionKey],\n ...options,\n });\n }\n\n async existsGsi(gsi: GsiNames<T>, options?: RepoExists<T>): Promise<boolean> {\n return this.db.exists({\n table: this.tableName,\n index: gsi,\n projection: [this.table.def.partitionKey],\n ...options,\n });\n }\n\n async batchGet<O extends RepoBatchGet<T>>(\n keys: RepoKey<T>[],\n options?: O,\n ): Promise<RepoBatchGetResult<T, O>> {\n const { items, unprocessed } = await this.db.batchGet({\n [this.tableName]: { keys: keys.map((key) => this.extractKey(key)), ...options },\n });\n return {\n items: items[this.tableName],\n unprocessed: unprocessed?.[this.tableName]?.keys,\n } as RepoBatchGetResult<T, O>;\n }\n\n async batchGetOrThrow<O extends RepoBatchGet<T>>(\n keys: RepoKey<T>[],\n options?: O,\n ): Promise<RepoBatchGetOrThrowResult<T, O>> {\n const result = await this.db.batchGetOrThrow({\n [this.tableName]: { keys: keys.map((key) => this.extractKey(key)), ...options },\n });\n return result[this.tableName] as RepoBatchGetOrThrowResult<T, O>;\n }\n\n async batchWrite(requests: RepoBatchWrite<T>): Promise<RepoBatchWriteResult<T>> {\n const { items, unprocessed } = await this.db.batchWrite({\n [this.tableName]: requests.map((request) => {\n if (request.type === \"DELETE\") {\n return { type: \"DELETE\", key: this.extractKey(request.key) };\n } else {\n const itemWithDefaults = this.table.def.beforePut\n ? { ...this.table.def.beforePut(request.item), ...request.item }\n : request.item;\n return { type: \"PUT\", item: itemWithDefaults };\n }\n }),\n });\n\n return {\n items: items[this.tableName],\n unprocessed: unprocessed?.[this.tableName],\n } as RepoBatchWriteResult<T>;\n }\n\n // this can be simplified now\n // also add upsert\n\n async trxGet<O extends RepoTrxGet<T>>(\n keys: RepoKey<T>[],\n options?: O,\n ): Promise<RepoTrxGetResult<T, O>> {\n return this.db.trxGet(\n ...keys.map((key) => ({ table: this.tableName, key: this.extractKey(key), ...options })),\n ) as Promise<RepoTrxGetResult<T, O>>;\n }\n\n async trxGetOrThrow<O extends RepoTrxGet<T>>(\n keys: RepoKey<T>[],\n options?: O,\n ): Promise<RepoTrxGetOrThrowResult<T, O>> {\n return this.db.trxGetOrThrow(\n ...keys.map((key) => ({ table: this.tableName, key: this.extractKey(key), ...options })),\n ) as Promise<RepoTrxGetOrThrowResult<T, O>>;\n }\n\n async trxWrite(...requests: RepoTrxWriteRequest<T>[]): Promise<void> {\n await this.db.trxWrite(\n ...requests.map((request) => {\n switch (request.type) {\n case \"CONDITION\": {\n const { key, condition, ...options } = request;\n return this.trxConditionRequest(key, condition, options);\n }\n\n case \"DELETE\": {\n const { key, ...options } = request;\n return this.trxDeleteRequest(key, options);\n }\n\n case \"PUT\": {\n const { item, ...options } = request;\n return this.trxPutRequest(item, options);\n }\n\n case \"UPDATE\": {\n const { key, update, ...options } = request;\n return this.trxUpdateRequest(key, update, options);\n }\n\n default:\n throw new Error(\"Unexpected request type.\");\n }\n }),\n );\n }\n\n async trxDelete(keys: RepoKey<T>[], options?: Omit<RepoDelete<T>, \"return\">): Promise<void> {\n return this.db.trxWrite(...keys.map((key) => this.trxDeleteRequest(key, options)));\n }\n\n // todo: return items\n async trxPut(items: RepoPutItem<T>[], options?: Omit<RepoPut<T>, \"return\">): Promise<void> {\n return this.db.trxWrite(...items.map((item) => this.trxPutRequest(item, options)));\n }\n\n async trxUpdate(\n keys: RepoKey<T>[],\n update: RepoUpdateData<T>,\n options?: Omit<RepoUpdate<T>, \"return\">,\n ): Promise<void> {\n return this.db.trxWrite(...keys.map((key) => this.trxUpdateRequest(key, update, options)));\n }\n\n // todo: return items\n async trxCreate(items: RepoCreateItem<T>[], options?: RepoCreate<T>): Promise<void> {\n return this.db.trxWrite(...items.map((item) => this.trxCreateRequest(item, options)));\n }\n\n trxGetRequest<O extends RepoGet<T>>(\n key: RepoKey<T>,\n options?: O,\n ): DbTrxGetRequest<RepoGetOrThrowResult<T, O>> {\n return { table: this.tableName, key: this.extractKey(key), ...options };\n }\n\n trxDeleteRequest(key: RepoKey<T>, options?: Omit<RepoDelete<T>, \"return\">): DbTrxWriteRequest {\n return { table: this.tableName, type: \"DELETE\", key: this.extractKey(key), ...options };\n }\n\n trxConditionRequest(\n key: RepoKey<T>,\n condition: Obj,\n options?: Omit<RepoDelete<T>, \"return\" | \"condition\">,\n ): DbTrxWriteRequest {\n return {\n table: this.tableName,\n type: \"CONDITION\",\n key: this.extractKey(key),\n condition,\n ...options,\n };\n }\n\n trxPutRequest(item: RepoPutItem<T>, options?: Omit<RepoPut<T>, \"return\">): DbTrxWriteRequest {\n const itemWithDefaults = this.table.def.beforePut\n ? { ...this.table.def.beforePut(item), ...item }\n : item;\n return { table: this.tableName, type: \"PUT\", item: itemWithDefaults, ...options };\n }\n\n trxUpdateRequest(\n key: RepoKey<T>,\n update: RepoUpdateData<T>,\n options?: Omit<RepoUpdate<T>, \"return\">,\n ): DbTrxWriteRequest {\n const updateWithDefaults = this.table.def.beforeUpdate\n ? { ...this.table.def.beforeUpdate(update), ...update }\n : update;\n return {\n table: this.tableName,\n type: \"UPDATE\",\n key: this.extractKey(key),\n update: updateWithDefaults,\n ...options,\n };\n }\n\n trxCreateRequest(item: RepoCreateItem<T>, options?: RepoCreate<T>): DbTrxWriteRequest {\n const { condition: otherCondition, ...otherOptions } = options ?? {};\n\n const condition = { $and: [{ [this.table.def.partitionKey]: { $exists: false } }] };\n\n if (otherCondition) {\n condition.$and.push(otherCondition);\n }\n\n const itemWithDefaults = this.table.def.beforePut\n ? { ...this.table.def.beforePut(item), ...item }\n : item;\n\n return {\n table: this.tableName,\n type: \"PUT\",\n item: itemWithDefaults,\n condition,\n ...otherOptions,\n };\n }\n}\n","import type { AttributeDefinition } from \"@aws-sdk/client-dynamodb\";\nimport { type TSchema } from \"typebox\";\nimport * as T from \"typebox\";\nimport type { Obj, ResolvedAttrType, TableDef, TableDesc, TableGsi, TableKey } from \"./types\";\n\nexport const resolveAttrType = (schema: TSchema, attrName: string): ResolvedAttrType => {\n if (T.IsString(schema) || T.IsLiteralString(schema)) {\n return T.IsOptional(schema) ? [\"S\", undefined] : [\"S\"];\n }\n\n if (T.IsNumber(schema) || T.IsLiteralNumber(schema)) {\n return T.IsOptional(schema) ? [\"N\", undefined] : [\"N\"];\n }\n\n if (T.IsObject(schema)) {\n const attrSchema = schema.properties[attrName];\n if (!attrSchema) return [undefined];\n return resolveAttrType(attrSchema, attrName);\n }\n\n if (T.IsIntersect(schema)) {\n return schema.allOf.flatMap((s) => resolveAttrType(s, attrName));\n }\n\n if (T.IsUnion(schema)) {\n return schema.anyOf.flatMap((s) => resolveAttrType(s, attrName));\n }\n\n return [];\n};\n\nexport const getAttrType = (\n schema: TSchema,\n attrName: string,\n allowedTypes: ResolvedAttrType = [\"S\", \"N\", undefined],\n): \"S\" | \"N\" => {\n const resolvedType = resolveAttrType(schema, attrName);\n if (resolvedType.includes(\"S\") && resolvedType.includes(\"N\")) {\n throw new Error(`Attribute ${attrName} cannot be both a number and a string.`);\n }\n if (!allowedTypes.includes(undefined) && resolvedType.includes(undefined)) {\n throw new Error(`Attribute ${attrName} cannot be optional.`);\n }\n return resolvedType.includes(\"S\") ? \"S\" : \"N\";\n};\n\nexport const extractAttribute = (\n schema: TSchema,\n attrName: string,\n allowedTypes?: ResolvedAttrType,\n): AttributeDefinition => {\n return {\n AttributeName: attrName,\n AttributeType: getAttrType(schema, attrName, allowedTypes),\n };\n};\n\nexport const extractTableKey = (\n schema: TSchema,\n attrName: string,\n allowedTypes?: ResolvedAttrType,\n): TableKey => {\n return {\n name: attrName,\n type: getAttrType(schema, attrName, allowedTypes),\n };\n};\n\nexport const extractTableDesc = (schema: TSchema, def: TableDef): TableDesc => {\n const gsis: TableGsi[] = Object.entries(def.gsis ?? {}).map(([indexName, gsi]) => {\n return {\n indexName,\n partitionKey: extractTableKey(schema, gsi.partitionKey),\n sortKey: gsi.sortKey ? extractTableKey(schema, gsi.sortKey) : undefined,\n projectionType: Array.isArray(gsi.projection) ? \"INCLUDE\" : (gsi.projection ?? \"ALL\"),\n nonKeyAttributes: Array.isArray(gsi.projection) ? gsi.projection : undefined,\n };\n });\n\n return {\n tableName: def.name,\n billingMode: def.billingMode ?? \"PAY_PER_REQUEST\",\n stream: def.stream,\n partitionKey: extractTableKey(schema, def.partitionKey, [\"N\", \"S\"]),\n sortKey: def.sortKey ? extractTableKey(schema, def.sortKey, [\"N\", \"S\"]) : undefined,\n gsis: gsis.length ? gsis : undefined,\n };\n};\n\nexport const chunk = <T = unknown>(arr: T[], chunkSize: number): T[][] => {\n if (chunkSize <= 0) return [];\n\n const chunks: T[][] = [];\n for (let i = 0; i < arr.length; i += chunkSize) {\n chunks.push(arr.slice(i, i + chunkSize));\n }\n\n return chunks;\n};\n\n// this currently creates sparse arrays if an undefined\n// value is found inside an array...not sure if thats valid?\nexport const removeUndefined = (obj: Obj) => {\n const stack = [obj];\n while (stack.length) {\n const currentObj = stack.pop();\n if (currentObj !== undefined) {\n Object.entries(currentObj).forEach(([k, v]) => {\n if (v && v instanceof Object) stack.push(v);\n else if (v === undefined) delete currentObj[k];\n });\n }\n }\n return obj;\n};\n","import {\n type BatchGetItemInput,\n type BatchWriteItemInput,\n ConditionalCheckFailedException,\n DynamoDB,\n DynamoDBClient,\n type DynamoDBClientConfig,\n ListTablesCommand,\n type WriteRequest,\n} from \"@aws-sdk/client-dynamodb\";\n//import { DynamoDBStreams } from '@aws-sdk/client-dynamodb-streams';\nimport type { TransactGetCommandInput, TransactWriteCommandInput } from \"@aws-sdk/lib-dynamodb\";\nimport * as Lib from \"@aws-sdk/lib-dynamodb\";\nimport sift from \"sift\";\nimport { ExpressionBuilder } from \"./expression-builder\";\nimport { Repository } from \"./repo\";\nimport type { Table } from \"./table\";\nimport type {\n DbBatchGet,\n DbBatchGetResponse,\n DbBatchWrite,\n DbBatchWriteResponse,\n DbConfig,\n DbCreate,\n DbDelete,\n DbExists,\n DbGet,\n DbListTables,\n DbPut,\n DbQuery,\n DbScan,\n DbTrxGetOrThrowResult,\n DbTrxGetRequest,\n DbTrxGetResult,\n DbTrxWriteRequest,\n DbUpdate,\n DbUpsert,\n Obj,\n} from \"./types\";\nimport { removeUndefined } from \"./util\";\n\n// TODO\n// - caching support?\n// - consider special \"limit\" handling for paged scans, maybe have \"scanLimit\" too\n// - consider \"sort\" shorthand for methods that return unpaginated collections\n// i.e. sort: { by: 'createdAt', dir: 'ASC' } or sort: (a,b) => a.createdAt - b.createdAt\n// - consider support for batchUpdates leveraging BatchExecute and PartiQL\n// - consider batchPut (batchPutOrThrow) and batchDelete that operate on a single table\n// - getGsi? throws if > 1 result, allows for assert, and filtering\n// - bug $in doesn't handle empty arrays, I imagine other operators are affected\n// - should create and upsert be here? I think not...\n\nexport class Db {\n readonly client: Lib.DynamoDBDocumentClient;\n readonly config: DbConfig | undefined;\n\n //protected streamEventListeners: DbStreamEventListener[] = [];\n\n constructor(\n clientOrConfig: DynamoDBClient | DynamoDB | DynamoDBClientConfig,\n dbConfig?: DbConfig,\n ) {\n if (clientOrConfig instanceof DynamoDBClient || clientOrConfig instanceof DynamoDB) {\n this.client = Lib.DynamoDBDocumentClient.from(new DynamoDBClient(clientOrConfig));\n } else {\n const { endpoint, ...clientConfig } = clientOrConfig;\n // client doesn't interpret an empty string endpoint as falsy so normalize things\n // here so consumers don't have to worry about that quirk\n this.client = Lib.DynamoDBDocumentClient.from(\n new DynamoDBClient({\n endpoint: endpoint || undefined,\n ...clientConfig,\n }),\n );\n }\n\n this.config = dbConfig;\n }\n\n createRepo<T extends Table<any, any>>(table: T): Repository<T> {\n return new Repository(table, this);\n }\n\n async listTables(data?: DbListTables): Promise<string[]> {\n const tables: string[] = [];\n let lastEvaluatedTableName: string | undefined;\n do {\n const output = await this.client.send(\n new ListTablesCommand({\n Limit: data?.limit,\n ExclusiveStartTableName: lastEvaluatedTableName,\n }),\n );\n\n tables.push(...(output.TableNames ?? []));\n lastEvaluatedTableName = output.LastEvaluatedTableName;\n } while (lastEvaluatedTableName);\n return tables;\n }\n\n async get<R = Obj>(data: DbGet): Promise<R | undefined> {\n const exp = new ExpressionBuilder();\n\n const input = new Lib.GetCommand({\n TableName: data.table,\n Key: data.key,\n ConsistentRead: data.consistent,\n ProjectionExpression: exp.projection(data.projection),\n ExpressionAttributeNames: exp.attributeNames,\n });\n\n const output = await this.client.send(input);\n\n if (output.Item && data.condition) {\n if (!sift(data.condition)(output.Item)) {\n return undefined;\n }\n }\n\n return output.Item as R;\n }\n\n async getOrThrow<R = Obj>(data: DbGet): Promise<R> {\n const item = await this.get(data);\n if (!item) {\n throw new Error(`Item not found in \"${data.table}\" table.`);\n }\n return item as R;\n }\n\n async put<R = Obj>(data: DbPut): Promise<R> {\n const exp = new ExpressionBuilder();\n\n const input = new Lib.PutCommand({\n TableName: data.table,\n Item: removeUndefined(data.item),\n ReturnValues: data.return === \"ALL_OLD\" ? \"ALL_OLD\" : undefined,\n ReturnValuesOnConditionCheckFailure: \"ALL_OLD\",\n ConditionExpression: exp.condition(data.condition),\n ExpressionAttributeNames: exp.attributeNames,\n ExpressionAttributeValues: exp.attributeValues,\n });\n\n const output = await this.client.send(input);\n\n return (data.return === \"ALL_OLD\" ? output.Attributes : data) as R;\n }\n\n async update<R = Obj>(data: DbUpdate): Promise<R | undefined> {\n const exp = new ExpressionBuilder();\n\n const condition = {\n $and: Object.keys(data.key).map((field) => ({\n [field]: { $exists: true },\n })),\n };\n\n if (data.condition) {\n condition.$and.push(data.condition);\n }\n\n const input = new Lib.UpdateCommand({\n TableName: data.table,\n Key: data.key,\n ReturnValues: data.return ?? \"ALL_NEW\",\n ReturnValuesOnConditionCheckFailure: \"ALL_OLD\",\n UpdateExpression: exp.update(data.update),\n ConditionExpression: exp.condition(condition),\n ExpressionAttributeNames: exp.attributeNames,\n ExpressionAttributeValues: exp.attributeValues,\n });\n\n const output = await this.client.send(input);\n\n return output.Attributes as R | undefined;\n }\n\n async create<R = Obj>(data: DbCreate): Promise<R> {\n const { condition: otherCondition, partitionKeyName, ...options } = data;\n\n const condition = { $and: [{ [partitionKeyName]: { $exists: false } }] };\n\n if (otherCondition) {\n condition.$and.push(otherCondition);\n }\n\n return this.put<R>({\n ...options,\n condition,\n return: \"ALL_NEW\",\n });\n }\n\n async upsert<R = Obj>(data: DbUpsert): Promise<R> {\n const { key, item, update, ...options } = data;\n try {\n return (await this.update({\n ...options,\n key,\n update,\n return: \"ALL_NEW\",\n })) as R;\n } catch (e) {\n if (e instanceof ConditionalCheckFailedException && !e.Item) {\n // TODO: deriving the partitionKeyName like this isn't foolproof\n return this.create({\n item,\n partitionKeyName: Object.keys(key)[0] ?? \"\",\n ...options,\n });\n }\n\n throw e;\n }\n }\n\n async delete<R = Obj>(data: DbDelete): Promise<R | undefined> {\n const exp = new ExpressionBuilder();\n\n const input = new Lib.DeleteCommand({\n TableName: data.table,\n Key: data.key,\n ReturnValues: data.return ?? \"ALL_OLD\",\n ConditionExpression: exp.condition(data.condition),\n ReturnValuesOnConditionCheckFailure: \"ALL_OLD\",\n ExpressionAttributeNames: exp.attributeNames,\n ExpressionAttributeValues: exp.attributeValues,\n });\n\n const output = await this.client.send(input);\n\n return output.Attributes as R | undefined;\n }\n\n async deleteOrThrow<R = Obj>(data: Omit<DbDelete, \"return\">): Promise<R> {\n const item = await this.delete<R>(data);\n if (!item) {\n throw new Error(`Item not found in \"${data.table}\" table.`);\n }\n return item;\n }\n\n async *queryPaged<R = Obj>(data: DbQuery): AsyncGenerator<R[]> {\n const exp = new ExpressionBuilder();\n\n let lastEvaluatedKey = data.startKey;\n do {\n const input = new Lib.QueryCommand({\n TableName: data.table,\n IndexName: data.index,\n ExclusiveStartKey: lastEvaluatedKey,\n Limit: data.limit,\n ScanIndexForward: data.sort !== \"DESC\",\n ConsistentRead: data.consistent,\n KeyConditionExpression: exp.condition(data.query),\n ProjectionExpression: exp.projection(data.projection),\n FilterExpression: exp.condition(data.filter),\n ExpressionAttributeNames: exp.attributeNames,\n ExpressionAttributeValues: exp.attributeValues,\n });\n\n const output = await this.client.send(input);\n\n if (output.Items?.length) {\n yield output.Items as R[];\n }\n lastEvaluatedKey = output.LastEvaluatedKey;\n } while (lastEvaluatedKey);\n }\n\n async query<R = Obj>(data: DbQuery): Promise<R[]> {\n const items: Obj[] = [];\n\n for await (const page of this.queryPaged(data)) {\n items.push(...page);\n }\n\n return items as R[];\n }\n\n async *scanPaged<R = Obj>(data: DbScan): AsyncGenerator<R[]> {\n const exp = new ExpressionBuilder();\n\n const totalSegments = data.parallel ?? 1;\n const segments = [...Array(totalSegments).keys()];\n\n // undefined: initial value when a start key isn't specified\n // null: scanning a particular segment is finished\n const lastEvaluatedKeys: (Obj | undefined | null)[] = segments.map(() => data.startKey);\n do {\n const results = await Promise.all(\n segments.map(async (segment) => {\n if (lastEvaluatedKeys[segment] === null) return [];\n\n const input = new Lib.ScanCommand({\n ExclusiveStartKey: lastEvaluatedKeys[segment],\n Segment: segment,\n TotalSegments: totalSegments,\n TableName: data.table,\n IndexName: data.index,\n Limit: data.limit,\n ConsistentRead: data.consistent,\n ProjectionExpression: exp.projection(data.projection),\n FilterExpression: exp.condition(data.filter),\n ExpressionAttributeNames: exp.attributeNames,\n ExpressionAttributeValues: exp.attributeValues,\n });\n\n const output = await this.client.send(input);\n lastEvaluatedKeys[segment] = output.LastEvaluatedKey ?? null;\n return output.Items ?? [];\n }),\n );\n\n const items = results.flat();\n\n if (items.length) {\n yield items as R[];\n }\n } while (lastEvaluatedKeys.some((key) => key !== null));\n }\n\n async scan<R = Obj>(data: DbScan): Promise<R[]> {\n const items: R[] = [];\n\n for await (const page of this.scanPaged(data)) {\n items.push(...(page as R[]));\n }\n\n return items;\n }\n\n async exists(data: DbExists): Promise<boolean> {\n const { query, ...otherOptions } = data;\n\n // we can't rely on the limit when a filter is being applied, since the filter is\n // applied after the scan/query page, so we only apply a limit of 1 when there is no filter.\n // otherwise, we page through the results and return true as soon as we see a single item\n const sharedOptions = {\n ...otherOptions,\n limit: data.filter ? undefined : 1,\n };\n\n const pagedItems = query\n ? this.queryPaged({ query, ...sharedOptions })\n : this.scanPaged(sharedOptions);\n\n for await (const items of pagedItems) {\n if (items.length) return true;\n }\n\n return false;\n }\n\n // TODO: test wildly different item sizes to make\n // sure retry algorithm works properly\n async batchGet(data: DbBatchGet): Promise<DbBatchGetResponse> {\n const result: DbBatchGetResponse = { items: {} };\n\n const tableData = Object.fromEntries(\n Object.entries(data).map(([table, request]) => {\n const exp = new ExpressionBuilder();\n const sifter = request?.condition ? sift(request?.condition) : undefined;\n\n result.items[table] = [];\n\n return [\n table,\n {\n request: {\n ConsistentRead: request?.consistent,\n ProjectionExpression: exp.projection(request?.projection),\n ExpressionAttributeNames: exp.attributeNames,\n },\n keyNames: Object.keys(request.keys.at(0) ?? {}),\n itemIndexMap: new Map<string, number>(),\n sifter,\n },\n ];\n }),\n );\n\n const flattenedBatches = Object.entries(data).flatMap(([table, request]) => {\n return request.keys.map((key, i) => {\n const itemIndexKey =\n tableData[table]?.keyNames.map((keyName) => key[keyName]).join(\"|\") ?? \"\";\n tableData[table]?.itemIndexMap.set(itemIndexKey, i);\n return [table, key] as const;\n });\n });\n\n let batchSize = 100;\n let retryCount = 0;\n\n while (flattenedBatches.length && retryCount < 5) {\n const batch = flattenedBatches.splice(0, batchSize || 1);\n\n const request: BatchGetItemInput[\"RequestItems\"] = {};\n for (const [table, key] of batch) {\n if (!request[table]) {\n request[table] = { ...tableData[table]?.request, Keys: [] };\n }\n\n request[table].Keys?.push(key);\n }\n\n const input = new Lib.BatchGetCommand({ RequestItems: request });\n const output = await this.client.send(input);\n\n for (const [table, items] of Object.entries(output.Responses ?? {})) {\n //just to appease typescript\n if (!result.items[table]) continue;\n\n for (const item of items) {\n // items that fail the condition are treated the same as non-existent keys,\n // i.e. they are not present in items/unprocessed arrays\n if (tableData[table]?.sifter && !tableData[table].sifter(item)) {\n continue;\n }\n\n result.items[table].push(item);\n }\n }\n\n // handle unprocessed requests\n let unprocessedKeyCount = 0;\n for (const [table, request] of Object.entries(output.UnprocessedKeys ?? {})) {\n const unprocessed = (request.Keys ?? []).map((key) => [table, key] as const);\n flattenedBatches.push(...unprocessed);\n unprocessedKeyCount += unprocessed.length;\n }\n\n //move immediately to next batch if all requests were processed\n if (!unprocessedKeyCount) {\n retryCount = 0;\n continue;\n }\n\n // reduce batch size so we don't continue to overfetch,\n // but do it gradually by only splitting the difference\n batchSize -= Math.floor(unprocessedKeyCount / 2);\n\n retryCount++;\n }\n\n // anything left in flattened batches needs to be returned in unprocessed\n if (flattenedBatches.length) {\n result.unprocessed = {};\n for (const [table, key] of flattenedBatches) {\n if (!result.unprocessed[table]) {\n result.unprocessed[table] = {\n ...tableData[table]?.request,\n keys: [],\n };\n }\n result.unprocessed[table].keys.push(key);\n }\n }\n\n // finally, sort any returned items by their original index to preserve order\n for (const [table, items] of Object.entries(result.items)) {\n const itemIndexMap = tableData[table]?.itemIndexMap;\n const keyNames = tableData[table]?.keyNames;\n\n if (!itemIndexMap || !keyNames) continue;\n\n items.sort((item1, item2) => {\n const item1IndexKey = keyNames.map((keyName) => item1[keyName]).join(\"|\");\n const item2IndexKey = keyNames.map((keyName) => item2[keyName]).join(\"|\");\n\n return (itemIndexMap.get(item1IndexKey) ?? 0) - (itemIndexMap.get(item2IndexKey) ?? 0);\n });\n }\n\n return result;\n }\n\n async batchGetOrThrow(data: DbBatchGet): Promise<DbBatchGetResponse[\"items\"]> {\n const { items, unprocessed } = await this.batchGet(data);\n\n for (const table of Object.keys(data)) {\n if (unprocessed?.[table]) {\n throw new Error(`One or more batch get requests were not processed in \"${table}\" table.`);\n }\n\n if (items[table]?.length !== data[table]?.keys?.length) {\n throw new Error(`One or more items were not found in \"${table}\" table.`);\n }\n }\n\n return items;\n }\n\n // TODO: test wildly different item sizes to make\n // sure retry algorithm works properly\n async batchWrite(data: DbBatchWrite): Promise<DbBatchWriteResponse> {\n const result: DbBatchWriteResponse = { items: {} };\n\n const flattenedBatches = Object.keys(data).flatMap((table) => {\n return (\n data[table]?.map((r) => {\n if (r.type === \"DELETE\") {\n return [table, { DeleteRequest: { Key: r.key } }] as [string, WriteRequest];\n } else {\n return [table, { PutRequest: { Item: r.item } }] as [string, WriteRequest];\n }\n }) ?? []\n );\n });\n\n let batchSize = 25;\n let retryCount = 0;\n\n while (flattenedBatches.length && retryCount < 5) {\n const batch = flattenedBatches.splice(0, batchSize || 1);\n\n const request: BatchWriteItemInput[\"RequestItems\"] = {};\n for (const [table, req] of batch) {\n if (!request[table]) {\n request[table] = [];\n }\n request[table].push(req);\n }\n\n const input = new Lib.BatchWriteCommand({ RequestItems: request });\n const output = await this.client.send(input);\n\n // handle unprocessed requests\n let unprocessedKeyCount = 0;\n for (const [table, requests] of Object.entries(output.UnprocessedItems ?? {})) {\n for (const request of requests) {\n flattenedBatches.push([table, request]);\n unprocessedKeyCount++;\n }\n }\n\n //move immediately to next batch if all requests were processed\n if (!unprocessedKeyCount) {\n retryCount = 0;\n continue;\n }\n\n // reduce batch size so we don't continue to overwrite,\n // but do it gradually by only splitting the difference\n batchSize -= Math.floor(unprocessedKeyCount / 2);\n\n retryCount++;\n }\n\n // anything left in flattened batches needs to be returned in unprocessed\n if (flattenedBatches.length) {\n result.unprocessed = {};\n for (const [table, request] of flattenedBatches) {\n if (!result.unprocessed[table]) {\n result.unprocessed[table] = [];\n }\n\n if (\"DeleteRequest\" in request && request.DeleteRequest?.Key) {\n result.unprocessed[table].push({\n type: \"DELETE\",\n key: request.DeleteRequest.Key,\n });\n } else if (\"PutRequest\" in request && request.PutRequest?.Item) {\n result.unprocessed[table].push({\n type: \"PUT\",\n item: request.PutRequest.Item,\n });\n }\n }\n }\n\n return result;\n }\n\n async trxGet<R extends DbTrxGetRequest[]>(...requests: R): Promise<DbTrxGetResult<R>> {\n const trxItems: TransactGetCommandInput[\"TransactItems\"] = requests.map((request) => {\n const exp = new ExpressionBuilder();\n return {\n Get: {\n Key: request.key,\n TableName: request.table,\n ProjectionExpression: exp.projection(request?.projection),\n ExpressionAttributeNames: exp.attributeNames,\n },\n };\n });\n\n const input = new Lib.TransactGetCommand({ TransactItems: trxItems });\n const output = await this.client.send(input);\n\n return (\n (output.Responses?.map((response, i) => {\n if (requests[i]?.condition && !sift(requests[i].condition)(response.Item)) {\n return undefined;\n }\n\n return response.Item;\n }) as DbTrxGetResult<R>) ?? []\n );\n }\n\n async trxGetOrThrow<R extends DbTrxGetRequest[]>(\n ...requests: R\n ): Promise<DbTrxGetOrThrowResult<R>> {\n const items = await this.trxGet(...requests);\n for (let i = 0; i < items.length; i++) {\n if (!items[i]) {\n throw new Error(`One or more items were not found in \"${requests[i]?.table}\" table.`);\n }\n }\n\n return items as DbTrxGetOrThrowResult<R>;\n }\n\n async trxWrite(...requests: DbTrxWriteRequest[]): Promise<void> {\n const trxItems: TransactWriteCommandInput[\"TransactItems\"] = requests.map((request) => {\n const exp = new ExpressionBuilder();\n\n if (request.type === \"CONDITION\" || request.type === \"DELETE\") {\n return {\n [request.type === \"CONDITION\" ? \"ConditionCheck\" : \"Delete\"]: {\n TableName: request.table,\n Key: request.key,\n ConditionExpression: exp.condition(request.condition),\n ExpressionAttributeNames: exp.attributeNames,\n ExpressionAttributeValues: exp.attributeValues,\n },\n };\n }\n\n if (request.type === \"PUT\") {\n return {\n Put: {\n TableName: request.table,\n Item: removeUndefined(request.item),\n ConditionExpression: exp.condition(request.condition),\n ExpressionAttributeNames: exp.attributeNames,\n ExpressionAttributeValues: exp.attributeValues,\n },\n };\n }\n\n return {\n Update: {\n Key: request.key,\n TableName: request.table,\n UpdateExpression: exp.update(request.update),\n ConditionExpression: exp.condition(request.condition),\n ExpressionAttributeNames: exp.attributeNames,\n ExpressionAttributeValues: exp.attributeValues,\n },\n };\n });\n\n const input = new Lib.TransactWriteCommand({ TransactItems: trxItems });\n await this.client.send(input);\n }\n\n /* Dynamodb Streams */\n\n /*\n\n\tasync enableStreamEvents(data?: DbEnableEventStreams): Promise<void> {\n\t\tconst tables = data?.tables ?? (await this.listTables());\n\n\t\tconst streamsClient = new DynamoDBStreams({\n\t\t\tendpoint: this.client.config.endpoint,\n\t\t\tcredentials: this.client.config.credentials,\n\t\t\tregion: this.client.config.region,\n\t\t});\n\n\t\tawait Promise.all(\n\t\t\ttables.map(async (table) => {\n\t\t\t\tconst tableDesc = await this.client.send(new DescribeTableCommand({ TableName: table }));\n\n\t\t\t\tconst latestStreamArn = tableDesc.Table?.LatestStreamArn;\n\n\t\t\t\tif (!latestStreamArn) {\n\t\t\t\t\tif (data?.tables) {\n\t\t\t\t\t\tthrow new Error(`Table \"${table}\" does not have streaming enabled.`);\n\t\t\t\t\t}\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst eventStreamer = new EventStreamer(streamsClient, latestStreamArn);\n\t\t\t\teventStreamer.startPolling();\n\t\t\t}),\n\t\t);\n\t}\n\n\tasync disableStreamEvents(): Promise<void> {}\n\n\tsubscribe(listener: DbStreamEventListener): void {\n\t\tthis.streamEventListeners.push(listener);\n\t}\n\n\tunsubscribe(listener: DbStreamEventListener): void {\n\t\tthis.streamEventListeners = this.streamEventListeners.filter((l) => l !== listener);\n\t}\n\n\tunsubscribeAll(): void {\n\t\tthis.streamEventListeners = [];\n\t}\n\t*/\n}\n\n// respondentRepo.onChange((respondent) => , { filter: (r1, r2) => r1.status !== r2.status } )\n/*\nconst time = new Date(record.dynamodb.ApproximateCreationDateTime).getTime();\nif (time >= startTime) {\n\tfor (const listener of this.streamEventListeners) {\n\t\tlistener({\n\t\t\ttable,\n\t\t\ttime,\n\t\t\tid: record.eventID,\n\t\t\ttype: record.eventName,\n\t\t\tkey: record.dynamodb.Keys,\n\t\t\toldItem: record.dynamodb.OldImage,\n\t\t\tnewItem: record.dynamodb.NewImage,\n\t\t});\n\t}\n}\n*/\n","import {\n type AttributeDefinition,\n CreateTableCommand,\n DeleteTableCommand,\n type KeySchemaElement,\n UpdateTimeToLiveCommand,\n} from \"@aws-sdk/client-dynamodb\";\nimport type { DynamoDBDocumentClient } from \"@aws-sdk/lib-dynamodb\";\nimport type { Static, TSchema } from \"typebox\";\n//import { Table as CdkTable, type GlobalSecondaryIndexProps, type TableProps } from 'aws-cdk-lib/aws-dynamodb';\n//import type { Construct } from 'constructs';\nimport type { TableDef, TableKey } from \"./types\";\nimport { extractTableDesc } from \"./util\";\n\nexport class Table<\n Schema extends TSchema = TSchema,\n Def extends TableDef<Static<Schema>> = TableDef<Static<Schema>>,\n> {\n readonly schema: TSchema;\n readonly def: TableDef;\n\n constructor(schema: Schema, def: Def) {\n this.schema = schema;\n this.def = def;\n }\n\n async drop(client: DynamoDBDocumentClient): Promise<void> {\n await client.send(new DeleteTableCommand({ TableName: this.def.name }));\n }\n\n async createTable(client: DynamoDBDocumentClient): Promise<void> {\n const desc = extractTableDesc(this.schema, this.def);\n\n const attributes = new Map<string, AttributeDefinition>();\n const setAttribute = (tableKey: TableKey) => {\n attributes.set(tableKey.name, { AttributeName: tableKey.name, AttributeType: tableKey.type });\n };\n\n const keySchema: KeySchemaElement[] = [\n { AttributeName: desc.partitionKey.name, KeyType: \"HASH\" },\n ];\n setAttribute(desc.partitionKey);\n\n if (desc.sortKey) {\n keySchema.push({ AttributeName: desc.sortKey.name, KeyType: \"RANGE\" });\n setAttribute(desc.sortKey);\n }\n\n const gsis = desc.gsis?.map((gsi) => {\n const gsiKeySchema: KeySchemaElement[] = [\n { AttributeName: gsi.partitionKey.name, KeyType: \"HASH\" },\n ];\n setAttribute(gsi.partitionKey);\n if (gsi.sortKey) {\n gsiKeySchema.push({ AttributeName: gsi.sortKey.name, KeyType: \"RANGE\" });\n setAttribute(gsi.sortKey);\n }\n return {\n IndexName: gsi.indexName,\n KeySchema: gsiKeySchema,\n Projection: {\n ProjectionType: gsi.projectionType,\n NonKeyAttributes: gsi.nonKeyAttributes,\n },\n };\n });\n\n await client.send(\n new CreateTableCommand({\n TableName: desc.tableName,\n KeySchema: keySchema,\n AttributeDefinitions: [...attributes.values()],\n BillingMode: desc.billingMode,\n GlobalSecondaryIndexes: gsis,\n StreamSpecification: desc.stream\n ? {\n StreamEnabled: true,\n StreamViewType: desc.stream,\n }\n : undefined,\n }),\n );\n\n if (desc.ttlAttribute) {\n await client.send(\n new UpdateTimeToLiveCommand({\n TableName: desc.tableName,\n TimeToLiveSpecification: {\n Enabled: true,\n AttributeName: desc.ttlAttribute,\n },\n }),\n );\n }\n }\n\n /*\n\tcreateCdkTable(construct: Construct): CdkTable {\n\t\tconst { ttlAttribute, gsis = [], ...tableProps } = extractTableDesc(this.schema, this.def);\n\n\t\tconst table = new CdkTable(construct, `${tableProps.tableName}-table`, {\n\t\t\t...(tableProps as TableProps),\n\t\t\ttimeToLiveAttribute: ttlAttribute,\n\t\t});\n\n\t\tfor (const gsi of gsis) {\n\t\t\ttable.addGlobalSecondaryIndex(gsi as GlobalSecondaryIndexProps);\n\t\t}\n\n\t\treturn table;\n\t}\n\t*/\n}\n"],"mappings":";;;;;AAEA,MAAM,YAAY;CAAC;CAAK;CAAM;CAAK;CAAM;CAAK;CAAM;CAAQ;CAAQ;CAAK;CAAI;AAC7E,MAAM,gBAA6B;CACjC,KAAK;CACL,KAAK;CACL,KAAK;CACL,MAAM;CACN,KAAK;CACL,MAAM;CACP;AAID,MAAa,eAAe,QAA6B;AACvD,KAAI,CAAC,OAAO,OAAO,QAAQ,YAAY,MAAM,QAAQ,IAAI,CAAE,QAAO;AAClE,QAAO,OAAO,KAAK,IAAI,CAAC,OAAO,OAAO,GAAG,WAAW,IAAI,CAAC;;AAG3D,IAAa,oBAAb,MAA+B;CAC7B,4BAA+B,IAAI,KAAqB;CACxD,6BAAgC,IAAI,KAAsB;CAE1D,IAAI,iBAA0C;AAC5C,SAAO,KAAK,UAAU,OAClB,OAAO,YACL,CAAC,GAAG,KAAK,UAAU,SAAS,CAAC,CAAC,KAAK,CAAC,MAAM,iBAAiB,CAAC,aAAa,KAAK,CAAC,CAChF,GACD,KAAA;;CAGN,IAAI,kBAAmC;AACrC,SAAO,KAAK,WAAW,OACnB,OAAO,YACL,CAAC,GAAG,KAAK,WAAW,SAAS,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,aAAa,MAAM,CAAC,CACnF,GACD,KAAA;;CAGN,QAAc;AACZ,OAAK,UAAU,OAAO;AACtB,OAAK,WAAW,OAAO;;CAGzB,WAAW,MAAsB;EAC/B,MAAM,WAAW,KAAK,MAAM,IAAI;EAChC,MAAM,eAAyB,EAAE;AAEjC,OAAK,MAAM,WAAW,UAAU;GAC9B,IAAI,cAAc,KAAK,UAAU,IAAI,QAAQ;AAC7C,OAAI,CAAC,aAAa;AAChB,kBAAc,IAAI,KAAK,UAAU;AACjC,SAAK,UAAU,IAAI,SAAS,YAAY;;AAG1C,gBAAa,KAAK,YAAY;;AAGhC,SAAO,aAAa,KAAK,IAAI;;CAG/B,YAAY,OAAwB;EAClC,IAAI,cAAc,KAAK,WAAW,IAAI,MAAM;AAC5C,MAAI,CAAC,aAAa;AAChB,iBAAc,IAAI,KAAK,WAAW;AAClC,QAAK,WAAW,IAAI,OAAO,YAAY;;AAGzC,SAAO;;CAGT,kBAAkB,aAA8B;AAC9C,MAAI,YAAY,YAAY,EAAE;AAC5B,OAAI,YAAY,MAAO,QAAO,KAAK,WAAW,YAAY,MAAM;AAChE,SAAM,IAAI,MAAM,6BAA6B;;AAG/C,SAAO,KAAK,YAAY,YAAY;;CAGtC,WAA2C,OAAoD;AAC7F,SAAO,OAAO,KAAK,SAAS,KAAK,WAAW,KAAK,CAAC,CAAC,KAAK,KAAK;;CAG/D,UAAqC,YAAyD;AAC5F,MAAI,CAAC,WAAY,QAAO,KAAA;EAExB,MAAM,cAAwB,EAAE;AAChC,OAAK,MAAM,CAAC,KAAK,QAAQ,OAAO,QAAQ,WAAW,CACjD,KAAI,QAAQ,QAAQ;AAClB,OAAI,CAAC,MAAM,QAAQ,IAAI,CAAE,OAAM,IAAI,MAAM,iCAAiC;AAC1E,eAAY,KAAK,IAAI,IAAI,KAAK,MAAM,KAAK,UAAU,EAAE,CAAC,CAAC,KAAK,QAAQ,CAAC,GAAG;aAC/D,QAAQ,SAAS,MAAM,QAAQ,IAAI,EAAE;AAC9C,OAAI,CAAC,MAAM,QAAQ,IAAI,CAAE,OAAM,IAAI,MAAM,gCAAgC;AACzE,eAAY,KAAK,IAAI,IAAI,KAAK,MAAM,KAAK,UAAU,EAAE,CAAC,CAAC,KAAK,OAAO,CAAC,GAAG;aAC9D,QAAQ,QAAQ;AACzB,OAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,OAAM,IAAI,MAAM,kCAAkC;AACvF,eAAY,KAAK,OAAO,KAAK,UAAU,IAAI,GAAG;aACrC,CAAC,IAAI,WAAW,IAAI,CAC7B,aAAY,KAAK,KAAK,iBAAiB,KAAK,IAAI,CAAC;MAEjD,OAAM,IAAI,MACR,wBAAwB,IAAI,kDAC7B;AAIL,SAAO,YAAY,KAAK,QAAQ;;CAGlC,OAAkC,YAAyD;AACzF,MAAI,eAAe,KAAA,EAAW,QAAO,KAAA;EAErC,MAAM,gBAA0B,EAAE;EAClC,MAAM,mBAA6B,EAAE;EACrC,MAAM,mBAA6B,EAAE;EACrC,MAAM,mBAA6B,EAAE;AAErC,OAAK,MAAM,CAAC,MAAM,mBAAmB,OAAO,QAAQ,WAAW,EAAE;GAC/D,MAAM,cAAc,KAAK,WAAW,KAAK;AAEzC,OAAI,mBAAmB,KAAA,EACrB,kBAAiB,KAAK,YAAY;YACzB,YAAY,eAAe,CACpC,KAAI,eAAe,YAAY,KAC7B,kBAAiB,KAAK,YAAY;YACzB,eAAe,YAAY,KAAA,KAAa,eAAe,YAAY,KAAA,GAAW;IACvF,MAAM,aAAa,eAAe,UAAU,mBAAmB;IAC/D,MAAM,UAAU,eAAe,WAAW,eAAe;AACzD,eAAW,KACT,GAAG,YAAY,GAAG,KAAK,YAAY,mBAAmB,MAAM,UAAU,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,GACjG;SAED,eAAc,KAAK,GAAG,YAAY,KAAK,KAAK,kBAAkB,MAAM,eAAe,GAAG;OAGxF,eAAc,KAAK,GAAG,YAAY,KAAK,KAAK,YAAY,eAAe,GAAG;;EAI9E,IAAI,mBAAmB;AACvB,MAAI,cAAc,OAChB,qBAAoB,OAAO,cAAc,KAAK,KAAK,CAAC;AAGtD,MAAI,iBAAiB,OACnB,qBAAoB,UAAU,iBAAiB,KAAK,KAAK,CAAC;AAG5D,MAAI,iBAAiB,OACnB,qBAAoB,OAAO,iBAAiB,KAAK,KAAK,CAAC;AAGzD,MAAI,iBAAiB,OACnB,qBAAoB,UAAU,iBAAiB,KAAK,KAAK,CAAC;AAG5D,SAAO;;CAGT,iBAA2B,MAAc,KAAsB;AAC7D,MAAI,CAAC,OAAO,OAAO,QAAQ,YAAY,CAAC,OAAO,KAAK,IAAI,CAAC,GAAG,EAAE,EAAE,WAAW,IAAI,CAC7E,QAAO,KAAK,iBAAiB,MAAM,EAAE,KAAK,KAAK,CAAC;EAGlD,MAAM,cAAc,KAAK,WAAW,KAAK;EACzC,MAAM,CAAC,UAAU,WAAW,OAAO,QAAQ,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG;AAE7D,MAAI,cAAc,WAAW;AAC3B,OAAI,YAAY,QAAQ,IAAI,QAAQ,MAClC,QAAO,GAAG,YAAY,GAAG,cAAc,UAAU,GAAG,KAAK,WAAW,QAAQ,MAAM;AAGpF,OAAI,CAAC;IAAC;IAAU;IAAW;IAAS,CAAC,SAAS,OAAO,QAAQ,CAC3D,OAAM,IAAI,MAAM,GAAG,SAAS,sCAAsC;AAEpE,UAAO,GAAG,YAAY,GAAG,cAAc,UAAU,GAAG,KAAK,YAAY,QAAQ;;AAG/E,UAAQ,UAAR;GACE,KAAK;AACH,QAAI,CAAC,MAAM,QAAQ,QAAQ,CACzB,OAAM,IAAI,MAAM,yCAAyC;AAE3D,WAAO,GAAG,YAAY,OAAO,QAAQ,KAAK,OAAO,KAAK,YAAY,GAAG,CAAC,CAAC,KAAK,KAAK,CAAC;GAEpF,KAAK;AACH,QAAI,CAAC,MAAM,QAAQ,QAAQ,CACzB,OAAM,IAAI,MAAM,0CAA0C;AAE5D,WAAO,GAAG,YAAY,WAAW,QAAQ,KAAK,OAAO,KAAK,YAAY,GAAG,CAAC,CAAC,KAAK,KAAK,CAAC;GAExF,KAAK;AACH,QAAI,OAAO,YAAY,SACrB,OAAM,IAAI,MAAM,6CAA6C;AAE/D,WAAO,eAAe,YAAY,IAAI,KAAK,YAAY,QAAQ,CAAC;GAElE,KAAK,YACH,QAAO,YAAY,YAAY,IAAI,KAAK,YAAY,QAAQ,CAAC;GAE/D,KAAK;AACH,QAAI,CAAC,MAAM,QAAQ,QAAQ,IAAI,QAAQ,WAAW,EAChD,OAAM,IAAI,MAAM,oEAAoE;AAEtF,WAAO,GAAG,YAAY,WAAW,KAAK,YAAY,QAAQ,GAAG,CAAC,OAAO,KAAK,YAAY,QAAQ,GAAG;GAEnG,KAAK;AACH,QAAI,OAAO,YAAY,UACrB,OAAM,IAAI,MAAM,8CAA8C;AAEhE,WAAO,UACH,oBAAoB,YAAY,KAChC,wBAAwB,YAAY;GAE1C,KAAK;AACH,QAAI,CAAC,UAAU,SAAS,QAAe,CACrC,OAAM,IAAI,MAAM,+CAA+C,UAAU,KAAK,IAAI,GAAG;AAEvF,WAAO,kBAAkB,YAAY;GAEvC,KAAK,SAAS;IACZ,MAAM,UAAU,OAAO,YAAY,WAAW,EAAE,KAAK,SAAS,GAAG;AACjE,QACE,CAAC,WACD,OAAO,YAAY,YACnB,CAAC,OAAO,KAAK,QAAQ,CAAC,GAAG,EAAE,EAAE,WAAW,IAAI,CAE5C,OAAM,IAAI,MAAM,kEAAkE;IAEpF,MAAM,CAAC,cAAc,eAAe,OAAO,QAAQ,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG;AAEzE,QAAI,CAAC,cAAc,cACjB,OAAM,IAAI,MAAM,kEAAkE;AAGpF,QAAI,CAAC;KAAC;KAAU;KAAW;KAAS,CAAC,SAAS,OAAO,YAAY,CAC/D,OAAM,IAAI,MAAM,GAAG,SAAS,sCAAsC;AAGpE,WAAO,QAAQ,YAAY,IAAI,cAAc,cAAc,GAAG,KAAK,YAAY,YAAY;;GAG7F,QACE,OAAM,IAAI,MAAM,qBAAqB,SAAS,GAAG;;;CAIvD,kBAA4B,MAAc,SAA0B;AAClE,MAAI,YAAY,QAAQ,EAAE;AACxB,OAAI,QAAQ,cAAc;IACxB,MAAM,SAAS,MAAM,QAAQ,QAAQ,aAAa,GAC9C,QAAQ,eACR,CAAC,MAAM,QAAQ,aAAa;AAChC,WAAO,iBAAiB,KAAK,WAAW,OAAO,GAAG,CAAC,IAAI,KAAK,kBAAkB,MAAM,OAAO,GAAG,CAAC;;AAGjG,OAAI,QAAQ,KACV,QAAO,KAAK,kBAAkB,MAAM,QAAQ,KAAK;AAGnD,OAAI,QAAQ,UAAU,KAAA,KAAa,QAAQ,WAAW,KAAA,GAAW;IAC/D,MAAM,eAAe,QAAQ,QAAQ,MAAM;IAC3C,MAAM,cAAc,QAAQ,SAAS,QAAQ;AAE7C,WAAO,IADQ,MAAM,QAAQ,YAAY,GAAG,cAAc,CAAC,EAAE,OAAO,MAAM,EAAE,YAAY,EACvE,KAAK,UAAU,KAAK,kBAAkB,MAAM,MAAM,CAAC,CAAC,KAAK,IAAI,aAAa,GAAG;;AAGhG,OAAI,QAAQ,YAAY,KAAA,KAAa,QAAQ,aAAa,KAAA,GAAW;IACnE,MAAM,cAAc,QAAQ,WAAW,QAAQ;IAC/C,MAAM,sBAAsB,YAAY,YAAY,GAAG,cAAc,CAAC,YAAY,CAAC,MAAM;AAIzF,WAAO,gBAHQ,QAAQ,UACnB,CAAC,EAAE,OAAO,MAAM,EAAE,oBAAoB,GACtC,CAAC,qBAAqB,EAAE,OAAO,MAAM,CAAC,EACb,KAAK,UAAU,KAAK,kBAAkB,MAAM,MAAM,CAAC,CAAC,KAAK,KAAK,CAAC;;;AAIhG,SAAO,KAAK,kBAAkB,QAAQ;;;;;AC/N1C,IAAa,aAAb,MAAmD;CACjD;CACA;CACA,YAAY,OAAU,IAAQ;AAC5B,OAAK,QAAQ;AACb,OAAK,KAAK;;CAGZ,IAAI,YAAoB;AACtB,SAAO,GAAG,KAAK,GAAG,QAAQ,mBAAmB,KAAK,KAAK,MAAM,IAAI;;CAGnE,IAAI,MAA0B;AAC5B,SAAO,KAAK,MAAM;;CAGpB,WAAW,MAA8B;EACvC,MAAM,EAAE,cAAc,YAAY,KAAK,MAAM;AAC7C,MAAI,QACF,QAAO;IACJ,eAAe,KAAK;IACpB,UAAU,KAAK;GACjB;AAGH,SAAO,GAAG,eAAe,KAAK,eAAe;;CAG/C,MAAM,IAA0B,KAAiB,SAA2C;AAC1F,SAAO,KAAK,GAAG,IAAI;GAAE,OAAO,KAAK;GAAW,KAAK,KAAK,WAAW,IAAI;GAAE,GAAG;GAAS,CAAC;;CAGtF,MAAM,WACJ,KACA,SACqC;AACrC,SAAO,KAAK,GAAG,WAAW;GAAE,OAAO,KAAK;GAAW,KAAK,KAAK,WAAW,IAAI;GAAE,GAAG;GAAS,CAAC;;CAG7F,MAAM,IAA0B,MAAsB,SAA2C;EAC/F,MAAM,mBAAmB,KAAK,MAAM,IAAI,YACpC;GAAE,GAAG,KAAK,MAAM,IAAI,UAAU,KAAK;GAAE,GAAG;GAAM,GAC9C;AACJ,SAAO,KAAK,GAAG,IAAI;GAAE,OAAO,KAAK;GAAW,MAAM;GAAkB,GAAG;GAAS,CAAC;;CAGnF,MAAM,OACJ,KACA,QACA,SACiC;EACjC,MAAM,qBAAqB,KAAK,MAAM,IAAI,eACtC;GAAE,GAAG,KAAK,MAAM,IAAI,aAAa,OAAO;GAAE,GAAG;GAAQ,GACrD;AACJ,SAAO,KAAK,GAAG,OAAO;GACpB,OAAO,KAAK;GACZ,KAAK,KAAK,WAAW,IAAI;GACzB,QAAQ;GACR,GAAG;GACJ,CAAC;;CAGJ,MAAM,OACJ,MACA,SACiC;EACjC,MAAM,mBAAmB,KAAK,MAAM,IAAI,YACpC;GAAE,GAAG,KAAK,MAAM,IAAI,UAAU,KAAK;GAAE,GAAG;GAAM,GAC9C;AACJ,SAAO,KAAK,GAAG,OAAO;GACpB,OAAO,KAAK;GACZ,MAAM;GACN,kBAAkB,KAAK,MAAM,IAAI;GACjC,GAAG;GACJ,CAAC;;CAGJ,MAAM,OAAO,MAAmD;EAC9D,MAAM,EAAE,QAAQ,MAAM,KAAK,GAAG,YAAY;EAC1C,MAAM,qBAAqB,KAAK,MAAM,IAAI,eACtC;GAAE,GAAG,KAAK,MAAM,IAAI,aAAa,OAAO;GAAE,GAAG;GAAQ,GACrD;EACJ,MAAM,mBAAmB,KAAK,MAAM,IAAI,YACpC;GAAE,GAAG,KAAK,MAAM,IAAI,UAAU,KAAK;GAAE,GAAG;GAAM,GAC9C;AACJ,SAAO,KAAK,GAAG,OAAO;GACpB,OAAO,KAAK;GACZ,KAAK,KAAK,WAAW,IAAI;GACzB,QAAQ;GACR,MAAM;GACN,GAAG;GACJ,CAAC;;CAGJ,MAAM,OAAO,KAAiB,SAAuD;AACnF,SAAO,KAAK,GAAG,OAAO;GAAE,OAAO,KAAK;GAAW,KAAK,KAAK,WAAW,IAAI;GAAE,GAAG;GAAS,CAAC;;CAGzF,MAAM,cACJ,KACA,SACqC;AACrC,SAAO,KAAK,GAAG,cAAc;GAAE,OAAO,KAAK;GAAW,KAAK,KAAK,WAAW,IAAI;GAAE,GAAG;GAAS,CAAC;;CAGhG,MAAM,MAA8B,OAAY,SAA6C;AAC3F,SAAO,KAAK,GAAG,MAAM;GAAE,OAAO,KAAK;GAAW;GAAO,GAAG;GAAS,CAAC;;CAGpE,OAAO,WAAmC,OAAY,SAAyC;AAC7F,SAAO,KAAK,GAAG,WAAW;GAAE,OAAO,KAAK;GAAW;GAAO,GAAG;GAAS,CAAC;;CAMzE,MAAM,SACJ,KACA,OACA,SACmC;AACnC,SAAO,KAAK,GAAG,MAAM;GAAE,OAAO,KAAK;GAAW,OAAO;GAAK;GAAO,GAAG;GAAS,CAAC;;CAGhF,OAAO,cACL,KACA,OACA,SAC+B;AAC/B,SAAO,KAAK,GAAG,WAAW;GACxB,OAAO,KAAK;GACZ,OAAO;GACP;GACA,GAAG;GACJ,CAAC;;CAGJ,MAAM,KAA4B,SAA4C;AAC5E,SAAO,KAAK,GAAG,KAAK;GAAE,OAAO,KAAK;GAAW,GAAG;GAAS,CAAC;;CAG5D,OAAO,UAAiC,SAAwC;AAC9E,SAAO,KAAK,GAAG,UAAU;GAAE,OAAO,KAAK;GAAW,GAAG;GAAS,CAAC;;CAGjE,MAAM,QACJ,KACA,SACkC;AAClC,SAAO,KAAK,GAAG,KAAK;GAAE,OAAO,KAAK;GAAW,OAAO;GAAK,GAAG;GAAS,CAAC;;CAGxE,OAAO,aACL,KACA,SAC8B;AAC9B,SAAO,KAAK,GAAG,UAAU;GACvB,OAAO,KAAK;GACZ,OAAO;GACP,GAAG;GACJ,CAAC;;CAGJ,MAAM,OAAO,SAA2C;AACtD,SAAO,KAAK,GAAG,OAAO;GACpB,OAAO,KAAK;GACZ,YAAY,CAAC,KAAK,MAAM,IAAI,aAAa;GACzC,GAAG;GACJ,CAAC;;CAGJ,MAAM,UAAU,KAAkB,SAA2C;AAC3E,SAAO,KAAK,GAAG,OAAO;GACpB,OAAO,KAAK;GACZ,OAAO;GACP,YAAY,CAAC,KAAK,MAAM,IAAI,aAAa;GACzC,GAAG;GACJ,CAAC;;CAGJ,MAAM,SACJ,MACA,SACmC;EACnC,MAAM,EAAE,OAAO,gBAAgB,MAAM,KAAK,GAAG,SAAS,GACnD,KAAK,YAAY;GAAE,MAAM,KAAK,KAAK,QAAQ,KAAK,WAAW,IAAI,CAAC;GAAE,GAAG;GAAS,EAChF,CAAC;AACF,SAAO;GACL,OAAO,MAAM,KAAK;GAClB,aAAa,cAAc,KAAK,YAAY;GAC7C;;CAGH,MAAM,gBACJ,MACA,SAC0C;AAI1C,UAHe,MAAM,KAAK,GAAG,gBAAgB,GAC1C,KAAK,YAAY;GAAE,MAAM,KAAK,KAAK,QAAQ,KAAK,WAAW,IAAI,CAAC;GAAE,GAAG;GAAS,EAChF,CAAC,EACY,KAAK;;CAGrB,MAAM,WAAW,UAA+D;EAC9E,MAAM,EAAE,OAAO,gBAAgB,MAAM,KAAK,GAAG,WAAW,GACrD,KAAK,YAAY,SAAS,KAAK,YAAY;AAC1C,OAAI,QAAQ,SAAS,SACnB,QAAO;IAAE,MAAM;IAAU,KAAK,KAAK,WAAW,QAAQ,IAAI;IAAE;OAK5D,QAAO;IAAE,MAAM;IAAO,MAHG,KAAK,MAAM,IAAI,YACpC;KAAE,GAAG,KAAK,MAAM,IAAI,UAAU,QAAQ,KAAK;KAAE,GAAG,QAAQ;KAAM,GAC9D,QAAQ;IACkC;IAEhD,EACH,CAAC;AAEF,SAAO;GACL,OAAO,MAAM,KAAK;GAClB,aAAa,cAAc,KAAK;GACjC;;CAMH,MAAM,OACJ,MACA,SACiC;AACjC,SAAO,KAAK,GAAG,OACb,GAAG,KAAK,KAAK,SAAS;GAAE,OAAO,KAAK;GAAW,KAAK,KAAK,WAAW,IAAI;GAAE,GAAG;GAAS,EAAE,CACzF;;CAGH,MAAM,cACJ,MACA,SACwC;AACxC,SAAO,KAAK,GAAG,cACb,GAAG,KAAK,KAAK,SAAS;GAAE,OAAO,KAAK;GAAW,KAAK,KAAK,WAAW,IAAI;GAAE,GAAG;GAAS,EAAE,CACzF;;CAGH,MAAM,SAAS,GAAG,UAAmD;AACnE,QAAM,KAAK,GAAG,SACZ,GAAG,SAAS,KAAK,YAAY;AAC3B,WAAQ,QAAQ,MAAhB;IACE,KAAK,aAAa;KAChB,MAAM,EAAE,KAAK,WAAW,GAAG,YAAY;AACvC,YAAO,KAAK,oBAAoB,KAAK,WAAW,QAAQ;;IAG1D,KAAK,UAAU;KACb,MAAM,EAAE,KAAK,GAAG,YAAY;AAC5B,YAAO,KAAK,iBAAiB,KAAK,QAAQ;;IAG5C,KAAK,OAAO;KACV,MAAM,EAAE,MAAM,GAAG,YAAY;AAC7B,YAAO,KAAK,cAAc,MAAM,QAAQ;;IAG1C,KAAK,UAAU;KACb,MAAM,EAAE,KAAK,QAAQ,GAAG,YAAY;AACpC,YAAO,KAAK,iBAAiB,KAAK,QAAQ,QAAQ;;IAGpD,QACE,OAAM,IAAI,MAAM,2BAA2B;;IAE/C,CACH;;CAGH,MAAM,UAAU,MAAoB,SAAwD;AAC1F,SAAO,KAAK,GAAG,SAAS,GAAG,KAAK,KAAK,QAAQ,KAAK,iBAAiB,KAAK,QAAQ,CAAC,CAAC;;CAIpF,MAAM,OAAO,OAAyB,SAAqD;AACzF,SAAO,KAAK,GAAG,SAAS,GAAG,MAAM,KAAK,SAAS,KAAK,cAAc,MAAM,QAAQ,CAAC,CAAC;;CAGpF,MAAM,UACJ,MACA,QACA,SACe;AACf,SAAO,KAAK,GAAG,SAAS,GAAG,KAAK,KAAK,QAAQ,KAAK,iBAAiB,KAAK,QAAQ,QAAQ,CAAC,CAAC;;CAI5F,MAAM,UAAU,OAA4B,SAAwC;AAClF,SAAO,KAAK,GAAG,SAAS,GAAG,MAAM,KAAK,SAAS,KAAK,iBAAiB,MAAM,QAAQ,CAAC,CAAC;;CAGvF,cACE,KACA,SAC6C;AAC7C,SAAO;GAAE,OAAO,KAAK;GAAW,KAAK,KAAK,WAAW,IAAI;GAAE,GAAG;GAAS;;CAGzE,iBAAiB,KAAiB,SAA4D;AAC5F,SAAO;GAAE,OAAO,KAAK;GAAW,MAAM;GAAU,KAAK,KAAK,WAAW,IAAI;GAAE,GAAG;GAAS;;CAGzF,oBACE,KACA,WACA,SACmB;AACnB,SAAO;GACL,OAAO,KAAK;GACZ,MAAM;GACN,KAAK,KAAK,WAAW,IAAI;GACzB;GACA,GAAG;GACJ;;CAGH,cAAc,MAAsB,SAAyD;EAC3F,MAAM,mBAAmB,KAAK,MAAM,IAAI,YACpC;GAAE,GAAG,KAAK,MAAM,IAAI,UAAU,KAAK;GAAE,GAAG;GAAM,GAC9C;AACJ,SAAO;GAAE,OAAO,KAAK;GAAW,MAAM;GAAO,MAAM;GAAkB,GAAG;GAAS;;CAGnF,iBACE,KACA,QACA,SACmB;EACnB,MAAM,qBAAqB,KAAK,MAAM,IAAI,eACtC;GAAE,GAAG,KAAK,MAAM,IAAI,aAAa,OAAO;GAAE,GAAG;GAAQ,GACrD;AACJ,SAAO;GACL,OAAO,KAAK;GACZ,MAAM;GACN,KAAK,KAAK,WAAW,IAAI;GACzB,QAAQ;GACR,GAAG;GACJ;;CAGH,iBAAiB,MAAyB,SAA4C;EACpF,MAAM,EAAE,WAAW,gBAAgB,GAAG,iBAAiB,WAAW,EAAE;EAEpE,MAAM,YAAY,EAAE,MAAM,CAAC,GAAG,KAAK,MAAM,IAAI,eAAe,EAAE,SAAS,OAAO,EAAE,CAAC,EAAE;AAEnF,MAAI,eACF,WAAU,KAAK,KAAK,eAAe;EAGrC,MAAM,mBAAmB,KAAK,MAAM,IAAI,YACpC;GAAE,GAAG,KAAK,MAAM,IAAI,UAAU,KAAK;GAAE,GAAG;GAAM,GAC9C;AAEJ,SAAO;GACL,OAAO,KAAK;GACZ,MAAM;GACN,MAAM;GACN;GACA,GAAG;GACJ;;;;;AC/ZL,MAAa,mBAAmB,QAAiB,aAAuC;AACtF,KAAI,EAAE,SAAS,OAAO,IAAI,EAAE,gBAAgB,OAAO,CACjD,QAAO,EAAE,WAAW,OAAO,GAAG,CAAC,KAAK,KAAA,EAAU,GAAG,CAAC,IAAI;AAGxD,KAAI,EAAE,SAAS,OAAO,IAAI,EAAE,gBAAgB,OAAO,CACjD,QAAO,EAAE,WAAW,OAAO,GAAG,CAAC,KAAK,KAAA,EAAU,GAAG,CAAC,IAAI;AAGxD,KAAI,EAAE,SAAS,OAAO,EAAE;EACtB,MAAM,aAAa,OAAO,WAAW;AACrC,MAAI,CAAC,WAAY,QAAO,CAAC,KAAA,EAAU;AACnC,SAAO,gBAAgB,YAAY,SAAS;;AAG9C,KAAI,EAAE,YAAY,OAAO,CACvB,QAAO,OAAO,MAAM,SAAS,MAAM,gBAAgB,GAAG,SAAS,CAAC;AAGlE,KAAI,EAAE,QAAQ,OAAO,CACnB,QAAO,OAAO,MAAM,SAAS,MAAM,gBAAgB,GAAG,SAAS,CAAC;AAGlE,QAAO,EAAE;;AAGX,MAAa,eACX,QACA,UACA,eAAiC;CAAC;CAAK;CAAK,KAAA;CAAU,KACxC;CACd,MAAM,eAAe,gBAAgB,QAAQ,SAAS;AACtD,KAAI,aAAa,SAAS,IAAI,IAAI,aAAa,SAAS,IAAI,CAC1D,OAAM,IAAI,MAAM,aAAa,SAAS,wCAAwC;AAEhF,KAAI,CAAC,aAAa,SAAS,KAAA,EAAU,IAAI,aAAa,SAAS,KAAA,EAAU,CACvE,OAAM,IAAI,MAAM,aAAa,SAAS,sBAAsB;AAE9D,QAAO,aAAa,SAAS,IAAI,GAAG,MAAM;;AAG5C,MAAa,oBACX,QACA,UACA,iBACwB;AACxB,QAAO;EACL,eAAe;EACf,eAAe,YAAY,QAAQ,UAAU,aAAa;EAC3D;;AAGH,MAAa,mBACX,QACA,UACA,iBACa;AACb,QAAO;EACL,MAAM;EACN,MAAM,YAAY,QAAQ,UAAU,aAAa;EAClD;;AAGH,MAAa,oBAAoB,QAAiB,QAA6B;CAC7E,MAAM,OAAmB,OAAO,QAAQ,IAAI,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,WAAW,SAAS;AAChF,SAAO;GACL;GACA,cAAc,gBAAgB,QAAQ,IAAI,aAAa;GACvD,SAAS,IAAI,UAAU,gBAAgB,QAAQ,IAAI,QAAQ,GAAG,KAAA;GAC9D,gBAAgB,MAAM,QAAQ,IAAI,WAAW,GAAG,YAAa,IAAI,cAAc;GAC/E,kBAAkB,MAAM,QAAQ,IAAI,WAAW,GAAG,IAAI,aAAa,KAAA;GACpE;GACD;AAEF,QAAO;EACL,WAAW,IAAI;EACf,aAAa,IAAI,eAAe;EAChC,QAAQ,IAAI;EACZ,cAAc,gBAAgB,QAAQ,IAAI,cAAc,CAAC,KAAK,IAAI,CAAC;EACnE,SAAS,IAAI,UAAU,gBAAgB,QAAQ,IAAI,SAAS,CAAC,KAAK,IAAI,CAAC,GAAG,KAAA;EAC1E,MAAM,KAAK,SAAS,OAAO,KAAA;EAC5B;;AAGH,MAAa,SAAsB,KAAU,cAA6B;AACxE,KAAI,aAAa,EAAG,QAAO,EAAE;CAE7B,MAAM,SAAgB,EAAE;AACxB,MAAK,IAAI,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK,UACnC,QAAO,KAAK,IAAI,MAAM,GAAG,IAAI,UAAU,CAAC;AAG1C,QAAO;;AAKT,MAAa,mBAAmB,QAAa;CAC3C,MAAM,QAAQ,CAAC,IAAI;AACnB,QAAO,MAAM,QAAQ;EACnB,MAAM,aAAa,MAAM,KAAK;AAC9B,MAAI,eAAe,KAAA,EACjB,QAAO,QAAQ,WAAW,CAAC,SAAS,CAAC,GAAG,OAAO;AAC7C,OAAI,KAAK,aAAa,OAAQ,OAAM,KAAK,EAAE;YAClC,MAAM,KAAA,EAAW,QAAO,WAAW;IAC5C;;AAGN,QAAO;;;;AC7DT,IAAa,KAAb,MAAgB;CACd;CACA;CAIA,YACE,gBACA,UACA;AACA,MAAI,0BAA0B,kBAAkB,0BAA0B,SACxE,MAAK,SAAS,IAAI,uBAAuB,KAAK,IAAI,eAAe,eAAe,CAAC;OAC5E;GACL,MAAM,EAAE,UAAU,GAAG,iBAAiB;AAGtC,QAAK,SAAS,IAAI,uBAAuB,KACvC,IAAI,eAAe;IACjB,UAAU,YAAY,KAAA;IACtB,GAAG;IACJ,CAAC,CACH;;AAGH,OAAK,SAAS;;CAGhB,WAAsC,OAAyB;AAC7D,SAAO,IAAI,WAAW,OAAO,KAAK;;CAGpC,MAAM,WAAW,MAAwC;EACvD,MAAM,SAAmB,EAAE;EAC3B,IAAI;AACJ,KAAG;GACD,MAAM,SAAS,MAAM,KAAK,OAAO,KAC/B,IAAI,kBAAkB;IACpB,OAAO,MAAM;IACb,yBAAyB;IAC1B,CAAC,CACH;AAED,UAAO,KAAK,GAAI,OAAO,cAAc,EAAE,CAAE;AACzC,4BAAyB,OAAO;WACzB;AACT,SAAO;;CAGT,MAAM,IAAa,MAAqC;EACtD,MAAM,MAAM,IAAI,mBAAmB;EAEnC,MAAM,QAAQ,IAAI,IAAI,WAAW;GAC/B,WAAW,KAAK;GAChB,KAAK,KAAK;GACV,gBAAgB,KAAK;GACrB,sBAAsB,IAAI,WAAW,KAAK,WAAW;GACrD,0BAA0B,IAAI;GAC/B,CAAC;EAEF,MAAM,SAAS,MAAM,KAAK,OAAO,KAAK,MAAM;AAE5C,MAAI,OAAO,QAAQ,KAAK;OAClB,CAAC,KAAK,KAAK,UAAU,CAAC,OAAO,KAAK,CACpC;;AAIJ,SAAO,OAAO;;CAGhB,MAAM,WAAoB,MAAyB;EACjD,MAAM,OAAO,MAAM,KAAK,IAAI,KAAK;AACjC,MAAI,CAAC,KACH,OAAM,IAAI,MAAM,sBAAsB,KAAK,MAAM,UAAU;AAE7D,SAAO;;CAGT,MAAM,IAAa,MAAyB;EAC1C,MAAM,MAAM,IAAI,mBAAmB;EAEnC,MAAM,QAAQ,IAAI,IAAI,WAAW;GAC/B,WAAW,KAAK;GAChB,MAAM,gBAAgB,KAAK,KAAK;GAChC,cAAc,KAAK,WAAW,YAAY,YAAY,KAAA;GACtD,qCAAqC;GACrC,qBAAqB,IAAI,UAAU,KAAK,UAAU;GAClD,0BAA0B,IAAI;GAC9B,2BAA2B,IAAI;GAChC,CAAC;EAEF,MAAM,SAAS,MAAM,KAAK,OAAO,KAAK,MAAM;AAE5C,SAAQ,KAAK,WAAW,YAAY,OAAO,aAAa;;CAG1D,MAAM,OAAgB,MAAwC;EAC5D,MAAM,MAAM,IAAI,mBAAmB;EAEnC,MAAM,YAAY,EAChB,MAAM,OAAO,KAAK,KAAK,IAAI,CAAC,KAAK,WAAW,GACzC,QAAQ,EAAE,SAAS,MAAM,EAC3B,EAAE,EACJ;AAED,MAAI,KAAK,UACP,WAAU,KAAK,KAAK,KAAK,UAAU;EAGrC,MAAM,QAAQ,IAAI,IAAI,cAAc;GAClC,WAAW,KAAK;GAChB,KAAK,KAAK;GACV,cAAc,KAAK,UAAU;GAC7B,qCAAqC;GACrC,kBAAkB,IAAI,OAAO,KAAK,OAAO;GACzC,qBAAqB,IAAI,UAAU,UAAU;GAC7C,0BAA0B,IAAI;GAC9B,2BAA2B,IAAI;GAChC,CAAC;AAIF,UAFe,MAAM,KAAK,OAAO,KAAK,MAAM,EAE9B;;CAGhB,MAAM,OAAgB,MAA4B;EAChD,MAAM,EAAE,WAAW,gBAAgB,kBAAkB,GAAG,YAAY;EAEpE,MAAM,YAAY,EAAE,MAAM,CAAC,GAAG,mBAAmB,EAAE,SAAS,OAAO,EAAE,CAAC,EAAE;AAExE,MAAI,eACF,WAAU,KAAK,KAAK,eAAe;AAGrC,SAAO,KAAK,IAAO;GACjB,GAAG;GACH;GACA,QAAQ;GACT,CAAC;;CAGJ,MAAM,OAAgB,MAA4B;EAChD,MAAM,EAAE,KAAK,MAAM,QAAQ,GAAG,YAAY;AAC1C,MAAI;AACF,UAAQ,MAAM,KAAK,OAAO;IACxB,GAAG;IACH;IACA;IACA,QAAQ;IACT,CAAC;WACK,GAAG;AACV,OAAI,aAAa,mCAAmC,CAAC,EAAE,KAErD,QAAO,KAAK,OAAO;IACjB;IACA,kBAAkB,OAAO,KAAK,IAAI,CAAC,MAAM;IACzC,GAAG;IACJ,CAAC;AAGJ,SAAM;;;CAIV,MAAM,OAAgB,MAAwC;EAC5D,MAAM,MAAM,IAAI,mBAAmB;EAEnC,MAAM,QAAQ,IAAI,IAAI,cAAc;GAClC,WAAW,KAAK;GAChB,KAAK,KAAK;GACV,cAAc,KAAK,UAAU;GAC7B,qBAAqB,IAAI,UAAU,KAAK,UAAU;GAClD,qCAAqC;GACrC,0BAA0B,IAAI;GAC9B,2BAA2B,IAAI;GAChC,CAAC;AAIF,UAFe,MAAM,KAAK,OAAO,KAAK,MAAM,EAE9B;;CAGhB,MAAM,cAAuB,MAA4C;EACvE,MAAM,OAAO,MAAM,KAAK,OAAU,KAAK;AACvC,MAAI,CAAC,KACH,OAAM,IAAI,MAAM,sBAAsB,KAAK,MAAM,UAAU;AAE7D,SAAO;;CAGT,OAAO,WAAoB,MAAoC;EAC7D,MAAM,MAAM,IAAI,mBAAmB;EAEnC,IAAI,mBAAmB,KAAK;AAC5B,KAAG;GACD,MAAM,QAAQ,IAAI,IAAI,aAAa;IACjC,WAAW,KAAK;IAChB,WAAW,KAAK;IAChB,mBAAmB;IACnB,OAAO,KAAK;IACZ,kBAAkB,KAAK,SAAS;IAChC,gBAAgB,KAAK;IACrB,wBAAwB,IAAI,UAAU,KAAK,MAAM;IACjD,sBAAsB,IAAI,WAAW,KAAK,WAAW;IACrD,kBAAkB,IAAI,UAAU,KAAK,OAAO;IAC5C,0BAA0B,IAAI;IAC9B,2BAA2B,IAAI;IAChC,CAAC;GAEF,MAAM,SAAS,MAAM,KAAK,OAAO,KAAK,MAAM;AAE5C,OAAI,OAAO,OAAO,OAChB,OAAM,OAAO;AAEf,sBAAmB,OAAO;WACnB;;CAGX,MAAM,MAAe,MAA6B;EAChD,MAAM,QAAe,EAAE;AAEvB,aAAW,MAAM,QAAQ,KAAK,WAAW,KAAK,CAC5C,OAAM,KAAK,GAAG,KAAK;AAGrB,SAAO;;CAGT,OAAO,UAAmB,MAAmC;EAC3D,MAAM,MAAM,IAAI,mBAAmB;EAEnC,MAAM,gBAAgB,KAAK,YAAY;EACvC,MAAM,WAAW,CAAC,GAAG,MAAM,cAAc,CAAC,MAAM,CAAC;EAIjD,MAAM,oBAAgD,SAAS,UAAU,KAAK,SAAS;AACvF,KAAG;GAyBD,MAAM,SAxBU,MAAM,QAAQ,IAC5B,SAAS,IAAI,OAAO,YAAY;AAC9B,QAAI,kBAAkB,aAAa,KAAM,QAAO,EAAE;IAElD,MAAM,QAAQ,IAAI,IAAI,YAAY;KAChC,mBAAmB,kBAAkB;KACrC,SAAS;KACT,eAAe;KACf,WAAW,KAAK;KAChB,WAAW,KAAK;KAChB,OAAO,KAAK;KACZ,gBAAgB,KAAK;KACrB,sBAAsB,IAAI,WAAW,KAAK,WAAW;KACrD,kBAAkB,IAAI,UAAU,KAAK,OAAO;KAC5C,0BAA0B,IAAI;KAC9B,2BAA2B,IAAI;KAChC,CAAC;IAEF,MAAM,SAAS,MAAM,KAAK,OAAO,KAAK,MAAM;AAC5C,sBAAkB,WAAW,OAAO,oBAAoB;AACxD,WAAO,OAAO,SAAS,EAAE;KACzB,CACH,EAEqB,MAAM;AAE5B,OAAI,MAAM,OACR,OAAM;WAED,kBAAkB,MAAM,QAAQ,QAAQ,KAAK;;CAGxD,MAAM,KAAc,MAA4B;EAC9C,MAAM,QAAa,EAAE;AAErB,aAAW,MAAM,QAAQ,KAAK,UAAU,KAAK,CAC3C,OAAM,KAAK,GAAI,KAAa;AAG9B,SAAO;;CAGT,MAAM,OAAO,MAAkC;EAC7C,MAAM,EAAE,OAAO,GAAG,iBAAiB;EAKnC,MAAM,gBAAgB;GACpB,GAAG;GACH,OAAO,KAAK,SAAS,KAAA,IAAY;GAClC;EAED,MAAM,aAAa,QACf,KAAK,WAAW;GAAE;GAAO,GAAG;GAAe,CAAC,GAC5C,KAAK,UAAU,cAAc;AAEjC,aAAW,MAAM,SAAS,WACxB,KAAI,MAAM,OAAQ,QAAO;AAG3B,SAAO;;CAKT,MAAM,SAAS,MAA+C;EAC5D,MAAM,SAA6B,EAAE,OAAO,EAAE,EAAE;EAEhD,MAAM,YAAY,OAAO,YACvB,OAAO,QAAQ,KAAK,CAAC,KAAK,CAAC,OAAO,aAAa;GAC7C,MAAM,MAAM,IAAI,mBAAmB;GACnC,MAAM,SAAS,SAAS,YAAY,KAAK,SAAS,UAAU,GAAG,KAAA;AAE/D,UAAO,MAAM,SAAS,EAAE;AAExB,UAAO,CACL,OACA;IACE,SAAS;KACP,gBAAgB,SAAS;KACzB,sBAAsB,IAAI,WAAW,SAAS,WAAW;KACzD,0BAA0B,IAAI;KAC/B;IACD,UAAU,OAAO,KAAK,QAAQ,KAAK,GAAG,EAAE,IAAI,EAAE,CAAC;IAC/C,8BAAc,IAAI,KAAqB;IACvC;IACD,CACF;IACD,CACH;EAED,MAAM,mBAAmB,OAAO,QAAQ,KAAK,CAAC,SAAS,CAAC,OAAO,aAAa;AAC1E,UAAO,QAAQ,KAAK,KAAK,KAAK,MAAM;IAClC,MAAM,eACJ,UAAU,QAAQ,SAAS,KAAK,YAAY,IAAI,SAAS,CAAC,KAAK,IAAI,IAAI;AACzE,cAAU,QAAQ,aAAa,IAAI,cAAc,EAAE;AACnD,WAAO,CAAC,OAAO,IAAI;KACnB;IACF;EAEF,IAAI,YAAY;EAChB,IAAI,aAAa;AAEjB,SAAO,iBAAiB,UAAU,aAAa,GAAG;GAChD,MAAM,QAAQ,iBAAiB,OAAO,GAAG,aAAa,EAAE;GAExD,MAAM,UAA6C,EAAE;AACrD,QAAK,MAAM,CAAC,OAAO,QAAQ,OAAO;AAChC,QAAI,CAAC,QAAQ,OACX,SAAQ,SAAS;KAAE,GAAG,UAAU,QAAQ;KAAS,MAAM,EAAE;KAAE;AAG7D,YAAQ,OAAO,MAAM,KAAK,IAAI;;GAGhC,MAAM,QAAQ,IAAI,IAAI,gBAAgB,EAAE,cAAc,SAAS,CAAC;GAChE,MAAM,SAAS,MAAM,KAAK,OAAO,KAAK,MAAM;AAE5C,QAAK,MAAM,CAAC,OAAO,UAAU,OAAO,QAAQ,OAAO,aAAa,EAAE,CAAC,EAAE;AAEnE,QAAI,CAAC,OAAO,MAAM,OAAQ;AAE1B,SAAK,MAAM,QAAQ,OAAO;AAGxB,SAAI,UAAU,QAAQ,UAAU,CAAC,UAAU,OAAO,OAAO,KAAK,CAC5D;AAGF,YAAO,MAAM,OAAO,KAAK,KAAK;;;GAKlC,IAAI,sBAAsB;AAC1B,QAAK,MAAM,CAAC,OAAO,YAAY,OAAO,QAAQ,OAAO,mBAAmB,EAAE,CAAC,EAAE;IAC3E,MAAM,eAAe,QAAQ,QAAQ,EAAE,EAAE,KAAK,QAAQ,CAAC,OAAO,IAAI,CAAU;AAC5E,qBAAiB,KAAK,GAAG,YAAY;AACrC,2BAAuB,YAAY;;AAIrC,OAAI,CAAC,qBAAqB;AACxB,iBAAa;AACb;;AAKF,gBAAa,KAAK,MAAM,sBAAsB,EAAE;AAEhD;;AAIF,MAAI,iBAAiB,QAAQ;AAC3B,UAAO,cAAc,EAAE;AACvB,QAAK,MAAM,CAAC,OAAO,QAAQ,kBAAkB;AAC3C,QAAI,CAAC,OAAO,YAAY,OACtB,QAAO,YAAY,SAAS;KAC1B,GAAG,UAAU,QAAQ;KACrB,MAAM,EAAE;KACT;AAEH,WAAO,YAAY,OAAO,KAAK,KAAK,IAAI;;;AAK5C,OAAK,MAAM,CAAC,OAAO,UAAU,OAAO,QAAQ,OAAO,MAAM,EAAE;GACzD,MAAM,eAAe,UAAU,QAAQ;GACvC,MAAM,WAAW,UAAU,QAAQ;AAEnC,OAAI,CAAC,gBAAgB,CAAC,SAAU;AAEhC,SAAM,MAAM,OAAO,UAAU;IAC3B,MAAM,gBAAgB,SAAS,KAAK,YAAY,MAAM,SAAS,CAAC,KAAK,IAAI;IACzE,MAAM,gBAAgB,SAAS,KAAK,YAAY,MAAM,SAAS,CAAC,KAAK,IAAI;AAEzE,YAAQ,aAAa,IAAI,cAAc,IAAI,MAAM,aAAa,IAAI,cAAc,IAAI;KACpF;;AAGJ,SAAO;;CAGT,MAAM,gBAAgB,MAAwD;EAC5E,MAAM,EAAE,OAAO,gBAAgB,MAAM,KAAK,SAAS,KAAK;AAExD,OAAK,MAAM,SAAS,OAAO,KAAK,KAAK,EAAE;AACrC,OAAI,cAAc,OAChB,OAAM,IAAI,MAAM,yDAAyD,MAAM,UAAU;AAG3F,OAAI,MAAM,QAAQ,WAAW,KAAK,QAAQ,MAAM,OAC9C,OAAM,IAAI,MAAM,wCAAwC,MAAM,UAAU;;AAI5E,SAAO;;CAKT,MAAM,WAAW,MAAmD;EAClE,MAAM,SAA+B,EAAE,OAAO,EAAE,EAAE;EAElD,MAAM,mBAAmB,OAAO,KAAK,KAAK,CAAC,SAAS,UAAU;AAC5D,UACE,KAAK,QAAQ,KAAK,MAAM;AACtB,QAAI,EAAE,SAAS,SACb,QAAO,CAAC,OAAO,EAAE,eAAe,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;QAEjD,QAAO,CAAC,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;KAElD,IAAI,EAAE;IAEV;EAEF,IAAI,YAAY;EAChB,IAAI,aAAa;AAEjB,SAAO,iBAAiB,UAAU,aAAa,GAAG;GAChD,MAAM,QAAQ,iBAAiB,OAAO,GAAG,aAAa,EAAE;GAExD,MAAM,UAA+C,EAAE;AACvD,QAAK,MAAM,CAAC,OAAO,QAAQ,OAAO;AAChC,QAAI,CAAC,QAAQ,OACX,SAAQ,SAAS,EAAE;AAErB,YAAQ,OAAO,KAAK,IAAI;;GAG1B,MAAM,QAAQ,IAAI,IAAI,kBAAkB,EAAE,cAAc,SAAS,CAAC;GAClE,MAAM,SAAS,MAAM,KAAK,OAAO,KAAK,MAAM;GAG5C,IAAI,sBAAsB;AAC1B,QAAK,MAAM,CAAC,OAAO,aAAa,OAAO,QAAQ,OAAO,oBAAoB,EAAE,CAAC,CAC3E,MAAK,MAAM,WAAW,UAAU;AAC9B,qBAAiB,KAAK,CAAC,OAAO,QAAQ,CAAC;AACvC;;AAKJ,OAAI,CAAC,qBAAqB;AACxB,iBAAa;AACb;;AAKF,gBAAa,KAAK,MAAM,sBAAsB,EAAE;AAEhD;;AAIF,MAAI,iBAAiB,QAAQ;AAC3B,UAAO,cAAc,EAAE;AACvB,QAAK,MAAM,CAAC,OAAO,YAAY,kBAAkB;AAC/C,QAAI,CAAC,OAAO,YAAY,OACtB,QAAO,YAAY,SAAS,EAAE;AAGhC,QAAI,mBAAmB,WAAW,QAAQ,eAAe,IACvD,QAAO,YAAY,OAAO,KAAK;KAC7B,MAAM;KACN,KAAK,QAAQ,cAAc;KAC5B,CAAC;aACO,gBAAgB,WAAW,QAAQ,YAAY,KACxD,QAAO,YAAY,OAAO,KAAK;KAC7B,MAAM;KACN,MAAM,QAAQ,WAAW;KAC1B,CAAC;;;AAKR,SAAO;;CAGT,MAAM,OAAoC,GAAG,UAAyC;EACpF,MAAM,WAAqD,SAAS,KAAK,YAAY;GACnF,MAAM,MAAM,IAAI,mBAAmB;AACnC,UAAO,EACL,KAAK;IACH,KAAK,QAAQ;IACb,WAAW,QAAQ;IACnB,sBAAsB,IAAI,WAAW,SAAS,WAAW;IACzD,0BAA0B,IAAI;IAC/B,EACF;IACD;EAEF,MAAM,QAAQ,IAAI,IAAI,mBAAmB,EAAE,eAAe,UAAU,CAAC;AAGrE,UAFe,MAAM,KAAK,OAAO,KAAK,MAAM,EAGlC,WAAW,KAAK,UAAU,MAAM;AACtC,OAAI,SAAS,IAAI,aAAa,CAAC,KAAK,SAAS,GAAG,UAAU,CAAC,SAAS,KAAK,CACvE;AAGF,UAAO,SAAS;IAChB,IAA0B,EAAE;;CAIlC,MAAM,cACJ,GAAG,UACgC;EACnC,MAAM,QAAQ,MAAM,KAAK,OAAO,GAAG,SAAS;AAC5C,OAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,IAChC,KAAI,CAAC,MAAM,GACT,OAAM,IAAI,MAAM,wCAAwC,SAAS,IAAI,MAAM,UAAU;AAIzF,SAAO;;CAGT,MAAM,SAAS,GAAG,UAA8C;EAC9D,MAAM,WAAuD,SAAS,KAAK,YAAY;GACrF,MAAM,MAAM,IAAI,mBAAmB;AAEnC,OAAI,QAAQ,SAAS,eAAe,QAAQ,SAAS,SACnD,QAAO,GACJ,QAAQ,SAAS,cAAc,mBAAmB,WAAW;IAC5D,WAAW,QAAQ;IACnB,KAAK,QAAQ;IACb,qBAAqB,IAAI,UAAU,QAAQ,UAAU;IACrD,0BAA0B,IAAI;IAC9B,2BAA2B,IAAI;IAChC,EACF;AAGH,OAAI,QAAQ,SAAS,MACnB,QAAO,EACL,KAAK;IACH,WAAW,QAAQ;IACnB,MAAM,gBAAgB,QAAQ,KAAK;IACnC,qBAAqB,IAAI,UAAU,QAAQ,UAAU;IACrD,0BAA0B,IAAI;IAC9B,2BAA2B,IAAI;IAChC,EACF;AAGH,UAAO,EACL,QAAQ;IACN,KAAK,QAAQ;IACb,WAAW,QAAQ;IACnB,kBAAkB,IAAI,OAAO,QAAQ,OAAO;IAC5C,qBAAqB,IAAI,UAAU,QAAQ,UAAU;IACrD,0BAA0B,IAAI;IAC9B,2BAA2B,IAAI;IAChC,EACF;IACD;EAEF,MAAM,QAAQ,IAAI,IAAI,qBAAqB,EAAE,eAAe,UAAU,CAAC;AACvE,QAAM,KAAK,OAAO,KAAK,MAAM;;;;;ACjoBjC,IAAa,QAAb,MAGE;CACA;CACA;CAEA,YAAY,QAAgB,KAAU;AACpC,OAAK,SAAS;AACd,OAAK,MAAM;;CAGb,MAAM,KAAK,QAA+C;AACxD,QAAM,OAAO,KAAK,IAAI,mBAAmB,EAAE,WAAW,KAAK,IAAI,MAAM,CAAC,CAAC;;CAGzE,MAAM,YAAY,QAA+C;EAC/D,MAAM,OAAO,iBAAiB,KAAK,QAAQ,KAAK,IAAI;EAEpD,MAAM,6BAAa,IAAI,KAAkC;EACzD,MAAM,gBAAgB,aAAuB;AAC3C,cAAW,IAAI,SAAS,MAAM;IAAE,eAAe,SAAS;IAAM,eAAe,SAAS;IAAM,CAAC;;EAG/F,MAAM,YAAgC,CACpC;GAAE,eAAe,KAAK,aAAa;GAAM,SAAS;GAAQ,CAC3D;AACD,eAAa,KAAK,aAAa;AAE/B,MAAI,KAAK,SAAS;AAChB,aAAU,KAAK;IAAE,eAAe,KAAK,QAAQ;IAAM,SAAS;IAAS,CAAC;AACtE,gBAAa,KAAK,QAAQ;;EAG5B,MAAM,OAAO,KAAK,MAAM,KAAK,QAAQ;GACnC,MAAM,eAAmC,CACvC;IAAE,eAAe,IAAI,aAAa;IAAM,SAAS;IAAQ,CAC1D;AACD,gBAAa,IAAI,aAAa;AAC9B,OAAI,IAAI,SAAS;AACf,iBAAa,KAAK;KAAE,eAAe,IAAI,QAAQ;KAAM,SAAS;KAAS,CAAC;AACxE,iBAAa,IAAI,QAAQ;;AAE3B,UAAO;IACL,WAAW,IAAI;IACf,WAAW;IACX,YAAY;KACV,gBAAgB,IAAI;KACpB,kBAAkB,IAAI;KACvB;IACF;IACD;AAEF,QAAM,OAAO,KACX,IAAI,mBAAmB;GACrB,WAAW,KAAK;GAChB,WAAW;GACX,sBAAsB,CAAC,GAAG,WAAW,QAAQ,CAAC;GAC9C,aAAa,KAAK;GAClB,wBAAwB;GACxB,qBAAqB,KAAK,SACtB;IACE,eAAe;IACf,gBAAgB,KAAK;IACtB,GACD,KAAA;GACL,CAAC,CACH;AAED,MAAI,KAAK,aACP,OAAM,OAAO,KACX,IAAI,wBAAwB;GAC1B,WAAW,KAAK;GAChB,yBAAyB;IACvB,SAAS;IACT,eAAe,KAAK;IACrB;GACF,CAAC,CACH"}
|
package/dist/streams.cjs
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
require("./index.cjs");
|
|
3
|
-
let _aws_sdk_client_dynamodb_streams = require("@aws-sdk/client-dynamodb-streams");
|
|
4
|
-
//#region src/streams.ts
|
|
5
|
-
var EventStreamer = class {
|
|
6
|
-
client;
|
|
7
|
-
streamArn;
|
|
8
|
-
pollTimer;
|
|
9
|
-
shards = /* @__PURE__ */ new Map();
|
|
10
|
-
constructor(client, streamArn) {
|
|
11
|
-
this.client = client;
|
|
12
|
-
this.streamArn = streamArn;
|
|
13
|
-
}
|
|
14
|
-
async syncShards(iteratorType, sequenceNumber) {
|
|
15
|
-
const { StreamDescription } = await this.client.send(new _aws_sdk_client_dynamodb_streams.DescribeStreamCommand({ StreamArn: this.streamArn }));
|
|
16
|
-
const availableShards = StreamDescription?.Shards ?? [];
|
|
17
|
-
const newShards = availableShards.filter((shard) => shard.ShardId && !this.shards.has(shard.ShardId));
|
|
18
|
-
await Promise.all(newShards.map(async (shard) => {
|
|
19
|
-
const { ShardIterator } = await this.client.send(new _aws_sdk_client_dynamodb_streams.GetShardIteratorCommand({
|
|
20
|
-
StreamArn: this.streamArn,
|
|
21
|
-
ShardIteratorType: iteratorType ?? "LATEST",
|
|
22
|
-
ShardId: shard.ShardId,
|
|
23
|
-
SequenceNumber: sequenceNumber
|
|
24
|
-
}));
|
|
25
|
-
this.shards.set(shard.ShardId, {
|
|
26
|
-
shard,
|
|
27
|
-
iterator: ShardIterator
|
|
28
|
-
});
|
|
29
|
-
}));
|
|
30
|
-
for (const shardId in this.shards.keys()) if (!availableShards.find((shard) => shard.ShardId === shardId)) this.shards.delete(shardId);
|
|
31
|
-
}
|
|
32
|
-
async getShardRecords(shardId) {
|
|
33
|
-
const shard = this.shards.get(shardId);
|
|
34
|
-
if (!shard?.iterator) return [];
|
|
35
|
-
const data = await this.client.send(new _aws_sdk_client_dynamodb_streams.GetRecordsCommand({ ShardIterator: shard.iterator })).catch((e) => {
|
|
36
|
-
if (e instanceof _aws_sdk_client_dynamodb_streams.ResourceNotFoundException || e instanceof _aws_sdk_client_dynamodb_streams.TrimmedDataAccessException) return {
|
|
37
|
-
Records: [],
|
|
38
|
-
NextShardIterator: void 0
|
|
39
|
-
};
|
|
40
|
-
throw e;
|
|
41
|
-
});
|
|
42
|
-
shard.iterator = data.NextShardIterator;
|
|
43
|
-
return data.Records ?? [];
|
|
44
|
-
}
|
|
45
|
-
async processStream() {
|
|
46
|
-
await this.syncShards("TRIM_HORIZON");
|
|
47
|
-
const sortedShardIds = [];
|
|
48
|
-
while (sortedShardIds.length !== this.shards.size) {
|
|
49
|
-
let shardAdded = false;
|
|
50
|
-
this.shards.forEach(({ shard }, shardId) => {
|
|
51
|
-
if (sortedShardIds.includes(shardId)) return;
|
|
52
|
-
if (shard.ParentShardId && !sortedShardIds.includes(shard.ParentShardId)) return;
|
|
53
|
-
sortedShardIds.push(shardId);
|
|
54
|
-
shardAdded = true;
|
|
55
|
-
});
|
|
56
|
-
if (!shardAdded) {
|
|
57
|
-
console.warn("Failed to topologically sort shards.");
|
|
58
|
-
break;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
let recordCount = 0;
|
|
62
|
-
for (const shardId of sortedShardIds) {
|
|
63
|
-
const records = await this.getShardRecords(shardId);
|
|
64
|
-
recordCount += records.length;
|
|
65
|
-
}
|
|
66
|
-
console.log(`Found ${recordCount} records across ${sortedShardIds.length} shards.`);
|
|
67
|
-
}
|
|
68
|
-
startPolling() {
|
|
69
|
-
clearTimeout(this.pollTimer);
|
|
70
|
-
this.pollTimer = setTimeout(() => {
|
|
71
|
-
this.processStream().then(() => {
|
|
72
|
-
if (this.pollTimer) this.startPolling();
|
|
73
|
-
});
|
|
74
|
-
}, 5e3);
|
|
75
|
-
}
|
|
76
|
-
stopPolling() {
|
|
77
|
-
clearTimeout(this.pollTimer);
|
|
78
|
-
this.pollTimer = void 0;
|
|
79
|
-
}
|
|
80
|
-
};
|
|
81
|
-
//#endregion
|
|
82
|
-
exports.EventStreamer = EventStreamer;
|
|
83
|
-
|
|
84
|
-
//# sourceMappingURL=streams.cjs.map
|
package/dist/streams.cjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"streams.cjs","names":["DescribeStreamCommand","GetShardIteratorCommand","GetRecordsCommand","ResourceNotFoundException","TrimmedDataAccessException"],"sources":["../src/streams.ts"],"sourcesContent":["import {\n type _Record,\n DescribeStreamCommand,\n type DynamoDBStreams,\n GetRecordsCommand,\n GetShardIteratorCommand,\n ResourceNotFoundException,\n type Shard,\n type ShardIteratorType,\n TrimmedDataAccessException,\n} from \"@aws-sdk/client-dynamodb-streams\";\n\nexport class EventStreamer {\n readonly client: DynamoDBStreams;\n readonly streamArn: string;\n\n private pollTimer: NodeJS.Timeout | undefined;\n\n // a map of shard ids to shards\n protected shards = new Map<string, { shard: Shard; iterator?: string }>();\n\n constructor(client: DynamoDBStreams, streamArn: string) {\n this.client = client;\n this.streamArn = streamArn;\n }\n\n protected async syncShards(\n iteratorType?: ShardIteratorType,\n sequenceNumber?: string,\n ): Promise<void> {\n // TODO: technically this will only return a maximum of 100 shards, pagination is required otherwise\n // DescribeStream can only be called 10 times per second\n // Closed streams can be returned, and will contain a EndingSequenceNumber\n const { StreamDescription } = await this.client.send(\n new DescribeStreamCommand({ StreamArn: this.streamArn }),\n );\n\n const availableShards = StreamDescription?.Shards ?? [];\n\n const newShards = availableShards.filter(\n (shard) => shard.ShardId && !this.shards.has(shard.ShardId),\n );\n\n // TODO: is this safe to process in parallel? Probably since its unlikely a large number\n // of shards would show up out of nowhere, but consider batching...\n await Promise.all(\n newShards.map(async (shard) => {\n // TODO: handle TrimmedDataAccessException which can happen if a sequence\n // number outside the 24hr sliding window is specified or the shard iterator\n // goes unused for 24 hrs (shouldn't be possible with this library)\n const { ShardIterator } = await this.client.send(\n new GetShardIteratorCommand({\n StreamArn: this.streamArn,\n ShardIteratorType: iteratorType ?? \"LATEST\",\n ShardId: shard.ShardId,\n SequenceNumber: sequenceNumber,\n }),\n );\n\n this.shards.set(shard.ShardId!, { shard, iterator: ShardIterator });\n }),\n );\n\n // clear out any shards that are no longer returned\n for (const shardId in this.shards.keys()) {\n const availableShard = availableShards.find((shard) => shard.ShardId === shardId);\n if (!availableShard) {\n this.shards.delete(shardId);\n }\n }\n }\n\n protected async getShardRecords(shardId: string): Promise<_Record[]> {\n const shard = this.shards.get(shardId);\n if (!shard?.iterator) return [];\n\n const data = await this.client\n .send(\n new GetRecordsCommand({\n ShardIterator: shard.iterator,\n }),\n )\n .catch((e) => {\n // we need to handle this case gracefully because of a quirk with dynamodb-local shard creation\n if (e instanceof ResourceNotFoundException || e instanceof TrimmedDataAccessException) {\n return { Records: [], NextShardIterator: undefined };\n }\n\n throw e;\n });\n\n shard.iterator = data.NextShardIterator;\n\n return data.Records ?? [];\n }\n\n protected async processStream(): Promise<void> {\n await this.syncShards(\"TRIM_HORIZON\");\n\n // topologically sort shard ids (parents come before children)\n // TODO: this could be optimized, but probably isn't worth it\n const sortedShardIds: string[] = [];\n while (sortedShardIds.length !== this.shards.size) {\n let shardAdded = false;\n this.shards.forEach(({ shard }, shardId) => {\n if (sortedShardIds.includes(shardId)) return;\n if (shard.ParentShardId && !sortedShardIds.includes(shard.ParentShardId)) return;\n\n sortedShardIds.push(shardId);\n shardAdded = true;\n });\n\n // this check is just to avoid an infinite loop and should\n // never happen if the API behaves as documented\n if (!shardAdded) {\n console.warn(\"Failed to topologically sort shards.\");\n break;\n }\n }\n\n // TODO: we could potentitally fetch shard records in parallel\n let recordCount = 0;\n for (const shardId of sortedShardIds) {\n const records = await this.getShardRecords(shardId);\n recordCount += records.length;\n }\n\n console.log(`Found ${recordCount} records across ${sortedShardIds.length} shards.`);\n }\n\n startPolling(): void {\n clearTimeout(this.pollTimer);\n this.pollTimer = setTimeout(() => {\n this.processStream().then(() => {\n if (this.pollTimer) this.startPolling();\n });\n }, 5000);\n }\n\n stopPolling(): void {\n clearTimeout(this.pollTimer);\n this.pollTimer = undefined;\n }\n}\n"],"mappings":";;;;AAYA,IAAa,gBAAb,MAA2B;CACzB;CACA;CAEA;CAGA,yBAAmB,IAAI,KAAkD;CAEzE,YAAY,QAAyB,WAAmB;AACtD,OAAK,SAAS;AACd,OAAK,YAAY;;CAGnB,MAAgB,WACd,cACA,gBACe;EAIf,MAAM,EAAE,sBAAsB,MAAM,KAAK,OAAO,KAC9C,IAAIA,iCAAAA,sBAAsB,EAAE,WAAW,KAAK,WAAW,CAAC,CACzD;EAED,MAAM,kBAAkB,mBAAmB,UAAU,EAAE;EAEvD,MAAM,YAAY,gBAAgB,QAC/B,UAAU,MAAM,WAAW,CAAC,KAAK,OAAO,IAAI,MAAM,QAAQ,CAC5D;AAID,QAAM,QAAQ,IACZ,UAAU,IAAI,OAAO,UAAU;GAI7B,MAAM,EAAE,kBAAkB,MAAM,KAAK,OAAO,KAC1C,IAAIC,iCAAAA,wBAAwB;IAC1B,WAAW,KAAK;IAChB,mBAAmB,gBAAgB;IACnC,SAAS,MAAM;IACf,gBAAgB;IACjB,CAAC,CACH;AAED,QAAK,OAAO,IAAI,MAAM,SAAU;IAAE;IAAO,UAAU;IAAe,CAAC;IACnE,CACH;AAGD,OAAK,MAAM,WAAW,KAAK,OAAO,MAAM,CAEtC,KAAI,CADmB,gBAAgB,MAAM,UAAU,MAAM,YAAY,QAAQ,CAE/E,MAAK,OAAO,OAAO,QAAQ;;CAKjC,MAAgB,gBAAgB,SAAqC;EACnE,MAAM,QAAQ,KAAK,OAAO,IAAI,QAAQ;AACtC,MAAI,CAAC,OAAO,SAAU,QAAO,EAAE;EAE/B,MAAM,OAAO,MAAM,KAAK,OACrB,KACC,IAAIC,iCAAAA,kBAAkB,EACpB,eAAe,MAAM,UACtB,CAAC,CACH,CACA,OAAO,MAAM;AAEZ,OAAI,aAAaC,iCAAAA,6BAA6B,aAAaC,iCAAAA,2BACzD,QAAO;IAAE,SAAS,EAAE;IAAE,mBAAmB,KAAA;IAAW;AAGtD,SAAM;IACN;AAEJ,QAAM,WAAW,KAAK;AAEtB,SAAO,KAAK,WAAW,EAAE;;CAG3B,MAAgB,gBAA+B;AAC7C,QAAM,KAAK,WAAW,eAAe;EAIrC,MAAM,iBAA2B,EAAE;AACnC,SAAO,eAAe,WAAW,KAAK,OAAO,MAAM;GACjD,IAAI,aAAa;AACjB,QAAK,OAAO,SAAS,EAAE,SAAS,YAAY;AAC1C,QAAI,eAAe,SAAS,QAAQ,CAAE;AACtC,QAAI,MAAM,iBAAiB,CAAC,eAAe,SAAS,MAAM,cAAc,CAAE;AAE1E,mBAAe,KAAK,QAAQ;AAC5B,iBAAa;KACb;AAIF,OAAI,CAAC,YAAY;AACf,YAAQ,KAAK,uCAAuC;AACpD;;;EAKJ,IAAI,cAAc;AAClB,OAAK,MAAM,WAAW,gBAAgB;GACpC,MAAM,UAAU,MAAM,KAAK,gBAAgB,QAAQ;AACnD,kBAAe,QAAQ;;AAGzB,UAAQ,IAAI,SAAS,YAAY,kBAAkB,eAAe,OAAO,UAAU;;CAGrF,eAAqB;AACnB,eAAa,KAAK,UAAU;AAC5B,OAAK,YAAY,iBAAiB;AAChC,QAAK,eAAe,CAAC,WAAW;AAC9B,QAAI,KAAK,UAAW,MAAK,cAAc;KACvC;KACD,IAAK;;CAGV,cAAoB;AAClB,eAAa,KAAK,UAAU;AAC5B,OAAK,YAAY,KAAA"}
|
package/dist/streams.d.cts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { DynamoDBStreams, Shard, ShardIteratorType, _Record } from "@aws-sdk/client-dynamodb-streams";
|
|
2
|
-
|
|
3
|
-
//#region src/streams.d.ts
|
|
4
|
-
declare class EventStreamer {
|
|
5
|
-
readonly client: DynamoDBStreams;
|
|
6
|
-
readonly streamArn: string;
|
|
7
|
-
private pollTimer;
|
|
8
|
-
protected shards: Map<string, {
|
|
9
|
-
shard: Shard;
|
|
10
|
-
iterator?: string;
|
|
11
|
-
}>;
|
|
12
|
-
constructor(client: DynamoDBStreams, streamArn: string);
|
|
13
|
-
protected syncShards(iteratorType?: ShardIteratorType, sequenceNumber?: string): Promise<void>;
|
|
14
|
-
protected getShardRecords(shardId: string): Promise<_Record[]>;
|
|
15
|
-
protected processStream(): Promise<void>;
|
|
16
|
-
startPolling(): void;
|
|
17
|
-
stopPolling(): void;
|
|
18
|
-
}
|
|
19
|
-
//#endregion
|
|
20
|
-
export { EventStreamer };
|
|
21
|
-
//# sourceMappingURL=streams.d.cts.map
|
package/dist/streams.d.cts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"streams.d.cts","names":[],"sources":["../src/streams.ts"],"mappings":";;;cAYa,aAAA;EAAA,SACF,MAAA,EAAQ,eAAA;EAAA,SACR,SAAA;EAAA,QAED,SAAA;EAAA,UAGE,MAAA,EAAM,GAAA;WAA4B,KAAA;;;EAE5C,WAAA,CAAY,MAAA,EAAQ,eAAA,EAAiB,SAAA;EAAA,UAKrB,UAAA,CACd,YAAA,GAAe,iBAAA,EACf,cAAA,YACC,OAAA;EAAA,UA2Ca,eAAA,CAAgB,OAAA,WAAkB,OAAA,CAAQ,OAAA;EAAA,UAwB1C,aAAA,CAAA,GAAiB,OAAA;EAkCjC,YAAA,CAAA;EASA,WAAA,CAAA;AAAA"}
|
package/dist/streams.d.mts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { DynamoDBStreams, Shard, ShardIteratorType, _Record } from "@aws-sdk/client-dynamodb-streams";
|
|
2
|
-
|
|
3
|
-
//#region src/streams.d.ts
|
|
4
|
-
declare class EventStreamer {
|
|
5
|
-
readonly client: DynamoDBStreams;
|
|
6
|
-
readonly streamArn: string;
|
|
7
|
-
private pollTimer;
|
|
8
|
-
protected shards: Map<string, {
|
|
9
|
-
shard: Shard;
|
|
10
|
-
iterator?: string;
|
|
11
|
-
}>;
|
|
12
|
-
constructor(client: DynamoDBStreams, streamArn: string);
|
|
13
|
-
protected syncShards(iteratorType?: ShardIteratorType, sequenceNumber?: string): Promise<void>;
|
|
14
|
-
protected getShardRecords(shardId: string): Promise<_Record[]>;
|
|
15
|
-
protected processStream(): Promise<void>;
|
|
16
|
-
startPolling(): void;
|
|
17
|
-
stopPolling(): void;
|
|
18
|
-
}
|
|
19
|
-
//#endregion
|
|
20
|
-
export { EventStreamer };
|
|
21
|
-
//# sourceMappingURL=streams.d.mts.map
|
package/dist/streams.d.mts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"streams.d.mts","names":[],"sources":["../src/streams.ts"],"mappings":";;;cAYa,aAAA;EAAA,SACF,MAAA,EAAQ,eAAA;EAAA,SACR,SAAA;EAAA,QAED,SAAA;EAAA,UAGE,MAAA,EAAM,GAAA;WAA4B,KAAA;;;EAE5C,WAAA,CAAY,MAAA,EAAQ,eAAA,EAAiB,SAAA;EAAA,UAKrB,UAAA,CACd,YAAA,GAAe,iBAAA,EACf,cAAA,YACC,OAAA;EAAA,UA2Ca,eAAA,CAAgB,OAAA,WAAkB,OAAA,CAAQ,OAAA;EAAA,UAwB1C,aAAA,CAAA,GAAiB,OAAA;EAkCjC,YAAA,CAAA;EASA,WAAA,CAAA;AAAA"}
|
package/dist/streams.mjs
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import { DescribeStreamCommand, GetRecordsCommand, GetShardIteratorCommand, ResourceNotFoundException, TrimmedDataAccessException } from "@aws-sdk/client-dynamodb-streams";
|
|
2
|
-
//#region src/streams.ts
|
|
3
|
-
var EventStreamer = class {
|
|
4
|
-
client;
|
|
5
|
-
streamArn;
|
|
6
|
-
pollTimer;
|
|
7
|
-
shards = /* @__PURE__ */ new Map();
|
|
8
|
-
constructor(client, streamArn) {
|
|
9
|
-
this.client = client;
|
|
10
|
-
this.streamArn = streamArn;
|
|
11
|
-
}
|
|
12
|
-
async syncShards(iteratorType, sequenceNumber) {
|
|
13
|
-
const { StreamDescription } = await this.client.send(new DescribeStreamCommand({ StreamArn: this.streamArn }));
|
|
14
|
-
const availableShards = StreamDescription?.Shards ?? [];
|
|
15
|
-
const newShards = availableShards.filter((shard) => shard.ShardId && !this.shards.has(shard.ShardId));
|
|
16
|
-
await Promise.all(newShards.map(async (shard) => {
|
|
17
|
-
const { ShardIterator } = await this.client.send(new GetShardIteratorCommand({
|
|
18
|
-
StreamArn: this.streamArn,
|
|
19
|
-
ShardIteratorType: iteratorType ?? "LATEST",
|
|
20
|
-
ShardId: shard.ShardId,
|
|
21
|
-
SequenceNumber: sequenceNumber
|
|
22
|
-
}));
|
|
23
|
-
this.shards.set(shard.ShardId, {
|
|
24
|
-
shard,
|
|
25
|
-
iterator: ShardIterator
|
|
26
|
-
});
|
|
27
|
-
}));
|
|
28
|
-
for (const shardId in this.shards.keys()) if (!availableShards.find((shard) => shard.ShardId === shardId)) this.shards.delete(shardId);
|
|
29
|
-
}
|
|
30
|
-
async getShardRecords(shardId) {
|
|
31
|
-
const shard = this.shards.get(shardId);
|
|
32
|
-
if (!shard?.iterator) return [];
|
|
33
|
-
const data = await this.client.send(new GetRecordsCommand({ ShardIterator: shard.iterator })).catch((e) => {
|
|
34
|
-
if (e instanceof ResourceNotFoundException || e instanceof TrimmedDataAccessException) return {
|
|
35
|
-
Records: [],
|
|
36
|
-
NextShardIterator: void 0
|
|
37
|
-
};
|
|
38
|
-
throw e;
|
|
39
|
-
});
|
|
40
|
-
shard.iterator = data.NextShardIterator;
|
|
41
|
-
return data.Records ?? [];
|
|
42
|
-
}
|
|
43
|
-
async processStream() {
|
|
44
|
-
await this.syncShards("TRIM_HORIZON");
|
|
45
|
-
const sortedShardIds = [];
|
|
46
|
-
while (sortedShardIds.length !== this.shards.size) {
|
|
47
|
-
let shardAdded = false;
|
|
48
|
-
this.shards.forEach(({ shard }, shardId) => {
|
|
49
|
-
if (sortedShardIds.includes(shardId)) return;
|
|
50
|
-
if (shard.ParentShardId && !sortedShardIds.includes(shard.ParentShardId)) return;
|
|
51
|
-
sortedShardIds.push(shardId);
|
|
52
|
-
shardAdded = true;
|
|
53
|
-
});
|
|
54
|
-
if (!shardAdded) {
|
|
55
|
-
console.warn("Failed to topologically sort shards.");
|
|
56
|
-
break;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
let recordCount = 0;
|
|
60
|
-
for (const shardId of sortedShardIds) {
|
|
61
|
-
const records = await this.getShardRecords(shardId);
|
|
62
|
-
recordCount += records.length;
|
|
63
|
-
}
|
|
64
|
-
console.log(`Found ${recordCount} records across ${sortedShardIds.length} shards.`);
|
|
65
|
-
}
|
|
66
|
-
startPolling() {
|
|
67
|
-
clearTimeout(this.pollTimer);
|
|
68
|
-
this.pollTimer = setTimeout(() => {
|
|
69
|
-
this.processStream().then(() => {
|
|
70
|
-
if (this.pollTimer) this.startPolling();
|
|
71
|
-
});
|
|
72
|
-
}, 5e3);
|
|
73
|
-
}
|
|
74
|
-
stopPolling() {
|
|
75
|
-
clearTimeout(this.pollTimer);
|
|
76
|
-
this.pollTimer = void 0;
|
|
77
|
-
}
|
|
78
|
-
};
|
|
79
|
-
//#endregion
|
|
80
|
-
export { EventStreamer };
|
|
81
|
-
|
|
82
|
-
//# sourceMappingURL=streams.mjs.map
|
package/dist/streams.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"streams.mjs","names":[],"sources":["../src/streams.ts"],"sourcesContent":["import {\n type _Record,\n DescribeStreamCommand,\n type DynamoDBStreams,\n GetRecordsCommand,\n GetShardIteratorCommand,\n ResourceNotFoundException,\n type Shard,\n type ShardIteratorType,\n TrimmedDataAccessException,\n} from \"@aws-sdk/client-dynamodb-streams\";\n\nexport class EventStreamer {\n readonly client: DynamoDBStreams;\n readonly streamArn: string;\n\n private pollTimer: NodeJS.Timeout | undefined;\n\n // a map of shard ids to shards\n protected shards = new Map<string, { shard: Shard; iterator?: string }>();\n\n constructor(client: DynamoDBStreams, streamArn: string) {\n this.client = client;\n this.streamArn = streamArn;\n }\n\n protected async syncShards(\n iteratorType?: ShardIteratorType,\n sequenceNumber?: string,\n ): Promise<void> {\n // TODO: technically this will only return a maximum of 100 shards, pagination is required otherwise\n // DescribeStream can only be called 10 times per second\n // Closed streams can be returned, and will contain a EndingSequenceNumber\n const { StreamDescription } = await this.client.send(\n new DescribeStreamCommand({ StreamArn: this.streamArn }),\n );\n\n const availableShards = StreamDescription?.Shards ?? [];\n\n const newShards = availableShards.filter(\n (shard) => shard.ShardId && !this.shards.has(shard.ShardId),\n );\n\n // TODO: is this safe to process in parallel? Probably since its unlikely a large number\n // of shards would show up out of nowhere, but consider batching...\n await Promise.all(\n newShards.map(async (shard) => {\n // TODO: handle TrimmedDataAccessException which can happen if a sequence\n // number outside the 24hr sliding window is specified or the shard iterator\n // goes unused for 24 hrs (shouldn't be possible with this library)\n const { ShardIterator } = await this.client.send(\n new GetShardIteratorCommand({\n StreamArn: this.streamArn,\n ShardIteratorType: iteratorType ?? \"LATEST\",\n ShardId: shard.ShardId,\n SequenceNumber: sequenceNumber,\n }),\n );\n\n this.shards.set(shard.ShardId!, { shard, iterator: ShardIterator });\n }),\n );\n\n // clear out any shards that are no longer returned\n for (const shardId in this.shards.keys()) {\n const availableShard = availableShards.find((shard) => shard.ShardId === shardId);\n if (!availableShard) {\n this.shards.delete(shardId);\n }\n }\n }\n\n protected async getShardRecords(shardId: string): Promise<_Record[]> {\n const shard = this.shards.get(shardId);\n if (!shard?.iterator) return [];\n\n const data = await this.client\n .send(\n new GetRecordsCommand({\n ShardIterator: shard.iterator,\n }),\n )\n .catch((e) => {\n // we need to handle this case gracefully because of a quirk with dynamodb-local shard creation\n if (e instanceof ResourceNotFoundException || e instanceof TrimmedDataAccessException) {\n return { Records: [], NextShardIterator: undefined };\n }\n\n throw e;\n });\n\n shard.iterator = data.NextShardIterator;\n\n return data.Records ?? [];\n }\n\n protected async processStream(): Promise<void> {\n await this.syncShards(\"TRIM_HORIZON\");\n\n // topologically sort shard ids (parents come before children)\n // TODO: this could be optimized, but probably isn't worth it\n const sortedShardIds: string[] = [];\n while (sortedShardIds.length !== this.shards.size) {\n let shardAdded = false;\n this.shards.forEach(({ shard }, shardId) => {\n if (sortedShardIds.includes(shardId)) return;\n if (shard.ParentShardId && !sortedShardIds.includes(shard.ParentShardId)) return;\n\n sortedShardIds.push(shardId);\n shardAdded = true;\n });\n\n // this check is just to avoid an infinite loop and should\n // never happen if the API behaves as documented\n if (!shardAdded) {\n console.warn(\"Failed to topologically sort shards.\");\n break;\n }\n }\n\n // TODO: we could potentitally fetch shard records in parallel\n let recordCount = 0;\n for (const shardId of sortedShardIds) {\n const records = await this.getShardRecords(shardId);\n recordCount += records.length;\n }\n\n console.log(`Found ${recordCount} records across ${sortedShardIds.length} shards.`);\n }\n\n startPolling(): void {\n clearTimeout(this.pollTimer);\n this.pollTimer = setTimeout(() => {\n this.processStream().then(() => {\n if (this.pollTimer) this.startPolling();\n });\n }, 5000);\n }\n\n stopPolling(): void {\n clearTimeout(this.pollTimer);\n this.pollTimer = undefined;\n }\n}\n"],"mappings":";;AAYA,IAAa,gBAAb,MAA2B;CACzB;CACA;CAEA;CAGA,yBAAmB,IAAI,KAAkD;CAEzE,YAAY,QAAyB,WAAmB;AACtD,OAAK,SAAS;AACd,OAAK,YAAY;;CAGnB,MAAgB,WACd,cACA,gBACe;EAIf,MAAM,EAAE,sBAAsB,MAAM,KAAK,OAAO,KAC9C,IAAI,sBAAsB,EAAE,WAAW,KAAK,WAAW,CAAC,CACzD;EAED,MAAM,kBAAkB,mBAAmB,UAAU,EAAE;EAEvD,MAAM,YAAY,gBAAgB,QAC/B,UAAU,MAAM,WAAW,CAAC,KAAK,OAAO,IAAI,MAAM,QAAQ,CAC5D;AAID,QAAM,QAAQ,IACZ,UAAU,IAAI,OAAO,UAAU;GAI7B,MAAM,EAAE,kBAAkB,MAAM,KAAK,OAAO,KAC1C,IAAI,wBAAwB;IAC1B,WAAW,KAAK;IAChB,mBAAmB,gBAAgB;IACnC,SAAS,MAAM;IACf,gBAAgB;IACjB,CAAC,CACH;AAED,QAAK,OAAO,IAAI,MAAM,SAAU;IAAE;IAAO,UAAU;IAAe,CAAC;IACnE,CACH;AAGD,OAAK,MAAM,WAAW,KAAK,OAAO,MAAM,CAEtC,KAAI,CADmB,gBAAgB,MAAM,UAAU,MAAM,YAAY,QAAQ,CAE/E,MAAK,OAAO,OAAO,QAAQ;;CAKjC,MAAgB,gBAAgB,SAAqC;EACnE,MAAM,QAAQ,KAAK,OAAO,IAAI,QAAQ;AACtC,MAAI,CAAC,OAAO,SAAU,QAAO,EAAE;EAE/B,MAAM,OAAO,MAAM,KAAK,OACrB,KACC,IAAI,kBAAkB,EACpB,eAAe,MAAM,UACtB,CAAC,CACH,CACA,OAAO,MAAM;AAEZ,OAAI,aAAa,6BAA6B,aAAa,2BACzD,QAAO;IAAE,SAAS,EAAE;IAAE,mBAAmB,KAAA;IAAW;AAGtD,SAAM;IACN;AAEJ,QAAM,WAAW,KAAK;AAEtB,SAAO,KAAK,WAAW,EAAE;;CAG3B,MAAgB,gBAA+B;AAC7C,QAAM,KAAK,WAAW,eAAe;EAIrC,MAAM,iBAA2B,EAAE;AACnC,SAAO,eAAe,WAAW,KAAK,OAAO,MAAM;GACjD,IAAI,aAAa;AACjB,QAAK,OAAO,SAAS,EAAE,SAAS,YAAY;AAC1C,QAAI,eAAe,SAAS,QAAQ,CAAE;AACtC,QAAI,MAAM,iBAAiB,CAAC,eAAe,SAAS,MAAM,cAAc,CAAE;AAE1E,mBAAe,KAAK,QAAQ;AAC5B,iBAAa;KACb;AAIF,OAAI,CAAC,YAAY;AACf,YAAQ,KAAK,uCAAuC;AACpD;;;EAKJ,IAAI,cAAc;AAClB,OAAK,MAAM,WAAW,gBAAgB;GACpC,MAAM,UAAU,MAAM,KAAK,gBAAgB,QAAQ;AACnD,kBAAe,QAAQ;;AAGzB,UAAQ,IAAI,SAAS,YAAY,kBAAkB,eAAe,OAAO,UAAU;;CAGrF,eAAqB;AACnB,eAAa,KAAK,UAAU;AAC5B,OAAK,YAAY,iBAAiB;AAChC,QAAK,eAAe,CAAC,WAAW;AAC9B,QAAI,KAAK,UAAW,MAAK,cAAc;KACvC;KACD,IAAK;;CAGV,cAAoB;AAClB,eAAa,KAAK,UAAU;AAC5B,OAAK,YAAY,KAAA"}
|