@jaypie/dynamodb 0.4.0 → 0.4.1

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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../src/client.ts","../../../src/constants.ts","../../../src/keyBuilders.ts","../../../src/entities.ts","../../../src/queries.ts","../../../src/query.ts","../../../src/seedExport.ts"],"sourcesContent":["import { DynamoDBClient } from \"@aws-sdk/client-dynamodb\";\nimport { DynamoDBDocumentClient } from \"@aws-sdk/lib-dynamodb\";\nimport { ConfigurationError } from \"@jaypie/errors\";\n\nimport type { DynamoClientConfig } from \"./types.js\";\n\n// Environment variable names\nconst ENV_AWS_REGION = \"AWS_REGION\";\nconst ENV_DYNAMODB_TABLE_NAME = \"DYNAMODB_TABLE_NAME\";\n\n// Defaults\nconst DEFAULT_REGION = \"us-east-1\";\nconst LOCAL_CREDENTIALS = {\n accessKeyId: \"local\",\n secretAccessKey: \"local\",\n};\n\n// Module-level state\nlet docClient: DynamoDBDocumentClient | null = null;\nlet tableName: string | null = null;\n\n/**\n * Check if endpoint indicates local development mode\n */\nfunction isLocalEndpoint(endpoint?: string): boolean {\n if (!endpoint) return false;\n return endpoint.includes(\"127.0.0.1\") || endpoint.includes(\"localhost\");\n}\n\n/**\n * Initialize the DynamoDB client\n * Must be called once at application startup before using query functions\n *\n * @param config - Client configuration\n */\nexport function initClient(config: DynamoClientConfig = {}): void {\n const { endpoint } = config;\n const region = config.region ?? process.env[ENV_AWS_REGION] ?? DEFAULT_REGION;\n\n // Auto-detect local mode and use dummy credentials\n const credentials =\n config.credentials ??\n (isLocalEndpoint(endpoint) ? LOCAL_CREDENTIALS : undefined);\n\n const dynamoClient = new DynamoDBClient({\n ...(credentials && { credentials }),\n ...(endpoint && { endpoint }),\n region,\n });\n\n docClient = DynamoDBDocumentClient.from(dynamoClient, {\n marshallOptions: {\n removeUndefinedValues: true,\n },\n });\n\n tableName = config.tableName ?? process.env[ENV_DYNAMODB_TABLE_NAME] ?? null;\n}\n\n/**\n * Get the initialized DynamoDB Document Client\n * @throws ConfigurationError if client has not been initialized\n */\nexport function getDocClient(): DynamoDBDocumentClient {\n if (!docClient) {\n throw new ConfigurationError(\n \"DynamoDB client not initialized. Call initClient() first.\",\n );\n }\n return docClient;\n}\n\n/**\n * Get the configured table name\n * @throws ConfigurationError if client has not been initialized\n */\nexport function getTableName(): string {\n if (!tableName) {\n throw new ConfigurationError(\n \"DynamoDB client not initialized. Call initClient() first.\",\n );\n }\n return tableName;\n}\n\n/**\n * Check if the client has been initialized\n */\nexport function isInitialized(): boolean {\n return docClient !== null && tableName !== null;\n}\n\n/**\n * Reset the client state (primarily for testing)\n */\nexport function resetClient(): void {\n docClient = null;\n tableName = null;\n}\n","// Re-export shared constants from fabric\nexport {\n APEX,\n ARCHIVED_SUFFIX,\n DELETED_SUFFIX,\n SEPARATOR,\n} from \"@jaypie/fabric\";\n\n// GSI names (derived from DEFAULT_INDEXES)\nexport const INDEX_ALIAS = \"indexAlias\";\nexport const INDEX_CLASS = \"indexClass\";\nexport const INDEX_SCOPE = \"indexScope\";\nexport const INDEX_TYPE = \"indexType\";\nexport const INDEX_XID = \"indexXid\";\n","import {\n buildCompositeKey as fabricBuildCompositeKey,\n calculateScope as fabricCalculateScope,\n DEFAULT_INDEXES,\n getModelIndexes,\n type IndexableModel,\n populateIndexKeys as fabricPopulateIndexKeys,\n} from \"@jaypie/fabric\";\n\nimport { APEX, SEPARATOR } from \"./constants.js\";\nimport type { ParentReference, StorableEntity } from \"./types.js\";\n\n// =============================================================================\n// Key Builders\n// =============================================================================\n\n/**\n * Build the indexScope key for hierarchical queries\n * @param scope - The scope (APEX or \"{parent.model}#{parent.id}\")\n * @param model - The entity model name\n * @returns Composite key: \"{scope}#{model}\"\n */\nexport function buildIndexScope(scope: string, model: string): string {\n return `${scope}${SEPARATOR}${model}`;\n}\n\n/**\n * Build the indexAlias key for human-friendly lookups\n * @param scope - The scope\n * @param model - The entity model name\n * @param alias - The human-friendly alias\n * @returns Composite key: \"{scope}#{model}#{alias}\"\n */\nexport function buildIndexAlias(\n scope: string,\n model: string,\n alias: string,\n): string {\n return `${scope}${SEPARATOR}${model}${SEPARATOR}${alias}`;\n}\n\n/**\n * Build the indexClass key for category filtering\n * @param scope - The scope\n * @param model - The entity model name\n * @param recordClass - The category classification\n * @returns Composite key: \"{scope}#{model}#{class}\"\n */\nexport function buildIndexClass(\n scope: string,\n model: string,\n recordClass: string,\n): string {\n return `${scope}${SEPARATOR}${model}${SEPARATOR}${recordClass}`;\n}\n\n/**\n * Build the indexType key for type filtering\n * @param scope - The scope\n * @param model - The entity model name\n * @param type - The type classification\n * @returns Composite key: \"{scope}#{model}#{type}\"\n */\nexport function buildIndexType(\n scope: string,\n model: string,\n type: string,\n): string {\n return `${scope}${SEPARATOR}${model}${SEPARATOR}${type}`;\n}\n\n/**\n * Build the indexXid key for external ID lookups\n * @param scope - The scope\n * @param model - The entity model name\n * @param xid - The external ID\n * @returns Composite key: \"{scope}#{model}#{xid}\"\n */\nexport function buildIndexXid(scope: string, model: string, xid: string): string {\n return `${scope}${SEPARATOR}${model}${SEPARATOR}${xid}`;\n}\n\n// =============================================================================\n// New Vocabulary-Based Functions\n// =============================================================================\n\n/**\n * Build a composite key from entity fields\n *\n * @param entity - Entity with fields to extract\n * @param fields - Field names to combine with SEPARATOR\n * @param suffix - Optional suffix to append (e.g., \"#deleted\")\n * @returns Composite key string\n */\nexport function buildCompositeKey(\n entity: IndexableModel,\n fields: string[],\n suffix?: string,\n): string {\n return fabricBuildCompositeKey(entity, fields, suffix);\n}\n\n/**\n * Calculate the scope from a parent reference\n * @param parent - Optional parent entity reference\n * @returns APEX (\"@\") if no parent, otherwise \"{parent.model}#{parent.id}\"\n */\nexport function calculateScope(parent?: ParentReference): string {\n if (!parent) {\n return APEX;\n }\n return fabricCalculateScope(parent);\n}\n\n/**\n * Auto-populate GSI index keys on an entity\n *\n * Uses the model's registered indexes (from vocabulary registry) or\n * DEFAULT_INDEXES if no custom indexes are registered.\n *\n * - indexScope is always populated from scope + model\n * - indexAlias is populated only when alias is present\n * - indexClass is populated only when class is present\n * - indexType is populated only when type is present\n * - indexXid is populated only when xid is present\n *\n * @param entity - The entity to populate index keys for\n * @param suffix - Optional suffix to append to all index keys (e.g., \"#deleted\", \"#archived\")\n * @returns The entity with populated index keys\n */\nexport function indexEntity<T extends StorableEntity>(\n entity: T,\n suffix: string = \"\",\n): T {\n const indexes = getModelIndexes(entity.model);\n // Cast through unknown to bridge the type gap between StorableEntity and IndexableModel\n return fabricPopulateIndexKeys(\n entity as unknown as IndexableModel,\n indexes,\n suffix,\n ) as unknown as T;\n}\n\n// Re-export DEFAULT_INDEXES for convenience\nexport { DEFAULT_INDEXES };\n","import { DeleteCommand, GetCommand, PutCommand } from \"@aws-sdk/lib-dynamodb\";\nimport { fabricService } from \"@jaypie/fabric\";\n\nimport { getDocClient, getTableName } from \"./client.js\";\nimport { ARCHIVED_SUFFIX, DELETED_SUFFIX } from \"./constants.js\";\nimport { indexEntity } from \"./keyBuilders.js\";\nimport type { StorableEntity } from \"./types.js\";\n\n/**\n * Calculate suffix based on entity's archived/deleted state\n */\nfunction calculateEntitySuffix(entity: {\n archivedAt?: string;\n deletedAt?: string;\n}): string {\n const hasArchived = Boolean(entity.archivedAt);\n const hasDeleted = Boolean(entity.deletedAt);\n\n if (hasArchived && hasDeleted) {\n return ARCHIVED_SUFFIX + DELETED_SUFFIX;\n }\n if (hasArchived) {\n return ARCHIVED_SUFFIX;\n }\n if (hasDeleted) {\n return DELETED_SUFFIX;\n }\n return \"\";\n}\n\n/**\n * Get a single entity by primary key\n */\nexport const getEntity = fabricService({\n alias: \"getEntity\",\n description: \"Get a single entity by primary key\",\n input: {\n id: { type: String, description: \"Entity ID (sort key)\" },\n model: { type: String, description: \"Entity model (partition key)\" },\n },\n service: async ({ id, model }): Promise<StorableEntity | null> => {\n const docClient = getDocClient();\n const tableName = getTableName();\n\n const command = new GetCommand({\n Key: { id, model },\n TableName: tableName,\n });\n\n const response = await docClient.send(command);\n return (response.Item as StorableEntity) ?? null;\n },\n});\n\n/**\n * Put (create or replace) an entity\n * Auto-populates GSI index keys via indexEntity\n *\n * Note: This is a regular async function (not fabricService) because it accepts\n * complex StorableEntity objects that can't be coerced by vocabulary's type system.\n */\nexport async function putEntity({\n entity,\n}: {\n entity: StorableEntity;\n}): Promise<StorableEntity> {\n const docClient = getDocClient();\n const tableName = getTableName();\n\n // Auto-populate index keys\n const indexedEntity = indexEntity(entity);\n\n const command = new PutCommand({\n Item: indexedEntity,\n TableName: tableName,\n });\n\n await docClient.send(command);\n return indexedEntity;\n}\n\n/**\n * Update an existing entity\n * Auto-populates GSI index keys and sets updatedAt\n *\n * Note: This is a regular async function (not fabricService) because it accepts\n * complex StorableEntity objects that can't be coerced by vocabulary's type system.\n */\nexport async function updateEntity({\n entity,\n}: {\n entity: StorableEntity;\n}): Promise<StorableEntity> {\n const docClient = getDocClient();\n const tableName = getTableName();\n\n // Update timestamp and re-index\n const updatedEntity = indexEntity({\n ...entity,\n updatedAt: new Date().toISOString(),\n });\n\n const command = new PutCommand({\n Item: updatedEntity,\n TableName: tableName,\n });\n\n await docClient.send(command);\n return updatedEntity;\n}\n\n/**\n * Soft delete an entity by setting deletedAt timestamp\n * Re-indexes with appropriate suffix based on archived/deleted state\n */\nexport const deleteEntity = fabricService({\n alias: \"deleteEntity\",\n description: \"Soft delete an entity (sets deletedAt timestamp)\",\n input: {\n id: { type: String, description: \"Entity ID (sort key)\" },\n model: { type: String, description: \"Entity model (partition key)\" },\n },\n service: async ({ id, model }): Promise<boolean> => {\n const docClient = getDocClient();\n const tableName = getTableName();\n\n // Fetch the current entity\n const existing = await getEntity({ id, model });\n if (!existing) {\n return false;\n }\n\n const now = new Date().toISOString();\n\n // Build updated entity with deletedAt\n const updatedEntity = {\n ...existing,\n deletedAt: now,\n updatedAt: now,\n };\n\n // Calculate suffix based on combined state (may already be archived)\n const suffix = calculateEntitySuffix(updatedEntity);\n const deletedEntity = indexEntity(updatedEntity, suffix);\n\n const command = new PutCommand({\n Item: deletedEntity,\n TableName: tableName,\n });\n\n await docClient.send(command);\n return true;\n },\n});\n\n/**\n * Archive an entity by setting archivedAt timestamp\n * Re-indexes with appropriate suffix based on archived/deleted state\n */\nexport const archiveEntity = fabricService({\n alias: \"archiveEntity\",\n description: \"Archive an entity (sets archivedAt timestamp)\",\n input: {\n id: { type: String, description: \"Entity ID (sort key)\" },\n model: { type: String, description: \"Entity model (partition key)\" },\n },\n service: async ({ id, model }): Promise<boolean> => {\n const docClient = getDocClient();\n const tableName = getTableName();\n\n // Fetch the current entity\n const existing = await getEntity({ id, model });\n if (!existing) {\n return false;\n }\n\n const now = new Date().toISOString();\n\n // Build updated entity with archivedAt\n const updatedEntity = {\n ...existing,\n archivedAt: now,\n updatedAt: now,\n };\n\n // Calculate suffix based on combined state (may already be deleted)\n const suffix = calculateEntitySuffix(updatedEntity);\n const archivedEntity = indexEntity(updatedEntity, suffix);\n\n const command = new PutCommand({\n Item: archivedEntity,\n TableName: tableName,\n });\n\n await docClient.send(command);\n return true;\n },\n});\n\n/**\n * Hard delete an entity (permanently removes from table)\n * Use with caution - prefer deleteEntity for soft delete\n */\nexport const destroyEntity = fabricService({\n alias: \"destroyEntity\",\n description: \"Hard delete an entity (permanently removes from table)\",\n input: {\n id: { type: String, description: \"Entity ID (sort key)\" },\n model: { type: String, description: \"Entity model (partition key)\" },\n },\n service: async ({ id, model }): Promise<boolean> => {\n const docClient = getDocClient();\n const tableName = getTableName();\n\n const command = new DeleteCommand({\n Key: { id, model },\n TableName: tableName,\n });\n\n await docClient.send(command);\n return true;\n },\n});\n","import { QueryCommand } from \"@aws-sdk/lib-dynamodb\";\nimport { fabricService } from \"@jaypie/fabric\";\n\nimport { getDocClient, getTableName } from \"./client.js\";\nimport {\n ARCHIVED_SUFFIX,\n DELETED_SUFFIX,\n INDEX_ALIAS,\n INDEX_CLASS,\n INDEX_SCOPE,\n INDEX_TYPE,\n INDEX_XID,\n} from \"./constants.js\";\nimport {\n buildIndexAlias,\n buildIndexClass,\n buildIndexScope,\n buildIndexType,\n buildIndexXid,\n} from \"./keyBuilders.js\";\nimport type { BaseQueryOptions, StorableEntity, QueryResult } from \"./types.js\";\n\n/**\n * Calculate the suffix based on archived/deleted flags\n * When both are true, returns combined suffix (archived first, alphabetically)\n */\nfunction calculateSuffix({\n archived,\n deleted,\n}: {\n archived?: boolean;\n deleted?: boolean;\n}): string {\n if (archived && deleted) {\n return ARCHIVED_SUFFIX + DELETED_SUFFIX;\n }\n if (archived) {\n return ARCHIVED_SUFFIX;\n }\n if (deleted) {\n return DELETED_SUFFIX;\n }\n return \"\";\n}\n\n/**\n * Execute a GSI query with common options\n */\nasync function executeQuery<T extends StorableEntity>(\n indexName: string,\n keyValue: string,\n options: BaseQueryOptions = {},\n): Promise<QueryResult<T>> {\n const { ascending = false, limit, startKey } = options;\n\n const docClient = getDocClient();\n const tableName = getTableName();\n\n const command = new QueryCommand({\n ExclusiveStartKey: startKey as Record<string, unknown> | undefined,\n ExpressionAttributeNames: {\n \"#pk\": indexName,\n },\n ExpressionAttributeValues: {\n \":pkValue\": keyValue,\n },\n IndexName: indexName,\n KeyConditionExpression: \"#pk = :pkValue\",\n ...(limit && { Limit: limit }),\n ScanIndexForward: ascending,\n TableName: tableName,\n });\n\n const response = await docClient.send(command);\n\n return {\n items: (response.Items ?? []) as T[],\n lastEvaluatedKey: response.LastEvaluatedKey,\n };\n}\n\n/**\n * Query parameters for queryByScope\n */\ninterface QueryByScopeParams extends BaseQueryOptions {\n model: string;\n scope: string;\n}\n\n/**\n * Query entities by scope (parent hierarchy)\n * Uses indexScope GSI\n *\n * Note: This is a regular async function (not fabricService) because it accepts\n * complex startKey objects that can't be coerced by vocabulary's type system.\n */\nexport async function queryByScope({\n archived = false,\n ascending = false,\n deleted = false,\n limit,\n model,\n scope,\n startKey,\n}: QueryByScopeParams): Promise<QueryResult<StorableEntity>> {\n const suffix = calculateSuffix({ archived, deleted });\n const keyValue = buildIndexScope(scope, model) + suffix;\n return executeQuery<StorableEntity>(INDEX_SCOPE, keyValue, {\n ascending,\n limit,\n startKey,\n });\n}\n\n/**\n * Query a single entity by human-friendly alias\n * Uses indexAlias GSI\n */\nexport const queryByAlias = fabricService({\n alias: \"queryByAlias\",\n description: \"Query a single entity by human-friendly alias\",\n input: {\n alias: { type: String, description: \"Human-friendly alias\" },\n archived: {\n type: Boolean,\n default: false,\n required: false,\n description: \"Query archived entities instead of active ones\",\n },\n deleted: {\n type: Boolean,\n default: false,\n required: false,\n description: \"Query deleted entities instead of active ones\",\n },\n model: { type: String, description: \"Entity model name\" },\n scope: { type: String, description: \"Scope (@ for root)\" },\n },\n service: async ({\n alias,\n archived,\n deleted,\n model,\n scope,\n }): Promise<StorableEntity | null> => {\n const aliasStr = alias as string;\n const archivedBool = archived as boolean | undefined;\n const deletedBool = deleted as boolean | undefined;\n const modelStr = model as string;\n const scopeStr = scope as string;\n\n const suffix = calculateSuffix({\n archived: archivedBool,\n deleted: deletedBool,\n });\n const keyValue = buildIndexAlias(scopeStr, modelStr, aliasStr) + suffix;\n const result = await executeQuery<StorableEntity>(INDEX_ALIAS, keyValue, {\n limit: 1,\n });\n return result.items[0] ?? null;\n },\n});\n\n/**\n * Query parameters for queryByClass\n */\ninterface QueryByClassParams extends BaseQueryOptions {\n model: string;\n scope: string;\n recordClass: string;\n}\n\n/**\n * Query entities by category classification\n * Uses indexClass GSI\n *\n * Note: This is a regular async function (not fabricService) because it accepts\n * complex startKey objects that can't be coerced by vocabulary's type system.\n */\nexport async function queryByClass({\n archived = false,\n ascending = false,\n deleted = false,\n limit,\n model,\n scope,\n recordClass,\n startKey,\n}: QueryByClassParams): Promise<QueryResult<StorableEntity>> {\n const suffix = calculateSuffix({ archived, deleted });\n const keyValue = buildIndexClass(scope, model, recordClass) + suffix;\n return executeQuery<StorableEntity>(INDEX_CLASS, keyValue, {\n ascending,\n limit,\n startKey,\n });\n}\n\n/**\n * Query parameters for queryByType\n */\ninterface QueryByTypeParams extends BaseQueryOptions {\n model: string;\n scope: string;\n type: string;\n}\n\n/**\n * Query entities by type classification\n * Uses indexType GSI\n *\n * Note: This is a regular async function (not fabricService) because it accepts\n * complex startKey objects that can't be coerced by vocabulary's type system.\n */\nexport async function queryByType({\n archived = false,\n ascending = false,\n deleted = false,\n limit,\n model,\n scope,\n startKey,\n type,\n}: QueryByTypeParams): Promise<QueryResult<StorableEntity>> {\n const suffix = calculateSuffix({ archived, deleted });\n const keyValue = buildIndexType(scope, model, type) + suffix;\n return executeQuery<StorableEntity>(INDEX_TYPE, keyValue, {\n ascending,\n limit,\n startKey,\n });\n}\n\n/**\n * Query a single entity by external ID\n * Uses indexXid GSI\n */\nexport const queryByXid = fabricService({\n alias: \"queryByXid\",\n description: \"Query a single entity by external ID\",\n input: {\n archived: {\n type: Boolean,\n default: false,\n required: false,\n description: \"Query archived entities instead of active ones\",\n },\n deleted: {\n type: Boolean,\n default: false,\n required: false,\n description: \"Query deleted entities instead of active ones\",\n },\n model: { type: String, description: \"Entity model name\" },\n scope: { type: String, description: \"Scope (@ for root)\" },\n xid: { type: String, description: \"External ID\" },\n },\n service: async ({\n archived,\n deleted,\n model,\n scope,\n xid,\n }): Promise<StorableEntity | null> => {\n const archivedBool = archived as boolean | undefined;\n const deletedBool = deleted as boolean | undefined;\n const modelStr = model as string;\n const scopeStr = scope as string;\n const xidStr = xid as string;\n\n const suffix = calculateSuffix({\n archived: archivedBool,\n deleted: deletedBool,\n });\n const keyValue = buildIndexXid(scopeStr, modelStr, xidStr) + suffix;\n const result = await executeQuery<StorableEntity>(INDEX_XID, keyValue, {\n limit: 1,\n });\n return result.items[0] ?? null;\n },\n});\n","/**\n * Unified Query Function with Auto-Detect Index Selection\n *\n * The query() function automatically selects the best index based on\n * the filter fields provided. This simplifies query construction by\n * removing the need to know which specific GSI to use.\n */\n\nimport { QueryCommand } from \"@aws-sdk/lib-dynamodb\";\nimport { ConfigurationError } from \"@jaypie/errors\";\nimport {\n DEFAULT_INDEXES,\n getModelIndexes,\n type IndexDefinition,\n} from \"@jaypie/fabric\";\n\nimport { getDocClient, getTableName } from \"./client.js\";\nimport { ARCHIVED_SUFFIX, DELETED_SUFFIX } from \"./constants.js\";\nimport { buildCompositeKey } from \"./keyBuilders.js\";\nimport type { QueryResult, StorableEntity } from \"./types.js\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Query parameters for the unified query function\n */\nexport interface QueryParams<T = StorableEntity> {\n /** Whether to query archived entities instead of active ones */\n archived?: boolean;\n /** Whether to sort ascending (oldest first). Default: false */\n ascending?: boolean;\n /** Whether to query deleted entities instead of active ones */\n deleted?: boolean;\n /** Filter object with field values to match. Used for index auto-detection. */\n filter?: Partial<T>;\n /** Maximum number of items to return */\n limit?: number;\n /** Model name (required) */\n model: string;\n /** Scope (APEX or \"{parent.model}#{parent.id}\") */\n scope?: string;\n /** Pagination cursor from previous query */\n startKey?: Record<string, unknown>;\n}\n\n/**\n * Score for an index based on filter field matching\n */\ninterface IndexScore {\n index: IndexDefinition;\n /** Whether all pk fields are present in the filter */\n pkComplete: boolean;\n /** Number of pk fields matched */\n matchedFields: number;\n}\n\n// =============================================================================\n// Helper Functions\n// =============================================================================\n\n/**\n * Calculate the suffix based on archived/deleted flags\n */\nfunction calculateSuffix(archived?: boolean, deleted?: boolean): string {\n if (archived && deleted) {\n return ARCHIVED_SUFFIX + DELETED_SUFFIX;\n }\n if (archived) {\n return ARCHIVED_SUFFIX;\n }\n if (deleted) {\n return DELETED_SUFFIX;\n }\n return \"\";\n}\n\n/**\n * Build a combined filter object from params\n */\nfunction buildFilterObject<T>(params: QueryParams<T>): Record<string, unknown> {\n const result: Record<string, unknown> = {\n model: params.model,\n };\n\n if (params.scope !== undefined) {\n result.scope = params.scope;\n }\n\n if (params.filter) {\n Object.assign(result, params.filter);\n }\n\n return result;\n}\n\n/**\n * Score an index based on how well it matches the filter fields\n */\nfunction scoreIndex(\n index: IndexDefinition,\n filterFields: Record<string, unknown>,\n): IndexScore {\n let matchedFields = 0;\n let pkComplete = true;\n\n for (const field of index.pk) {\n if (filterFields[field] !== undefined) {\n matchedFields++;\n } else {\n pkComplete = false;\n }\n }\n\n return {\n index,\n matchedFields,\n pkComplete,\n };\n}\n\n/**\n * Select the best index for the given filter\n *\n * Scoring criteria:\n * 1. Index must have all pk fields present (pkComplete)\n * 2. Prefer indexes with more matched fields\n * 3. Prefer more specific indexes (more pk fields)\n */\nfunction selectBestIndex(\n indexes: IndexDefinition[],\n filterFields: Record<string, unknown>,\n): IndexDefinition {\n const scores = indexes.map((index) => scoreIndex(index, filterFields));\n\n // Filter to only complete matches\n const completeMatches = scores.filter((s) => s.pkComplete);\n\n if (completeMatches.length === 0) {\n const availableIndexes = indexes\n .map((i) => i.name ?? `[${i.pk.join(\", \")}]`)\n .join(\", \");\n const providedFields = Object.keys(filterFields).join(\", \");\n throw new ConfigurationError(\n `No index matches filter fields. ` +\n `Provided: ${providedFields}. ` +\n `Available indexes: ${availableIndexes}`,\n );\n }\n\n // Sort by:\n // 1. More matched fields first (descending)\n // 2. More pk fields (more specific) first (descending)\n completeMatches.sort((a, b) => {\n const fieldDiff = b.matchedFields - a.matchedFields;\n if (fieldDiff !== 0) return fieldDiff;\n return b.index.pk.length - a.index.pk.length;\n });\n\n return completeMatches[0].index;\n}\n\n// =============================================================================\n// Main Query Function\n// =============================================================================\n\n/**\n * Query entities with automatic index selection\n *\n * The query function automatically selects the best GSI based on\n * the filter fields provided. This removes the need to know which\n * specific query function (queryByOu, queryByAlias, etc.) to use.\n *\n * @example\n * // Uses indexScope (pk: [\"scope\", \"model\"])\n * const allMessages = await query({ model: \"message\", scope: `chat#${chatId}` });\n *\n * @example\n * // Uses indexAlias (pk: [\"scope\", \"model\", \"alias\"])\n * const byAlias = await query({\n * model: \"record\",\n * scope: \"@\",\n * filter: { alias: \"my-record\" },\n * });\n *\n * @example\n * // Uses a custom registered index if model has one\n * const byChat = await query({\n * model: \"message\",\n * filter: { chatId: \"abc-123\" },\n * });\n */\nexport async function query<T extends StorableEntity = StorableEntity>(\n params: QueryParams<T>,\n): Promise<QueryResult<T>> {\n const {\n archived = false,\n ascending = false,\n deleted = false,\n limit,\n model,\n startKey,\n } = params;\n\n // Build the combined filter object\n const filterFields = buildFilterObject(params);\n\n // Get indexes for this model (custom or DEFAULT_INDEXES)\n const indexes = getModelIndexes(model);\n\n // Select the best matching index\n const selectedIndex = selectBestIndex(indexes, filterFields);\n const indexName = selectedIndex.name ?? generateIndexName(selectedIndex.pk);\n\n // Build the partition key value\n const suffix = calculateSuffix(archived, deleted);\n const keyValue = buildCompositeKey(\n filterFields as Record<string, unknown> & { model: string },\n selectedIndex.pk as string[],\n suffix,\n );\n\n // Execute the query\n const docClient = getDocClient();\n const tableName = getTableName();\n\n const command = new QueryCommand({\n ExclusiveStartKey: startKey as Record<string, unknown> | undefined,\n ExpressionAttributeNames: {\n \"#pk\": indexName,\n },\n ExpressionAttributeValues: {\n \":pkValue\": keyValue,\n },\n IndexName: indexName,\n KeyConditionExpression: \"#pk = :pkValue\",\n ...(limit && { Limit: limit }),\n ScanIndexForward: ascending,\n TableName: tableName,\n });\n\n const response = await docClient.send(command);\n\n return {\n items: (response.Items ?? []) as T[],\n lastEvaluatedKey: response.LastEvaluatedKey,\n };\n}\n\n/**\n * Generate an index name from pk fields\n */\nfunction generateIndexName(pk: string[]): string {\n const suffix = pk\n .map((field) => field.charAt(0).toUpperCase() + field.slice(1))\n .join(\"\");\n return `index${suffix}`;\n}\n\n// Re-export for convenience\nexport { DEFAULT_INDEXES, getModelIndexes };\n","import { putEntity } from \"./entities.js\";\nimport { queryByAlias, queryByScope } from \"./queries.js\";\nimport type { StorableEntity } from \"./types.js\";\n\n/**\n * Result of a seed operation\n */\nexport interface SeedResult {\n /** Aliases of entities that were created */\n created: string[];\n /** Error details for failed seed operations */\n errors: Array<{ alias: string; error: string }>;\n /** Aliases of entities that were skipped (already exist) */\n skipped: string[];\n}\n\n/**\n * Options for seed operations\n */\nexport interface SeedOptions {\n /** Preview without writing (default: false) */\n dryRun?: boolean;\n /** Overwrite existing entities (default: false) */\n replace?: boolean;\n}\n\n/**\n * Result of an export operation\n */\nexport interface ExportResult<T extends StorableEntity = StorableEntity> {\n /** Number of entities exported */\n count: number;\n /** Exported entities */\n entities: T[];\n}\n\n/**\n * Seed a single entity if it doesn't already exist\n *\n * @param entity - Partial entity with at least alias, model, and scope\n * @returns true if entity was created, false if it already exists\n */\nexport async function seedEntityIfNotExists<T extends Partial<StorableEntity>>(\n entity: T,\n): Promise<boolean> {\n if (!entity.alias || !entity.model || !entity.scope) {\n throw new Error(\"Entity must have alias, model, and scope to check existence\");\n }\n\n // Check if entity already exists\n const existing = await queryByAlias({\n alias: entity.alias,\n model: entity.model,\n scope: entity.scope,\n });\n\n if (existing) {\n return false;\n }\n\n // Generate required fields if missing\n const now = new Date().toISOString();\n const completeEntity: StorableEntity = {\n createdAt: entity.createdAt ?? now,\n id: entity.id ?? crypto.randomUUID(),\n model: entity.model,\n name: entity.name ?? entity.alias,\n scope: entity.scope,\n sequence: entity.sequence ?? Date.now(),\n updatedAt: entity.updatedAt ?? now,\n ...entity,\n } as StorableEntity;\n\n await putEntity({ entity: completeEntity });\n return true;\n}\n\n/**\n * Seed multiple entities (idempotent)\n *\n * - Checks existence by alias (via queryByAlias) before creating\n * - Auto-generates id (UUID), createdAt, updatedAt if missing\n * - Skip existing unless replace: true\n * - Returns counts of created/skipped/errors\n *\n * @param entities - Array of partial entities to seed\n * @param options - Seed options\n * @returns Result with created, skipped, and errors arrays\n */\nexport async function seedEntities<T extends Partial<StorableEntity>>(\n entities: T[],\n options: SeedOptions = {},\n): Promise<SeedResult> {\n const { dryRun = false, replace = false } = options;\n const result: SeedResult = {\n created: [],\n errors: [],\n skipped: [],\n };\n\n for (const entity of entities) {\n const alias = entity.alias ?? entity.name ?? \"unknown\";\n\n try {\n if (!entity.model || !entity.scope) {\n throw new Error(\"Entity must have model and scope\");\n }\n\n // For entities with alias, check existence\n if (entity.alias) {\n const existing = await queryByAlias({\n alias: entity.alias,\n model: entity.model,\n scope: entity.scope,\n });\n\n if (existing && !replace) {\n result.skipped.push(alias);\n continue;\n }\n\n // If replacing, use existing ID to update rather than create new\n if (existing && replace) {\n entity.id = existing.id;\n }\n }\n\n if (dryRun) {\n result.created.push(alias);\n continue;\n }\n\n // Generate required fields if missing\n const now = new Date().toISOString();\n const completeEntity: StorableEntity = {\n createdAt: entity.createdAt ?? now,\n id: entity.id ?? crypto.randomUUID(),\n model: entity.model,\n name: entity.name ?? entity.alias ?? \"Unnamed\",\n scope: entity.scope,\n sequence: entity.sequence ?? Date.now(),\n updatedAt: entity.updatedAt ?? now,\n ...entity,\n } as StorableEntity;\n\n await putEntity({ entity: completeEntity });\n result.created.push(alias);\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n result.errors.push({ alias, error: errorMessage });\n }\n }\n\n return result;\n}\n\n/**\n * Export entities by model and scope\n *\n * - Paginates through all matching entities via queryByScope\n * - Returns entities sorted by sequence (ascending)\n *\n * @param model - The entity model name\n * @param scope - The scope (APEX or \"{parent.model}#{parent.id}\")\n * @param limit - Optional maximum number of entities to export\n * @returns Export result with entities and count\n */\nexport async function exportEntities<T extends StorableEntity>(\n model: string,\n scope: string,\n limit?: number,\n): Promise<ExportResult<T>> {\n const entities: T[] = [];\n let startKey: Record<string, unknown> | undefined;\n let remaining = limit;\n\n do {\n const batchLimit =\n remaining !== undefined ? Math.min(remaining, 100) : undefined;\n\n const { items, lastEvaluatedKey } = await queryByScope({\n ascending: true,\n limit: batchLimit,\n model,\n scope,\n startKey,\n });\n\n entities.push(...(items as T[]));\n startKey = lastEvaluatedKey;\n\n if (remaining !== undefined) {\n remaining -= items.length;\n }\n } while (startKey && (remaining === undefined || remaining > 0));\n\n return {\n count: entities.length,\n entities,\n };\n}\n\n/**\n * Export entities as a JSON string\n *\n * @param model - The entity model name\n * @param scope - The scope (APEX or \"{parent.model}#{parent.id}\")\n * @param pretty - Format JSON with indentation (default: true)\n * @returns JSON string of exported entities\n */\nexport async function exportEntitiesToJson(\n model: string,\n scope: string,\n pretty: boolean = true,\n): Promise<string> {\n const { entities } = await exportEntities(model, scope);\n return pretty ? JSON.stringify(entities, null, 2) : JSON.stringify(entities);\n}\n"],"names":["fabricBuildCompositeKey","fabricCalculateScope","fabricPopulateIndexKeys","calculateSuffix"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../../src/client.ts","../../../src/constants.ts","../../../src/keyBuilders.ts","../../../src/entities.ts","../../../src/queries.ts","../../../src/query.ts","../../../src/seedExport.ts"],"sourcesContent":["import { DynamoDBClient } from \"@aws-sdk/client-dynamodb\";\nimport { DynamoDBDocumentClient } from \"@aws-sdk/lib-dynamodb\";\nimport { ConfigurationError } from \"@jaypie/errors\";\n\nimport type { DynamoClientConfig } from \"./types.js\";\n\n// Environment variable names\nconst ENV_AWS_REGION = \"AWS_REGION\";\nconst ENV_DYNAMODB_TABLE_NAME = \"DYNAMODB_TABLE_NAME\";\n\n// Defaults\nconst DEFAULT_REGION = \"us-east-1\";\nconst LOCAL_CREDENTIALS = {\n accessKeyId: \"local\",\n secretAccessKey: \"local\",\n};\n\n// Module-level state\nlet docClient: DynamoDBDocumentClient | null = null;\nlet tableName: string | null = null;\n\n/**\n * Check if endpoint indicates local development mode\n */\nfunction isLocalEndpoint(endpoint?: string): boolean {\n if (!endpoint) return false;\n return endpoint.includes(\"127.0.0.1\") || endpoint.includes(\"localhost\");\n}\n\n/**\n * Initialize the DynamoDB client\n * Must be called once at application startup before using query functions\n *\n * @param config - Client configuration\n */\nexport function initClient(config: DynamoClientConfig = {}): void {\n const { endpoint } = config;\n const region = config.region ?? process.env[ENV_AWS_REGION] ?? DEFAULT_REGION;\n\n // Auto-detect local mode and use dummy credentials\n const credentials =\n config.credentials ??\n (isLocalEndpoint(endpoint) ? LOCAL_CREDENTIALS : undefined);\n\n const dynamoClient = new DynamoDBClient({\n ...(credentials && { credentials }),\n ...(endpoint && { endpoint }),\n region,\n });\n\n docClient = DynamoDBDocumentClient.from(dynamoClient, {\n marshallOptions: {\n removeUndefinedValues: true,\n },\n });\n\n tableName = config.tableName ?? process.env[ENV_DYNAMODB_TABLE_NAME] ?? null;\n}\n\n/**\n * Get the initialized DynamoDB Document Client\n * @throws ConfigurationError if client has not been initialized\n */\nexport function getDocClient(): DynamoDBDocumentClient {\n if (!docClient) {\n throw new ConfigurationError(\n \"DynamoDB client not initialized. Call initClient() first.\",\n );\n }\n return docClient;\n}\n\n/**\n * Get the configured table name\n * @throws ConfigurationError if client has not been initialized\n */\nexport function getTableName(): string {\n if (!tableName) {\n throw new ConfigurationError(\n \"DynamoDB client not initialized. Call initClient() first.\",\n );\n }\n return tableName;\n}\n\n/**\n * Check if the client has been initialized\n */\nexport function isInitialized(): boolean {\n return docClient !== null && tableName !== null;\n}\n\n/**\n * Reset the client state (primarily for testing)\n */\nexport function resetClient(): void {\n docClient = null;\n tableName = null;\n}\n","// Re-export shared constants from fabric\nexport {\n APEX,\n ARCHIVED_SUFFIX,\n DELETED_SUFFIX,\n SEPARATOR,\n} from \"@jaypie/fabric\";\n\n// GSI names (derived from DEFAULT_INDEXES)\nexport const INDEX_ALIAS = \"indexAlias\";\nexport const INDEX_CATEGORY = \"indexCategory\";\nexport const INDEX_SCOPE = \"indexScope\";\nexport const INDEX_TYPE = \"indexType\";\nexport const INDEX_XID = \"indexXid\";\n","import {\n buildCompositeKey as fabricBuildCompositeKey,\n calculateScope as fabricCalculateScope,\n DEFAULT_INDEXES,\n getModelIndexes,\n type IndexableModel,\n populateIndexKeys as fabricPopulateIndexKeys,\n} from \"@jaypie/fabric\";\n\nimport { APEX, SEPARATOR } from \"./constants.js\";\nimport type { ParentReference, StorableEntity } from \"./types.js\";\n\n// =============================================================================\n// Key Builders\n// =============================================================================\n\n/**\n * Build the indexScope key for hierarchical queries\n * @param scope - The scope (APEX or \"{parent.model}#{parent.id}\")\n * @param model - The entity model name\n * @returns Composite key: \"{scope}#{model}\"\n */\nexport function buildIndexScope(scope: string, model: string): string {\n return `${scope}${SEPARATOR}${model}`;\n}\n\n/**\n * Build the indexAlias key for human-friendly lookups\n * @param scope - The scope\n * @param model - The entity model name\n * @param alias - The human-friendly alias\n * @returns Composite key: \"{scope}#{model}#{alias}\"\n */\nexport function buildIndexAlias(\n scope: string,\n model: string,\n alias: string,\n): string {\n return `${scope}${SEPARATOR}${model}${SEPARATOR}${alias}`;\n}\n\n/**\n * Build the indexCategory key for category filtering\n * @param scope - The scope\n * @param model - The entity model name\n * @param category - The category classification\n * @returns Composite key: \"{scope}#{model}#{category}\"\n */\nexport function buildIndexCategory(\n scope: string,\n model: string,\n category: string,\n): string {\n return `${scope}${SEPARATOR}${model}${SEPARATOR}${category}`;\n}\n\n/**\n * Build the indexType key for type filtering\n * @param scope - The scope\n * @param model - The entity model name\n * @param type - The type classification\n * @returns Composite key: \"{scope}#{model}#{type}\"\n */\nexport function buildIndexType(\n scope: string,\n model: string,\n type: string,\n): string {\n return `${scope}${SEPARATOR}${model}${SEPARATOR}${type}`;\n}\n\n/**\n * Build the indexXid key for external ID lookups\n * @param scope - The scope\n * @param model - The entity model name\n * @param xid - The external ID\n * @returns Composite key: \"{scope}#{model}#{xid}\"\n */\nexport function buildIndexXid(\n scope: string,\n model: string,\n xid: string,\n): string {\n return `${scope}${SEPARATOR}${model}${SEPARATOR}${xid}`;\n}\n\n// =============================================================================\n// New Vocabulary-Based Functions\n// =============================================================================\n\n/**\n * Build a composite key from entity fields\n *\n * @param entity - Entity with fields to extract\n * @param fields - Field names to combine with SEPARATOR\n * @param suffix - Optional suffix to append (e.g., \"#deleted\")\n * @returns Composite key string\n */\nexport function buildCompositeKey(\n entity: IndexableModel,\n fields: string[],\n suffix?: string,\n): string {\n return fabricBuildCompositeKey(entity, fields, suffix);\n}\n\n/**\n * Calculate the scope from a parent reference\n * @param parent - Optional parent entity reference\n * @returns APEX (\"@\") if no parent, otherwise \"{parent.model}#{parent.id}\"\n */\nexport function calculateScope(parent?: ParentReference): string {\n if (!parent) {\n return APEX;\n }\n return fabricCalculateScope(parent);\n}\n\n/**\n * Auto-populate GSI index keys on an entity\n *\n * Uses the model's registered indexes (from vocabulary registry) or\n * DEFAULT_INDEXES if no custom indexes are registered.\n *\n * - indexScope is always populated from scope + model\n * - indexAlias is populated only when alias is present\n * - indexCategory is populated only when category is present\n * - indexType is populated only when type is present\n * - indexXid is populated only when xid is present\n *\n * @param entity - The entity to populate index keys for\n * @param suffix - Optional suffix to append to all index keys (e.g., \"#deleted\", \"#archived\")\n * @returns The entity with populated index keys\n */\nexport function indexEntity<T extends StorableEntity>(\n entity: T,\n suffix: string = \"\",\n): T {\n const indexes = getModelIndexes(entity.model);\n // Cast through unknown to bridge the type gap between StorableEntity and IndexableModel\n return fabricPopulateIndexKeys(\n entity as unknown as IndexableModel,\n indexes,\n suffix,\n ) as unknown as T;\n}\n\n// Re-export DEFAULT_INDEXES for convenience\nexport { DEFAULT_INDEXES };\n","import { DeleteCommand, GetCommand, PutCommand } from \"@aws-sdk/lib-dynamodb\";\nimport { fabricService } from \"@jaypie/fabric\";\n\nimport { getDocClient, getTableName } from \"./client.js\";\nimport { ARCHIVED_SUFFIX, DELETED_SUFFIX } from \"./constants.js\";\nimport { indexEntity } from \"./keyBuilders.js\";\nimport type { StorableEntity } from \"./types.js\";\n\n/**\n * Calculate suffix based on entity's archived/deleted state\n */\nfunction calculateEntitySuffix(entity: {\n archivedAt?: string;\n deletedAt?: string;\n}): string {\n const hasArchived = Boolean(entity.archivedAt);\n const hasDeleted = Boolean(entity.deletedAt);\n\n if (hasArchived && hasDeleted) {\n return ARCHIVED_SUFFIX + DELETED_SUFFIX;\n }\n if (hasArchived) {\n return ARCHIVED_SUFFIX;\n }\n if (hasDeleted) {\n return DELETED_SUFFIX;\n }\n return \"\";\n}\n\n/**\n * Get a single entity by primary key\n */\nexport const getEntity = fabricService({\n alias: \"getEntity\",\n description: \"Get a single entity by primary key\",\n input: {\n id: { type: String, description: \"Entity ID (sort key)\" },\n model: { type: String, description: \"Entity model (partition key)\" },\n },\n service: async ({ id, model }): Promise<StorableEntity | null> => {\n const docClient = getDocClient();\n const tableName = getTableName();\n\n const command = new GetCommand({\n Key: { id, model },\n TableName: tableName,\n });\n\n const response = await docClient.send(command);\n return (response.Item as StorableEntity) ?? null;\n },\n});\n\n/**\n * Put (create or replace) an entity\n * Auto-populates GSI index keys via indexEntity\n *\n * Note: This is a regular async function (not fabricService) because it accepts\n * complex StorableEntity objects that can't be coerced by vocabulary's type system.\n */\nexport async function putEntity({\n entity,\n}: {\n entity: StorableEntity;\n}): Promise<StorableEntity> {\n const docClient = getDocClient();\n const tableName = getTableName();\n\n // Auto-populate index keys\n const indexedEntity = indexEntity(entity);\n\n const command = new PutCommand({\n Item: indexedEntity,\n TableName: tableName,\n });\n\n await docClient.send(command);\n return indexedEntity;\n}\n\n/**\n * Update an existing entity\n * Auto-populates GSI index keys and sets updatedAt\n *\n * Note: This is a regular async function (not fabricService) because it accepts\n * complex StorableEntity objects that can't be coerced by vocabulary's type system.\n */\nexport async function updateEntity({\n entity,\n}: {\n entity: StorableEntity;\n}): Promise<StorableEntity> {\n const docClient = getDocClient();\n const tableName = getTableName();\n\n // Update timestamp and re-index\n const updatedEntity = indexEntity({\n ...entity,\n updatedAt: new Date().toISOString(),\n });\n\n const command = new PutCommand({\n Item: updatedEntity,\n TableName: tableName,\n });\n\n await docClient.send(command);\n return updatedEntity;\n}\n\n/**\n * Soft delete an entity by setting deletedAt timestamp\n * Re-indexes with appropriate suffix based on archived/deleted state\n */\nexport const deleteEntity = fabricService({\n alias: \"deleteEntity\",\n description: \"Soft delete an entity (sets deletedAt timestamp)\",\n input: {\n id: { type: String, description: \"Entity ID (sort key)\" },\n model: { type: String, description: \"Entity model (partition key)\" },\n },\n service: async ({ id, model }): Promise<boolean> => {\n const docClient = getDocClient();\n const tableName = getTableName();\n\n // Fetch the current entity\n const existing = await getEntity({ id, model });\n if (!existing) {\n return false;\n }\n\n const now = new Date().toISOString();\n\n // Build updated entity with deletedAt\n const updatedEntity = {\n ...existing,\n deletedAt: now,\n updatedAt: now,\n };\n\n // Calculate suffix based on combined state (may already be archived)\n const suffix = calculateEntitySuffix(updatedEntity);\n const deletedEntity = indexEntity(updatedEntity, suffix);\n\n const command = new PutCommand({\n Item: deletedEntity,\n TableName: tableName,\n });\n\n await docClient.send(command);\n return true;\n },\n});\n\n/**\n * Archive an entity by setting archivedAt timestamp\n * Re-indexes with appropriate suffix based on archived/deleted state\n */\nexport const archiveEntity = fabricService({\n alias: \"archiveEntity\",\n description: \"Archive an entity (sets archivedAt timestamp)\",\n input: {\n id: { type: String, description: \"Entity ID (sort key)\" },\n model: { type: String, description: \"Entity model (partition key)\" },\n },\n service: async ({ id, model }): Promise<boolean> => {\n const docClient = getDocClient();\n const tableName = getTableName();\n\n // Fetch the current entity\n const existing = await getEntity({ id, model });\n if (!existing) {\n return false;\n }\n\n const now = new Date().toISOString();\n\n // Build updated entity with archivedAt\n const updatedEntity = {\n ...existing,\n archivedAt: now,\n updatedAt: now,\n };\n\n // Calculate suffix based on combined state (may already be deleted)\n const suffix = calculateEntitySuffix(updatedEntity);\n const archivedEntity = indexEntity(updatedEntity, suffix);\n\n const command = new PutCommand({\n Item: archivedEntity,\n TableName: tableName,\n });\n\n await docClient.send(command);\n return true;\n },\n});\n\n/**\n * Hard delete an entity (permanently removes from table)\n * Use with caution - prefer deleteEntity for soft delete\n */\nexport const destroyEntity = fabricService({\n alias: \"destroyEntity\",\n description: \"Hard delete an entity (permanently removes from table)\",\n input: {\n id: { type: String, description: \"Entity ID (sort key)\" },\n model: { type: String, description: \"Entity model (partition key)\" },\n },\n service: async ({ id, model }): Promise<boolean> => {\n const docClient = getDocClient();\n const tableName = getTableName();\n\n const command = new DeleteCommand({\n Key: { id, model },\n TableName: tableName,\n });\n\n await docClient.send(command);\n return true;\n },\n});\n","import { QueryCommand } from \"@aws-sdk/lib-dynamodb\";\nimport { fabricService } from \"@jaypie/fabric\";\n\nimport { getDocClient, getTableName } from \"./client.js\";\nimport {\n ARCHIVED_SUFFIX,\n DELETED_SUFFIX,\n INDEX_ALIAS,\n INDEX_CATEGORY,\n INDEX_SCOPE,\n INDEX_TYPE,\n INDEX_XID,\n} from \"./constants.js\";\nimport {\n buildIndexAlias,\n buildIndexCategory,\n buildIndexScope,\n buildIndexType,\n buildIndexXid,\n} from \"./keyBuilders.js\";\nimport type { BaseQueryOptions, StorableEntity, QueryResult } from \"./types.js\";\n\n/**\n * Calculate the suffix based on archived/deleted flags\n * When both are true, returns combined suffix (archived first, alphabetically)\n */\nfunction calculateSuffix({\n archived,\n deleted,\n}: {\n archived?: boolean;\n deleted?: boolean;\n}): string {\n if (archived && deleted) {\n return ARCHIVED_SUFFIX + DELETED_SUFFIX;\n }\n if (archived) {\n return ARCHIVED_SUFFIX;\n }\n if (deleted) {\n return DELETED_SUFFIX;\n }\n return \"\";\n}\n\n/**\n * Execute a GSI query with common options\n */\nasync function executeQuery<T extends StorableEntity>(\n indexName: string,\n keyValue: string,\n options: BaseQueryOptions = {},\n): Promise<QueryResult<T>> {\n const { ascending = false, limit, startKey } = options;\n\n const docClient = getDocClient();\n const tableName = getTableName();\n\n const command = new QueryCommand({\n ExclusiveStartKey: startKey as Record<string, unknown> | undefined,\n ExpressionAttributeNames: {\n \"#pk\": indexName,\n },\n ExpressionAttributeValues: {\n \":pkValue\": keyValue,\n },\n IndexName: indexName,\n KeyConditionExpression: \"#pk = :pkValue\",\n ...(limit && { Limit: limit }),\n ScanIndexForward: ascending,\n TableName: tableName,\n });\n\n const response = await docClient.send(command);\n\n return {\n items: (response.Items ?? []) as T[],\n lastEvaluatedKey: response.LastEvaluatedKey,\n };\n}\n\n/**\n * Query parameters for queryByScope\n */\ninterface QueryByScopeParams extends BaseQueryOptions {\n model: string;\n scope: string;\n}\n\n/**\n * Query entities by scope (parent hierarchy)\n * Uses indexScope GSI\n *\n * Note: This is a regular async function (not fabricService) because it accepts\n * complex startKey objects that can't be coerced by vocabulary's type system.\n */\nexport async function queryByScope({\n archived = false,\n ascending = false,\n deleted = false,\n limit,\n model,\n scope,\n startKey,\n}: QueryByScopeParams): Promise<QueryResult<StorableEntity>> {\n const suffix = calculateSuffix({ archived, deleted });\n const keyValue = buildIndexScope(scope, model) + suffix;\n return executeQuery<StorableEntity>(INDEX_SCOPE, keyValue, {\n ascending,\n limit,\n startKey,\n });\n}\n\n/**\n * Query a single entity by human-friendly alias\n * Uses indexAlias GSI\n */\nexport const queryByAlias = fabricService({\n alias: \"queryByAlias\",\n description: \"Query a single entity by human-friendly alias\",\n input: {\n alias: { type: String, description: \"Human-friendly alias\" },\n archived: {\n type: Boolean,\n default: false,\n required: false,\n description: \"Query archived entities instead of active ones\",\n },\n deleted: {\n type: Boolean,\n default: false,\n required: false,\n description: \"Query deleted entities instead of active ones\",\n },\n model: { type: String, description: \"Entity model name\" },\n scope: { type: String, description: \"Scope (@ for root)\" },\n },\n service: async ({\n alias,\n archived,\n deleted,\n model,\n scope,\n }): Promise<StorableEntity | null> => {\n const aliasStr = alias as string;\n const archivedBool = archived as boolean | undefined;\n const deletedBool = deleted as boolean | undefined;\n const modelStr = model as string;\n const scopeStr = scope as string;\n\n const suffix = calculateSuffix({\n archived: archivedBool,\n deleted: deletedBool,\n });\n const keyValue = buildIndexAlias(scopeStr, modelStr, aliasStr) + suffix;\n const result = await executeQuery<StorableEntity>(INDEX_ALIAS, keyValue, {\n limit: 1,\n });\n return result.items[0] ?? null;\n },\n});\n\n/**\n * Query parameters for queryByCategory\n */\ninterface QueryByCategoryParams extends BaseQueryOptions {\n category: string;\n model: string;\n scope: string;\n}\n\n/**\n * Query entities by category classification\n * Uses indexCategory GSI\n *\n * Note: This is a regular async function (not fabricService) because it accepts\n * complex startKey objects that can't be coerced by vocabulary's type system.\n */\nexport async function queryByCategory({\n archived = false,\n ascending = false,\n category,\n deleted = false,\n limit,\n model,\n scope,\n startKey,\n}: QueryByCategoryParams): Promise<QueryResult<StorableEntity>> {\n const suffix = calculateSuffix({ archived, deleted });\n const keyValue = buildIndexCategory(scope, model, category) + suffix;\n return executeQuery<StorableEntity>(INDEX_CATEGORY, keyValue, {\n ascending,\n limit,\n startKey,\n });\n}\n\n/**\n * Query parameters for queryByType\n */\ninterface QueryByTypeParams extends BaseQueryOptions {\n model: string;\n scope: string;\n type: string;\n}\n\n/**\n * Query entities by type classification\n * Uses indexType GSI\n *\n * Note: This is a regular async function (not fabricService) because it accepts\n * complex startKey objects that can't be coerced by vocabulary's type system.\n */\nexport async function queryByType({\n archived = false,\n ascending = false,\n deleted = false,\n limit,\n model,\n scope,\n startKey,\n type,\n}: QueryByTypeParams): Promise<QueryResult<StorableEntity>> {\n const suffix = calculateSuffix({ archived, deleted });\n const keyValue = buildIndexType(scope, model, type) + suffix;\n return executeQuery<StorableEntity>(INDEX_TYPE, keyValue, {\n ascending,\n limit,\n startKey,\n });\n}\n\n/**\n * Query a single entity by external ID\n * Uses indexXid GSI\n */\nexport const queryByXid = fabricService({\n alias: \"queryByXid\",\n description: \"Query a single entity by external ID\",\n input: {\n archived: {\n type: Boolean,\n default: false,\n required: false,\n description: \"Query archived entities instead of active ones\",\n },\n deleted: {\n type: Boolean,\n default: false,\n required: false,\n description: \"Query deleted entities instead of active ones\",\n },\n model: { type: String, description: \"Entity model name\" },\n scope: { type: String, description: \"Scope (@ for root)\" },\n xid: { type: String, description: \"External ID\" },\n },\n service: async ({\n archived,\n deleted,\n model,\n scope,\n xid,\n }): Promise<StorableEntity | null> => {\n const archivedBool = archived as boolean | undefined;\n const deletedBool = deleted as boolean | undefined;\n const modelStr = model as string;\n const scopeStr = scope as string;\n const xidStr = xid as string;\n\n const suffix = calculateSuffix({\n archived: archivedBool,\n deleted: deletedBool,\n });\n const keyValue = buildIndexXid(scopeStr, modelStr, xidStr) + suffix;\n const result = await executeQuery<StorableEntity>(INDEX_XID, keyValue, {\n limit: 1,\n });\n return result.items[0] ?? null;\n },\n});\n","/**\n * Unified Query Function with Auto-Detect Index Selection\n *\n * The query() function automatically selects the best index based on\n * the filter fields provided. This simplifies query construction by\n * removing the need to know which specific GSI to use.\n */\n\nimport { QueryCommand } from \"@aws-sdk/lib-dynamodb\";\nimport { ConfigurationError } from \"@jaypie/errors\";\nimport {\n DEFAULT_INDEXES,\n getModelIndexes,\n type IndexDefinition,\n} from \"@jaypie/fabric\";\n\nimport { getDocClient, getTableName } from \"./client.js\";\nimport { ARCHIVED_SUFFIX, DELETED_SUFFIX } from \"./constants.js\";\nimport { buildCompositeKey } from \"./keyBuilders.js\";\nimport type { QueryResult, StorableEntity } from \"./types.js\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Query parameters for the unified query function\n */\nexport interface QueryParams<T = StorableEntity> {\n /** Whether to query archived entities instead of active ones */\n archived?: boolean;\n /** Whether to sort ascending (oldest first). Default: false */\n ascending?: boolean;\n /** Whether to query deleted entities instead of active ones */\n deleted?: boolean;\n /** Filter object with field values to match. Used for index auto-detection. */\n filter?: Partial<T>;\n /** Maximum number of items to return */\n limit?: number;\n /** Model name (required) */\n model: string;\n /** Scope (APEX or \"{parent.model}#{parent.id}\") */\n scope?: string;\n /** Pagination cursor from previous query */\n startKey?: Record<string, unknown>;\n}\n\n/**\n * Score for an index based on filter field matching\n */\ninterface IndexScore {\n index: IndexDefinition;\n /** Whether all pk fields are present in the filter */\n pkComplete: boolean;\n /** Number of pk fields matched */\n matchedFields: number;\n}\n\n// =============================================================================\n// Helper Functions\n// =============================================================================\n\n/**\n * Calculate the suffix based on archived/deleted flags\n */\nfunction calculateSuffix(archived?: boolean, deleted?: boolean): string {\n if (archived && deleted) {\n return ARCHIVED_SUFFIX + DELETED_SUFFIX;\n }\n if (archived) {\n return ARCHIVED_SUFFIX;\n }\n if (deleted) {\n return DELETED_SUFFIX;\n }\n return \"\";\n}\n\n/**\n * Build a combined filter object from params\n */\nfunction buildFilterObject<T>(params: QueryParams<T>): Record<string, unknown> {\n const result: Record<string, unknown> = {\n model: params.model,\n };\n\n if (params.scope !== undefined) {\n result.scope = params.scope;\n }\n\n if (params.filter) {\n Object.assign(result, params.filter);\n }\n\n return result;\n}\n\n/**\n * Score an index based on how well it matches the filter fields\n */\nfunction scoreIndex(\n index: IndexDefinition,\n filterFields: Record<string, unknown>,\n): IndexScore {\n let matchedFields = 0;\n let pkComplete = true;\n\n for (const field of index.pk) {\n if (filterFields[field] !== undefined) {\n matchedFields++;\n } else {\n pkComplete = false;\n }\n }\n\n return {\n index,\n matchedFields,\n pkComplete,\n };\n}\n\n/**\n * Select the best index for the given filter\n *\n * Scoring criteria:\n * 1. Index must have all pk fields present (pkComplete)\n * 2. Prefer indexes with more matched fields\n * 3. Prefer more specific indexes (more pk fields)\n */\nfunction selectBestIndex(\n indexes: IndexDefinition[],\n filterFields: Record<string, unknown>,\n): IndexDefinition {\n const scores = indexes.map((index) => scoreIndex(index, filterFields));\n\n // Filter to only complete matches\n const completeMatches = scores.filter((s) => s.pkComplete);\n\n if (completeMatches.length === 0) {\n const availableIndexes = indexes\n .map((i) => i.name ?? `[${i.pk.join(\", \")}]`)\n .join(\", \");\n const providedFields = Object.keys(filterFields).join(\", \");\n throw new ConfigurationError(\n `No index matches filter fields. ` +\n `Provided: ${providedFields}. ` +\n `Available indexes: ${availableIndexes}`,\n );\n }\n\n // Sort by:\n // 1. More matched fields first (descending)\n // 2. More pk fields (more specific) first (descending)\n completeMatches.sort((a, b) => {\n const fieldDiff = b.matchedFields - a.matchedFields;\n if (fieldDiff !== 0) return fieldDiff;\n return b.index.pk.length - a.index.pk.length;\n });\n\n return completeMatches[0].index;\n}\n\n// =============================================================================\n// Main Query Function\n// =============================================================================\n\n/**\n * Query entities with automatic index selection\n *\n * The query function automatically selects the best GSI based on\n * the filter fields provided. This removes the need to know which\n * specific query function (queryByOu, queryByAlias, etc.) to use.\n *\n * @example\n * // Uses indexScope (pk: [\"scope\", \"model\"])\n * const allMessages = await query({ model: \"message\", scope: `chat#${chatId}` });\n *\n * @example\n * // Uses indexAlias (pk: [\"scope\", \"model\", \"alias\"])\n * const byAlias = await query({\n * model: \"record\",\n * scope: \"@\",\n * filter: { alias: \"my-record\" },\n * });\n *\n * @example\n * // Uses a custom registered index if model has one\n * const byChat = await query({\n * model: \"message\",\n * filter: { chatId: \"abc-123\" },\n * });\n */\nexport async function query<T extends StorableEntity = StorableEntity>(\n params: QueryParams<T>,\n): Promise<QueryResult<T>> {\n const {\n archived = false,\n ascending = false,\n deleted = false,\n limit,\n model,\n startKey,\n } = params;\n\n // Build the combined filter object\n const filterFields = buildFilterObject(params);\n\n // Get indexes for this model (custom or DEFAULT_INDEXES)\n const indexes = getModelIndexes(model);\n\n // Select the best matching index\n const selectedIndex = selectBestIndex(indexes, filterFields);\n const indexName = selectedIndex.name ?? generateIndexName(selectedIndex.pk);\n\n // Build the partition key value\n const suffix = calculateSuffix(archived, deleted);\n const keyValue = buildCompositeKey(\n filterFields as Record<string, unknown> & { model: string },\n selectedIndex.pk as string[],\n suffix,\n );\n\n // Execute the query\n const docClient = getDocClient();\n const tableName = getTableName();\n\n const command = new QueryCommand({\n ExclusiveStartKey: startKey as Record<string, unknown> | undefined,\n ExpressionAttributeNames: {\n \"#pk\": indexName,\n },\n ExpressionAttributeValues: {\n \":pkValue\": keyValue,\n },\n IndexName: indexName,\n KeyConditionExpression: \"#pk = :pkValue\",\n ...(limit && { Limit: limit }),\n ScanIndexForward: ascending,\n TableName: tableName,\n });\n\n const response = await docClient.send(command);\n\n return {\n items: (response.Items ?? []) as T[],\n lastEvaluatedKey: response.LastEvaluatedKey,\n };\n}\n\n/**\n * Generate an index name from pk fields\n */\nfunction generateIndexName(pk: string[]): string {\n const suffix = pk\n .map((field) => field.charAt(0).toUpperCase() + field.slice(1))\n .join(\"\");\n return `index${suffix}`;\n}\n\n// Re-export for convenience\nexport { DEFAULT_INDEXES, getModelIndexes };\n","import { putEntity } from \"./entities.js\";\nimport { queryByAlias, queryByScope } from \"./queries.js\";\nimport type { StorableEntity } from \"./types.js\";\n\n/**\n * Result of a seed operation\n */\nexport interface SeedResult {\n /** Aliases of entities that were created */\n created: string[];\n /** Error details for failed seed operations */\n errors: Array<{ alias: string; error: string }>;\n /** Aliases of entities that were skipped (already exist) */\n skipped: string[];\n}\n\n/**\n * Options for seed operations\n */\nexport interface SeedOptions {\n /** Preview without writing (default: false) */\n dryRun?: boolean;\n /** Overwrite existing entities (default: false) */\n replace?: boolean;\n}\n\n/**\n * Result of an export operation\n */\nexport interface ExportResult<T extends StorableEntity = StorableEntity> {\n /** Number of entities exported */\n count: number;\n /** Exported entities */\n entities: T[];\n}\n\n/**\n * Seed a single entity if it doesn't already exist\n *\n * @param entity - Partial entity with at least alias, model, and scope\n * @returns true if entity was created, false if it already exists\n */\nexport async function seedEntityIfNotExists<T extends Partial<StorableEntity>>(\n entity: T,\n): Promise<boolean> {\n if (!entity.alias || !entity.model || !entity.scope) {\n throw new Error(\n \"Entity must have alias, model, and scope to check existence\",\n );\n }\n\n // Check if entity already exists\n const existing = await queryByAlias({\n alias: entity.alias,\n model: entity.model,\n scope: entity.scope,\n });\n\n if (existing) {\n return false;\n }\n\n // Generate required fields if missing\n const now = new Date().toISOString();\n const completeEntity: StorableEntity = {\n createdAt: entity.createdAt ?? now,\n id: entity.id ?? crypto.randomUUID(),\n model: entity.model,\n name: entity.name ?? entity.alias,\n scope: entity.scope,\n sequence: entity.sequence ?? Date.now(),\n updatedAt: entity.updatedAt ?? now,\n ...entity,\n } as StorableEntity;\n\n await putEntity({ entity: completeEntity });\n return true;\n}\n\n/**\n * Seed multiple entities (idempotent)\n *\n * - Checks existence by alias (via queryByAlias) before creating\n * - Auto-generates id (UUID), createdAt, updatedAt if missing\n * - Skip existing unless replace: true\n * - Returns counts of created/skipped/errors\n *\n * @param entities - Array of partial entities to seed\n * @param options - Seed options\n * @returns Result with created, skipped, and errors arrays\n */\nexport async function seedEntities<T extends Partial<StorableEntity>>(\n entities: T[],\n options: SeedOptions = {},\n): Promise<SeedResult> {\n const { dryRun = false, replace = false } = options;\n const result: SeedResult = {\n created: [],\n errors: [],\n skipped: [],\n };\n\n for (const entity of entities) {\n const alias = entity.alias ?? entity.name ?? \"unknown\";\n\n try {\n if (!entity.model || !entity.scope) {\n throw new Error(\"Entity must have model and scope\");\n }\n\n // For entities with alias, check existence\n if (entity.alias) {\n const existing = await queryByAlias({\n alias: entity.alias,\n model: entity.model,\n scope: entity.scope,\n });\n\n if (existing && !replace) {\n result.skipped.push(alias);\n continue;\n }\n\n // If replacing, use existing ID to update rather than create new\n if (existing && replace) {\n entity.id = existing.id;\n }\n }\n\n if (dryRun) {\n result.created.push(alias);\n continue;\n }\n\n // Generate required fields if missing\n const now = new Date().toISOString();\n const completeEntity: StorableEntity = {\n createdAt: entity.createdAt ?? now,\n id: entity.id ?? crypto.randomUUID(),\n model: entity.model,\n name: entity.name ?? entity.alias ?? \"Unnamed\",\n scope: entity.scope,\n sequence: entity.sequence ?? Date.now(),\n updatedAt: entity.updatedAt ?? now,\n ...entity,\n } as StorableEntity;\n\n await putEntity({ entity: completeEntity });\n result.created.push(alias);\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n result.errors.push({ alias, error: errorMessage });\n }\n }\n\n return result;\n}\n\n/**\n * Export entities by model and scope\n *\n * - Paginates through all matching entities via queryByScope\n * - Returns entities sorted by sequence (ascending)\n *\n * @param model - The entity model name\n * @param scope - The scope (APEX or \"{parent.model}#{parent.id}\")\n * @param limit - Optional maximum number of entities to export\n * @returns Export result with entities and count\n */\nexport async function exportEntities<T extends StorableEntity>(\n model: string,\n scope: string,\n limit?: number,\n): Promise<ExportResult<T>> {\n const entities: T[] = [];\n let startKey: Record<string, unknown> | undefined;\n let remaining = limit;\n\n do {\n const batchLimit =\n remaining !== undefined ? Math.min(remaining, 100) : undefined;\n\n const { items, lastEvaluatedKey } = await queryByScope({\n ascending: true,\n limit: batchLimit,\n model,\n scope,\n startKey,\n });\n\n entities.push(...(items as T[]));\n startKey = lastEvaluatedKey;\n\n if (remaining !== undefined) {\n remaining -= items.length;\n }\n } while (startKey && (remaining === undefined || remaining > 0));\n\n return {\n count: entities.length,\n entities,\n };\n}\n\n/**\n * Export entities as a JSON string\n *\n * @param model - The entity model name\n * @param scope - The scope (APEX or \"{parent.model}#{parent.id}\")\n * @param pretty - Format JSON with indentation (default: true)\n * @returns JSON string of exported entities\n */\nexport async function exportEntitiesToJson(\n model: string,\n scope: string,\n pretty: boolean = true,\n): Promise<string> {\n const { entities } = await exportEntities(model, scope);\n return pretty ? JSON.stringify(entities, null, 2) : JSON.stringify(entities);\n}\n"],"names":["fabricBuildCompositeKey","fabricCalculateScope","fabricPopulateIndexKeys","calculateSuffix"],"mappings":"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"}
@@ -16,13 +16,13 @@ export declare function buildIndexScope(scope: string, model: string): string;
16
16
  */
