import { Context, Effect, Layer } from 'effect'; import type { BatchWriteItemOutput, GetItemOutput } from '../client/index.js'; import type { DynamoDBClient } from '../client/index.js'; import { marshall } from '../attribute-value/index.js'; import { DynamoDBNativeError } from '../setup/index.js'; declare const dynamoServiceType: unique symbol; export interface DynamoTableService { readonly client: DynamoDBClient; readonly tableName: string; readonly [dynamoServiceType]: Name; } const nativeTags = new Map< string, Context.Service, DynamoTableService> >(); export const dynamoTableService = (logicalName: Name) => { let tag = nativeTags.get(logicalName); if (tag === undefined) { tag = Context.Service>( `std-toolkit/db/DynamoTable/${logicalName}`, ); nativeTags.set(logicalName, tag); } return tag as unknown as Context.Service< DynamoTableService, DynamoTableService >; }; export const makeNativeService = ( logicalName: Name, client: DynamoDBClient, tableName: string, ) => { const tag = dynamoTableService(logicalName); const service = { client, tableName } as DynamoTableService; return Layer.succeed(tag, service); }; export const getItem = ( logicalName: Name, key: Readonly>, options?: { readonly consistentRead?: boolean }, ): Effect.Effect< GetItemOutput, DynamoDBNativeError, DynamoTableService > => Effect.gen(function* () { const service = yield* dynamoTableService(logicalName); return yield* service.client .getItem({ TableName: service.tableName, Key: marshall(key), ...(options?.consistentRead === undefined ? {} : { ConsistentRead: options.consistentRead }), }) .pipe( Effect.mapError( (cause) => new DynamoDBNativeError({ operation: 'getItem', cause }), ), ); }); export const batchInsert = < Name extends string, Item extends Readonly>, >( logicalName: Name, items: readonly Item[], ): Effect.Effect< BatchWriteItemOutput, DynamoDBNativeError, DynamoTableService > => Effect.gen(function* () { const service = yield* dynamoTableService(logicalName); const unprocessed = []; for (let offset = 0; offset < items.length; offset += 25) { const chunk = items.slice(offset, offset + 25); const result = yield* service.client .batchWriteItem({ RequestItems: { [service.tableName]: chunk.map((item) => ({ PutRequest: { Item: marshall(item) }, })), }, }) .pipe( Effect.mapError( (cause) => new DynamoDBNativeError({ operation: 'batchInsert', cause }), ), ); unprocessed.push(...(result.UnprocessedItems?.[service.tableName] ?? [])); } return unprocessed.length === 0 ? {} : { UnprocessedItems: { [service.tableName]: unprocessed } }; }); export { update } from './entity-update.js';