17
17
  export declare function buildIndexAlias(scope: string, model: string, alias: string): string;
18
18
  /**
19
- * Build the indexClass key for category filtering
19
+ * Build the indexCategory key for category filtering
20
20
  * @param scope - The scope
21
21
  * @param model - The entity model name
22
- * @param recordClass - The category classification
23
- * @returns Composite key: "{scope}#{model}#{class}"
22
+ * @param category - The category classification
23
+ * @returns Composite key: "{scope}#{model}#{category}"
24
24
  */
25
- export declare function buildIndexClass(scope: string, model: string, recordClass: string): string;
25
+ export declare function buildIndexCategory(scope: string, model: string, category: string): string;
26
26
  /**
27
27
  * Build the indexType key for type filtering
28
28
  * @param scope - The scope
@@ -62,7 +62,7 @@ export declare function calculateScope(parent?: ParentReference): string;
62
62
  *
63
63
  * - indexScope is always populated from scope + model
64
64
  * - indexAlias is populated only when alias is present
65
- * - indexClass is populated only when class is present
65
+ * - indexCategory is populated only when category is present
66
66
  * - indexType is populated only when type is present
67
67
  * - indexXid is populated only when xid is present
68
68
  *
@@ -78,7 +78,7 @@ function isInitialized() {
78
78
  // Re-export shared constants from fabric
79
79
  // GSI names (derived from DEFAULT_INDEXES)
80
80
  const INDEX_ALIAS = "indexAlias";
81
- const INDEX_CLASS = "indexClass";
81
+ const INDEX_CATEGORY = "indexCategory";
82
82
  const INDEX_SCOPE = "indexScope";
83
83
  const INDEX_TYPE = "indexType";
84
84
  const INDEX_XID = "indexXid";
@@ -106,14 +106,14 @@ function buildIndexAlias(scope, model, alias) {
106
106
  return `${scope}${SEPARATOR}${model}${SEPARATOR}${alias}`;
107
107
  }
108
108
  /**
109
- * Build the indexClass key for category filtering
109
+ * Build the indexCategory key for category filtering
110
110
  * @param scope - The scope
111
111
  * @param model - The entity model name
112
- * @param recordClass - The category classification
113
- * @returns Composite key: "{scope}#{model}#{class}"
112
+ * @param category - The category classification
113
+ * @returns Composite key: "{scope}#{model}#{category}"
114
114
  */
115
- function buildIndexClass(scope, model, recordClass) {
116
- return `${scope}${SEPARATOR}${model}${SEPARATOR}${recordClass}`;
115
+ function buildIndexCategory(scope, model, category) {
116
+ return `${scope}${SEPARATOR}${model}${SEPARATOR}${category}`;
117
117
  }
118
118
  /**
119
119
  * Build the indexType key for type filtering
@@ -143,7 +143,7 @@ function buildIndexXid(scope, model, xid) {
143
143
  *
144
144
  * - indexScope is always populated from scope + model
145
145
  * - indexAlias is populated only when alias is present
146
- * - indexClass is populated only when class is present
146
+ * - indexCategory is populated only when category is present
147
147
  * - indexType is populated only when type is present
148
148
  * - indexXid is populated only when xid is present
149
149
  *
@@ -435,15 +435,15 @@ const queryByAlias = fabricService({
435
435
  });
436
436
  /**
437
437
  * Query entities by category classification
438
- * Uses indexClass GSI
438
+ * Uses indexCategory GSI
439
439
  *
440
440
  * Note: This is a regular async function (not fabricService) because it accepts
441
441
  * complex startKey objects that can't be coerced by vocabulary's type system.
442
442
  */
443
- async function queryByClass({ archived = false, ascending = false, deleted = false, limit, model, scope, recordClass, startKey, }) {
443
+ async function queryByCategory({ archived = false, ascending = false, category, deleted = false, limit, model, scope, startKey, }) {
444
444
  const suffix = calculateSuffix({ archived, deleted });
445
- const keyValue = buildIndexClass(scope, model, recordClass) + suffix;
446
- return executeQuery(INDEX_CLASS, keyValue, {
445
+ const keyValue = buildIndexCategory(scope, model, category) + suffix;
446
+ return executeQuery(INDEX_CATEGORY, keyValue, {
447
447
  ascending,
448
448
  limit,
449
449
  startKey,
@@ -798,6 +798,7 @@ const statusHandler = fabricService({
798
798
 
799
799
  /**
800
800
  * Wrap a handler to auto-initialize before execution
801
+ * Uses explicit type assertion to allow any Service type to be wrapped
801
802
  */
802
803
  function wrapWithInit(handler) {
803
804
  const wrapped = async (input) => {
@@ -833,7 +834,7 @@ const mcpPutEntity = fabricService({
833
834
  required: false,
834
835
  description: "Human-friendly alias",
835
836
  },
836
- class: {
837
+ category: {
837
838
  type: String,
838
839
  required: false,
839
840
  description: "Category classification",
@@ -845,7 +846,7 @@ const mcpPutEntity = fabricService({
845
846
  const now = new Date().toISOString();
846
847
  const entity = {
847
848
  alias: input.alias,
848
- class: input.class,
849
+ category: input.category,
849
850
  createdAt: now,
850
851
  id: input.id,
851
852
  model: input.model,
@@ -878,7 +879,7 @@ const mcpUpdateEntity = fabricService({
878
879
  required: false,
879
880
  description: "Human-friendly alias",
880
881
  },
881
- class: {
882
+ category: {
882
883
  type: String,
883
884
  required: false,
884
885
  description: "Category classification",
@@ -899,7 +900,9 @@ const mcpUpdateEntity = fabricService({
899
900
  const entity = {
900
901
  ...existing,
901
902
  ...(input.alias !== undefined && { alias: input.alias }),
902
- ...(input.class !== undefined && { class: input.class }),
903
+ ...(input.category !== undefined && {
904
+ category: input.category,
905
+ }),
903
906
  ...(input.name !== undefined && { name: input.name }),
904
907
  ...(input.scope !== undefined && { scope: input.scope }),
905
908
  ...(input.type !== undefined && { type: input.type }),
@@ -954,16 +957,16 @@ const mcpQueryByScope = fabricService({
954
957
  },
955
958
  });
956
959
  /**
957
- * MCP wrapper for queryByClass
960
+ * MCP wrapper for queryByCategory
958
961
  * Note: Pagination via startKey is not exposed to MCP; use limit instead
959
962
  */
960
- const mcpQueryByClass = fabricService({
961
- alias: "dynamodb_query_class",
963
+ const mcpQueryByCategory = fabricService({
964
+ alias: "dynamodb_query_category",
962
965
  description: "Query entities by category classification",
963
966
  input: {
967
+ category: { type: String, description: "Category classification" },
964
968
  model: { type: String, description: "Entity model name" },
965
969
  scope: { type: String, description: "Scope (@ for root)" },
966
- recordClass: { type: String, description: "Category classification" },
967
970
  archived: {
968
971
  type: Boolean,
969
972
  default: false,
@@ -989,14 +992,14 @@ const mcpQueryByClass = fabricService({
989
992
  },
990
993
  },
991
994
  service: async (input) => {
992
- return queryByClass({
995
+ return queryByCategory({
993
996
  archived: input.archived,
994
997
  ascending: input.ascending,
998
+ category: input.category,
995
999
  deleted: input.deleted,
996
1000
  limit: input.limit,
997
1001
  model: input.model,
998
1002
  scope: input.scope,
999
- recordClass: input.recordClass,
1000
1003
  });
1001
1004
  },
1002
1005
  });
@@ -1104,11 +1107,11 @@ function registerDynamoDbTools(config) {
1104
1107
  });
1105
1108
  tools.push("dynamodb_query_alias");
1106
1109
  fabricMcp({
1107
- service: wrapWithInit(mcpQueryByClass),
1108
- name: "dynamodb_query_class",
1110
+ service: wrapWithInit(mcpQueryByCategory),
1111
+ name: "dynamodb_query_category",
1109
1112
  server,
1110
1113
  });
1111
- tools.push("dynamodb_query_class");
1114
+ tools.push("dynamodb_query_category");
1112
1115
  fabricMcp({
1113
1116
  service: wrapWithInit(mcpQueryByType),
1114
1117
  name: "dynamodb_query_type",