@fjell/lib
Version:
Server-side Library for Fjell
1,449 lines (1,417 loc) • 50.6 kB
JavaScript
var __defProp = Object.defineProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
// src/errors.ts
import { abbrevIK, isComKey, isPriKey } from "@fjell/core";
var LibError = class _LibError extends Error {
operation;
coordinate;
constructor(message, operation, coordinate, options) {
super(`${message} - ${coordinate} - ${operation}`, options);
this.operation = operation;
this.coordinate = coordinate;
if (Error.captureStackTrace) {
Error.captureStackTrace(this, _LibError);
}
}
};
var NotFoundError = class extends LibError {
key;
constructor(operation, coordinate, key, options) {
const keyTypeArray = coordinate.kta;
const isCompositeLibrary = keyTypeArray.length > 1;
const keyType = isComKey(key) ? "ComKey" : "PriKey";
const message = [
`Item not found for key: ${abbrevIK(key)}`,
``,
`Note: If you believe this item should exist, verify:`,
`1. The key values are correct: ${abbrevIK(key)}`,
`2. The item was created in the expected location`,
isCompositeLibrary ? `3. This is a composite item library - keys should have both parent and location components` : `3. This is a primary item library - keys should only have a primary key`,
``,
`Expected key type: ${keyType}`
].join("\n");
super(message, operation, coordinate, options);
this.key = key;
}
};
var InvalidKeyTypeError = class extends LibError {
key;
expectedFormat;
receivedFormat;
constructor(operation, coordinate, key, expectedIsComposite, options) {
const keyTypeArray = coordinate.kta;
let expectedFormat;
let receivedFormat;
if (expectedIsComposite) {
const locationTypes = keyTypeArray.slice(1).join(", ");
expectedFormat = `ComKey with format: { kt: '${keyTypeArray[0]}', pk: string|number, loc: [${locationTypes.split(", ").map((kt) => `{ kt: '${kt}', lk: string|number }`).join(", ")}] }`;
} else {
expectedFormat = `PriKey with format: { kt: '${keyTypeArray[0]}', pk: string|number }`;
}
if (typeof key === "string" || typeof key === "number") {
receivedFormat = `a ${typeof key} value: ${JSON.stringify(key)}`;
} else if (key && typeof key === "object") {
if (isPriKey(key)) {
receivedFormat = `PriKey: ${abbrevIK(key)}`;
} else if (isComKey(key)) {
receivedFormat = `ComKey: ${abbrevIK(key)}`;
} else {
receivedFormat = `an object: ${JSON.stringify(key)}`;
}
} else {
receivedFormat = `${typeof key}: ${JSON.stringify(key)}`;
}
const message = [
`Invalid key type for ${operation} operation.`,
`Expected: ${expectedFormat}`,
`Received: ${receivedFormat}`,
``,
expectedIsComposite ? `This is a composite item library. You must provide both the parent key and location keys.` : `This is a primary item library. You should provide just the primary key.`,
``,
`Example correct usage:`,
expectedIsComposite ? ` library.operations.${operation}({ kt: '${keyTypeArray[0]}', pk: 'parent-id', loc: [{ kt: '${keyTypeArray[1]}', lk: 'child-id' }] })` : ` library.operations.${operation}({ kt: '${keyTypeArray[0]}', pk: 'item-id' })`
].join("\n");
super(message, operation, coordinate, options);
this.key = key;
this.expectedFormat = expectedFormat;
this.receivedFormat = receivedFormat;
}
};
var NotUpdatedError = class extends LibError {
key;
constructor(operation, coordinate, key, options) {
super(`Item not updated for key ${abbrevIK(key)}`, operation, coordinate, options);
this.key = key;
}
};
var ValidationError = class extends LibError {
constructor(message, operation, coordinate, options) {
super(`Validation failed: ${message}`, operation, coordinate, options);
}
};
var CreateValidationError = class extends ValidationError {
constructor(parameters, coordinate, options) {
super(
`Create Validation Failed: ${JSON.stringify(parameters)}`,
"create",
coordinate,
options
);
}
};
var UpdateValidationError = class extends ValidationError {
constructor(parameters, coordinate, options) {
super(
`Update Validation Failed: ${JSON.stringify(parameters)}`,
"update",
coordinate,
options
);
}
};
var RemoveValidationError = class extends ValidationError {
constructor(parameters, coordinate, options) {
const keyInfo = parameters.key ? `key: ${abbrevIK(parameters.key)}` : "key: undefined";
super(
`Remove Validation Failed: ${keyInfo}`,
"remove",
coordinate,
options
);
}
};
var UpdateError = class extends LibError {
constructor(parameters, coordinate, options) {
const keyInfo = parameters.key ? `key: ${abbrevIK(parameters.key)}` : "key: undefined";
super(
`Update Failed: ${keyInfo}`,
"update",
coordinate,
options
);
}
};
var RemoveError = class extends LibError {
constructor(parameters, coordinate, options) {
const keyInfo = parameters.key ? `key: ${abbrevIK(parameters.key)}` : "key: undefined";
super(
`Remove Failed: ${keyInfo}`,
"remove",
coordinate,
options
);
}
};
var HookError = class extends LibError {
constructor(message, operation, coordinate, options) {
super(`${message}`, operation, coordinate, options);
}
};
var LocationKeyOrderError = class extends LibError {
key;
constructor(operation, coordinate, key, options) {
const keyTypeArray = coordinate.kta;
const expectedLocationTypes = keyTypeArray.slice(1);
const actualLocationTypes = key.loc.map((loc) => loc.kt);
const expectedOrder = expectedLocationTypes.map(
(kt, i) => ` [${i}] { kt: '${kt}', lk: <value> }`
).join("\n");
const actualOrder = key.loc.map(
(loc, i) => ` [${i}] { kt: '${loc.kt}', lk: ${JSON.stringify(loc.lk)} }`
).join("\n");
const mismatches = [];
for (let i = 0; i < Math.max(expectedLocationTypes.length, actualLocationTypes.length); i++) {
if (i >= expectedLocationTypes.length) {
mismatches.push(` \u2022 Position ${i}: Unexpected location key with type '${actualLocationTypes[i]}'`);
} else if (i >= actualLocationTypes.length) {
mismatches.push(` \u2022 Position ${i}: Missing location key with type '${expectedLocationTypes[i]}'`);
} else if (expectedLocationTypes[i] !== actualLocationTypes[i]) {
mismatches.push(` \u2022 Position ${i}: Expected '${expectedLocationTypes[i]}' but got '${actualLocationTypes[i]}'`);
}
}
const message = [
`Location key array order mismatch for ${operation} operation.`,
``,
`The location keys in your ComKey must match the hierarchy defined by the library.`,
``,
`Expected location key order for '${keyTypeArray[0]}':`,
expectedOrder,
``,
`Received location key order:`,
actualOrder,
``,
`Issues found:`,
...mismatches,
``,
`Understanding the hierarchy:`,
` The key type array [${keyTypeArray.map((kt) => `'${kt}'`).join(", ")}] defines the containment hierarchy.`,
` - '${keyTypeArray[0]}' is the primary item type`,
expectedLocationTypes.length > 0 ? ` - '${keyTypeArray[0]}' items are contained in '${expectedLocationTypes[0]}'` : "",
expectedLocationTypes.length > 1 ? ` - '${expectedLocationTypes[0]}' items are contained in '${expectedLocationTypes[1]}'` : "",
expectedLocationTypes.length > 2 ? ` - '${expectedLocationTypes[1]}' items are contained in '${expectedLocationTypes[2]}'` : "",
``,
`Correct example:`,
` library.operations.${operation}({`,
` kt: '${keyTypeArray[0]}',`,
` pk: 'item-id',`,
` loc: [`,
expectedLocationTypes.map((kt) => ` { kt: '${kt}', lk: 'parent-id' }`).join(",\n"),
` ]`,
` })`
].filter((line) => line !== "").join("\n");
super(message, operation, coordinate, options);
this.key = key;
}
};
// src/logger.ts
import Logging from "@fjell/logging";
var LibLogger = Logging.getLogger("@fjell/lib");
var logger_default = LibLogger;
// src/Library.ts
import { createInstance as createBaseInstance } from "@fjell/registry";
var logger = logger_default.get("Library");
var createLibrary = (registry, coordinate, operations, options) => {
logger.debug("createLibrary", { coordinate, operations, registry, options });
const baseInstance = createBaseInstance(registry, coordinate);
return { ...baseInstance, operations, options: options || {} };
};
var isLibrary = (library) => {
return library !== null && typeof library !== "undefined" && typeof library.coordinate !== "undefined" && typeof library.operations !== "undefined" && typeof library.options !== "undefined" && typeof library.registry !== "undefined";
};
// src/LibraryFactory.ts
var logger2 = logger_default.get("InstanceFactory");
var createLibraryFactory = (operations, options) => {
return (coordinate, context) => {
logger2.debug("Creating lib instance", { coordinate, registry: context.registry, operations, options });
return createLibrary(context.registry, coordinate, operations, options);
};
};
// src/Operations.ts
import { isComKey as isComKey2, isPriKey as isPriKey2 } from "@fjell/core";
// src/ops/all.ts
import {
executeWithContext
} from "@fjell/core";
import { validateLocations } from "@fjell/validation";
var logger3 = logger_default.get("library", "ops", "all");
var wrapAllOperation = (toWrap, options, coordinate, registry) => {
const all = async (query, locations, allOptions) => {
const locs = locations ?? [];
logger3.debug("all", { query, locations: locs, options: allOptions });
if (locs.length > 0) {
validateLocations(locs, coordinate, "all");
}
const context = {
itemType: coordinate.kta[0],
operationType: "all",
operationName: "all",
params: { query, locations: locs, options: allOptions },
locations: locs
};
return executeWithContext(
() => toWrap.all(query, locs, allOptions),
context
);
};
return all;
};
// src/ops/create.ts
import {
executeWithContext as executeWithContext2
} from "@fjell/core";
import { validateKey, validateLocations as validateLocations2, validateSchema } from "@fjell/validation";
var logger4 = logger_default.get("library", "ops", "create");
var wrapCreateOperation = (toWrap, options, coordinate, registry) => {
const libOptions = options;
const create = async (item, createOptions3) => {
try {
logger4.debug("create", { item, createOptions: createOptions3 });
if (createOptions3) {
if ("key" in createOptions3 && createOptions3.key) {
validateKey(createOptions3.key, coordinate, "create");
}
if ("locations" in createOptions3 && createOptions3.locations) {
validateLocations2(createOptions3.locations, coordinate, "create");
}
}
let itemToCreate = item;
itemToCreate = await runPreCreateHook(itemToCreate, createOptions3);
await validateCreate(itemToCreate, createOptions3);
const context = {
itemType: coordinate.kta[0],
operationType: "create",
operationName: "create",
params: { item: itemToCreate, options: createOptions3 },
locations: createOptions3?.locations
};
let createdItem = await executeWithContext2(
() => toWrap.create(itemToCreate, createOptions3),
context
);
createdItem = await runPostCreateHook(createdItem);
logger4.debug("Create operation completed successfully", { createdItem });
return createdItem;
} catch (error) {
if (error instanceof CreateValidationError) {
throw error;
}
logger4.error("Create operation failed", {
component: "lib",
operation: "create",
itemType: coordinate.kta[0],
itemData: JSON.stringify(item),
createOptions: JSON.stringify(createOptions3),
errorType: error?.constructor?.name || typeof error,
errorMessage: error?.message,
errorCode: error?.errorInfo?.code || error?.code,
suggestion: "Check validation rules, required fields, unique constraints, and hooks implementation",
coordinate: JSON.stringify(coordinate),
stack: error?.stack
});
throw new Error(error.message, { cause: error });
}
};
return create;
async function runPreCreateHook(item, options2) {
let itemToCreate = item;
if (libOptions?.hooks?.preCreate) {
try {
itemToCreate = await libOptions.hooks.preCreate(itemToCreate, options2);
} catch (error) {
logger4.error("preCreate hook failed", {
component: "lib",
operation: "create",
hook: "preCreate",
itemType: coordinate.kta[0],
itemData: JSON.stringify(item),
errorType: error?.constructor?.name,
errorMessage: error?.message,
suggestion: "Check preCreate hook implementation for errors",
coordinate: JSON.stringify(coordinate),
stack: error?.stack
});
throw new HookError(
"Error in preCreate",
"create",
coordinate,
{ cause: error }
);
}
} else {
}
return itemToCreate;
}
async function runPostCreateHook(createdItem) {
if (libOptions?.hooks?.postCreate) {
try {
createdItem = await libOptions.hooks.postCreate(createdItem);
} catch (error) {
throw new HookError(
"Error in postCreate",
"create",
coordinate,
{ cause: error }
);
}
} else {
}
return createdItem;
}
async function validateCreate(item, options2) {
if (libOptions?.validation?.schema) {
try {
await validateSchema(item, libOptions.validation.schema);
} catch (error) {
throw new CreateValidationError(
{ item, options: options2 },
coordinate,
{ cause: error }
);
}
}
if (!libOptions?.validators?.onCreate) {
return;
}
try {
const isValid = await libOptions.validators.onCreate(item, options2);
if (!isValid) {
throw new CreateValidationError(
{ item, options: options2 },
coordinate,
{ cause: new Error("Invalid item") }
);
}
} catch (error) {
if (error instanceof CreateValidationError) throw error;
throw new CreateValidationError(
{ item, options: options2 },
coordinate,
{ cause: error }
);
}
}
};
// src/ops/find.ts
import {
executeWithContext as executeWithContext3,
ValidationError as ValidationError2
} from "@fjell/core";
var logger5 = logger_default.get("library", "ops", "find");
var wrapFindOperation = (toWrap, options, coordinate, registry) => {
const { finders } = options || {};
const find = async (finder, params, locations, findOptions) => {
const locs = locations ?? [];
logger5.debug("find", { finder, params, locations: locs, findOptions });
if (!finders?.[finder]) {
const availableFinders = finders ? Object.keys(finders) : [];
throw new ValidationError2(
`Finder "${finder}" not found`,
availableFinders,
"Use one of the available finders"
);
}
const context = {
itemType: coordinate.kta[0],
operationType: "find",
operationName: finder,
params,
locations: locs
};
return executeWithContext3(
() => toWrap.find(finder, params, locs, findOptions),
context
);
};
return find;
};
// src/ops/findOne.ts
import {
executeWithContext as executeWithContext4,
ValidationError as ValidationError3
} from "@fjell/core";
var logger6 = logger_default.get("library", "ops", "findOne");
var wrapFindOneOperation = (toWrap, options, coordinate, registry) => {
const { finders } = options || {};
const findOne = async (finder, params, locations) => {
const locs = locations ?? [];
logger6.debug("findOne", { finder, params, locations: locs });
if (!finders?.[finder]) {
const availableFinders = finders ? Object.keys(finders) : [];
throw new ValidationError3(
`Finder "${finder}" not found`,
availableFinders,
"Use one of the available finders"
);
}
const context = {
itemType: coordinate.kta[0],
operationType: "findOne",
operationName: finder,
params,
locations: locs
};
return executeWithContext4(
() => toWrap.findOne(finder, params, locs),
context
);
};
return findOne;
};
// src/ops/get.ts
import {
executeWithContext as executeWithContext5
} from "@fjell/core";
import { validateKey as validateKey2 } from "@fjell/validation";
var logger7 = logger_default.get("library", "ops", "get");
var wrapGetOperation = (toWrap, options, coordinate, registry) => {
const get = async (key) => {
logger7.debug("get", { key });
validateKey2(key, coordinate, "get");
const context = {
itemType: coordinate.kta[0],
operationType: "get",
operationName: "get",
params: { key },
key
};
return executeWithContext5(
() => toWrap.get(key),
context
);
};
return get;
};
// src/ops/one.ts
import {
executeWithContext as executeWithContext6
} from "@fjell/core";
var logger8 = logger_default.get("library", "ops", "one");
var wrapOneOperation = (toWrap, options, coordinate, registry) => {
const one = async (query, locations) => {
const locs = locations ?? [];
logger8.debug("one", { query, locations: locs });
const context = {
itemType: coordinate.kta[0],
operationType: "one",
operationName: "one",
params: { query, locations: locs },
locations: locs
};
return executeWithContext6(
() => toWrap.one(query, locs),
context
);
};
return one;
};
// src/ops/remove.ts
import {
executeWithContext as executeWithContext7
} from "@fjell/core";
var logger9 = logger_default.get("library", "ops", "remove");
var wrapRemoveOperation = (toWrap, options, coordinate, registry) => {
const remove = async (key) => {
try {
logger9.debug("remove", { key });
await runPreRemoveHook(key);
await validateRemove(key);
const context = {
itemType: coordinate.kta[0],
operationType: "remove",
operationName: "remove",
params: { key },
key
};
const item = await executeWithContext7(
() => toWrap.remove(key),
context
);
if (!item) {
logger9.error("Remove operation failed - no item returned", { key });
throw new RemoveError({ key }, coordinate);
}
await runPostRemoveHook(item);
logger9.debug("Remove operation completed successfully", { item });
return item;
} catch (error) {
throw new Error(error.message, { cause: error });
}
};
return remove;
async function runPreRemoveHook(key) {
if (options?.hooks?.preRemove) {
try {
await options.hooks.preRemove(key);
} catch (error) {
throw new HookError("preRemove", "remove", coordinate, { cause: error });
}
} else {
}
}
async function runPostRemoveHook(item) {
if (options?.hooks?.postRemove) {
try {
await options.hooks.postRemove(item);
} catch (error) {
throw new HookError("postRemove", "remove", coordinate, { cause: error });
}
} else {
}
}
async function validateRemove(key) {
if (!options?.validators?.onRemove) {
return;
}
try {
const isValid = await options.validators.onRemove(key);
if (!isValid) {
throw new RemoveValidationError(
{ key },
coordinate,
{ cause: new Error("Error validating remove") }
);
}
} catch (error) {
throw new RemoveValidationError(
{ key },
coordinate,
{ cause: error }
);
}
}
};
// src/ops/update.ts
import {
executeWithContext as executeWithContext8
} from "@fjell/core";
import { validateSchema as validateSchema2 } from "@fjell/validation";
var logger10 = logger_default.get("library", "ops", "update");
var wrapUpdateOperation = (toWrap, options, coordinate, registry) => {
const update = async (key, item) => {
try {
logger10.debug("update", { key, item });
let originalItem;
if (options?.hooks?.onChange) {
try {
originalItem = await toWrap.get(key);
} catch (error) {
logger10.warning("Failed to fetch original item for onChange hook", {
component: "lib",
operation: "update",
phase: "fetch-for-onChange",
key: JSON.stringify(key),
itemType: coordinate.kta[0],
errorType: error?.constructor?.name || typeof error,
errorMessage: error?.message,
note: "Update will continue without onChange hook",
coordinate: JSON.stringify(coordinate)
});
}
}
let itemToUpdate = item;
itemToUpdate = await runPreUpdateHook(key, itemToUpdate);
await validateUpdate(key, itemToUpdate);
const context = {
itemType: coordinate.kta[0],
operationType: "update",
operationName: "update",
params: { key, updates: itemToUpdate },
key
};
let updatedItem;
try {
updatedItem = await executeWithContext8(
() => toWrap.update(key, itemToUpdate),
context
);
} catch (updateError) {
throw new UpdateError({ key, item: itemToUpdate }, coordinate, { cause: updateError });
}
try {
updatedItem = await runPostUpdateHook(updatedItem);
} catch (hookError) {
throw new UpdateError({ key, item: itemToUpdate }, coordinate, { cause: hookError });
}
if (options?.hooks?.onChange && originalItem != null) {
try {
await options.hooks.onChange(originalItem, updatedItem);
} catch (error) {
throw new HookError(
"Error in onChange",
"update",
coordinate,
{ cause: error }
);
}
}
logger10.debug("Update operation completed successfully", { updatedItem });
return updatedItem;
} catch (error) {
if (error instanceof UpdateValidationError) {
throw error;
}
throw new Error(error.message, { cause: error });
}
};
return update;
async function runPreUpdateHook(key, itemToUpdate) {
if (options?.hooks?.preUpdate) {
try {
itemToUpdate = await options.hooks.preUpdate(key, itemToUpdate);
} catch (error) {
throw new HookError(
"Error in preUpdate",
"update",
coordinate,
{ cause: error }
);
}
} else {
}
return itemToUpdate;
}
async function runPostUpdateHook(updatedItem) {
if (options?.hooks?.postUpdate) {
try {
updatedItem = await options.hooks.postUpdate(updatedItem);
} catch (error) {
throw new HookError(
"Error in postUpdate",
"update",
coordinate,
{ cause: error }
);
}
} else {
}
return updatedItem;
}
async function validateUpdate(key, itemToUpdate) {
if (options?.validation) {
try {
if (options.validation.updateSchema) {
await validateSchema2(itemToUpdate, options.validation.updateSchema);
}
} catch (error) {
throw new UpdateValidationError(
{ key, item: itemToUpdate },
coordinate,
{ cause: error }
);
}
}
if (!options?.validators?.onUpdate) {
return;
}
try {
const isValid = await options.validators.onUpdate(key, itemToUpdate);
if (!isValid) {
throw new UpdateValidationError(
{ key, item: itemToUpdate },
coordinate,
{ cause: new Error("Invalid item") }
);
}
} catch (error) {
if (error instanceof UpdateValidationError) throw error;
throw new UpdateValidationError(
{ key, item: itemToUpdate },
coordinate,
{ cause: error }
);
}
}
};
// src/ops/upsert.ts
import {
executeWithContext as executeWithContext9
} from "@fjell/core";
var logger11 = logger_default.get("ops", "upsert");
var wrapUpsertOperation = (ops, coordinate, registry) => {
const upsert = async (key, itemProperties, locations, options) => {
logger11.debug("upsert", { key, itemProperties });
const context = {
itemType: coordinate.kta[0],
operationType: "upsert",
operationName: "upsert",
params: { key, item: itemProperties },
key
};
return executeWithContext9(
async () => {
let item = null;
try {
logger11.debug("Retrieving item by key", { key });
item = await ops.get(key);
} catch (error) {
const isNotFound = error instanceof NotFoundError || error?.name === "NotFoundError" || error?.errorInfo?.code === "NOT_FOUND";
if (isNotFound) {
logger11.debug("Item not found, creating new item", { key, errorType: error?.name, errorCode: error?.errorInfo?.code });
const createOptions3 = locations ? { locations } : { key };
item = await ops.create(itemProperties, createOptions3);
} else {
logger11.error("Unexpected error during upsert get operation", {
component: "lib",
operation: "upsert",
phase: "get-existing",
key: JSON.stringify(key),
itemType: coordinate.kta[0],
errorType: error?.constructor?.name || typeof error,
errorMessage: error?.message,
errorName: error?.name,
errorCode: error?.errorInfo?.code,
suggestion: "Check database connectivity, permissions, and key validity",
coordinate: JSON.stringify(coordinate)
});
throw error;
}
}
if (!item) {
logger11.error("Failed to retrieve or create item during upsert", {
component: "lib",
operation: "upsert",
key: JSON.stringify(key),
itemType: coordinate.kta[0],
suggestion: "This should not happen. Check create operation implementation and error handling.",
coordinate: JSON.stringify(coordinate)
});
throw new Error(`Failed to retrieve or create item for upsert with key: ${JSON.stringify(key)}`);
}
logger11.debug("Updating item", { key: item.key, itemProperties });
item = await ops.update(item.key, itemProperties, options);
logger11.debug("Item updated successfully", { item });
return item;
},
context
);
};
return upsert;
};
// src/ops/action.ts
import {
executeWithContext as executeWithContext10,
ValidationError as ValidationError4
} from "@fjell/core";
var logger12 = logger_default.get("library", "ops", "action");
var wrapActionOperation = (toWrap, options, coordinate) => {
const { actions } = options || {};
const action = async (key, actionKey, actionParams) => {
logger12.debug("Action operation started", { key, actionKey, actionParams });
if (!actions?.[actionKey]) {
const availableActions = actions ? Object.keys(actions) : [];
logger12.error("Action not found", {
component: "lib",
operation: "action",
requestedAction: actionKey,
availableActions,
key: JSON.stringify(key),
itemType: coordinate.kta[0],
suggestion: availableActions.length > 0 ? `Use one of the available actions: ${availableActions.join(", ")}` : "Define actions in your library configuration",
coordinate: JSON.stringify(coordinate)
});
throw new ValidationError4(
`Action "${actionKey}" not found`,
availableActions,
availableActions.length > 0 ? `Use one of the available actions: ${availableActions.join(", ")}` : "Define actions in your library configuration"
);
}
const context = {
itemType: coordinate.kta[0],
operationType: "action",
operationName: actionKey,
params: actionParams || {},
key
};
try {
return await executeWithContext10(
async () => {
const actionMethod = actions[actionKey];
const item = await toWrap.get(key);
if (!item) {
logger12.error("Item not found for action", {
component: "lib",
operation: "action",
action: actionKey,
key: JSON.stringify(key),
itemType: coordinate.kta[0],
suggestion: "Verify the item exists before calling actions. Check if the key is correct or if the item was deleted.",
coordinate: JSON.stringify(coordinate)
});
throw new Error(`Item not found for action "${actionKey}" with key: ${JSON.stringify(key)}`);
}
const result = await actionMethod(item, actionParams || {});
logger12.debug("Action operation completed", { actionKey, result });
return result;
},
context
);
} catch (error) {
const errorDetails = {
operation: "action",
actionKey,
coordinate: coordinate.kta,
key: JSON.stringify(key)
};
if (typeof error.message === "string") {
errorDetails.message = error.message;
}
if (typeof error.name === "string") {
errorDetails.name = error.name;
}
if (typeof error.code === "string" || typeof error.code === "number") {
errorDetails.code = error.code;
}
if (typeof error.stack === "string") {
errorDetails.stack = error.stack;
}
if (typeof error.constraint === "string") {
errorDetails.constraint = error.constraint;
}
if (typeof error.detail === "string") {
errorDetails.detail = error.detail;
}
if (error.errors && Array.isArray(error.errors)) {
errorDetails.validationErrors = error.errors.map((e) => ({
message: e.message,
type: e.type,
path: e.path
}));
}
logger12.error(`Action "${actionKey}" failed`, errorDetails);
throw error;
}
};
return action;
};
// src/ops/facet.ts
import {
executeWithContext as executeWithContext11,
ValidationError as ValidationError5
} from "@fjell/core";
var logger13 = logger_default.get("library", "ops", "facet");
var wrapFacetOperation = (toWrap, options, coordinate, registry) => {
const { facets } = options || {};
const facet = async (key, facetKey, facetParams) => {
logger13.debug("Facet operation started", { key, facetKey, facetParams });
if (!facets?.[facetKey]) {
const availableFacets = facets ? Object.keys(facets) : [];
throw new ValidationError5(
`Facet "${facetKey}" not found`,
availableFacets,
"Use one of the available facets"
);
}
const context = {
itemType: coordinate.kta[0],
operationType: "facet",
operationName: facetKey,
params: facetParams || {},
key
};
return executeWithContext11(
async () => {
const facetMethod = facets[facetKey];
logger13.debug("Getting item for facet", { key });
const item = await toWrap.get(key);
if (!item) {
throw new Error(`Item not found for key: ${JSON.stringify(key)}`);
}
const result = facetMethod(item, facetParams || {});
logger13.debug("Facet operation completed", { facetKey, result });
return result;
},
context
);
};
return facet;
};
// src/ops/allAction.ts
import {
executeWithContext as executeWithContext12,
ValidationError as ValidationError6
} from "@fjell/core";
var logger14 = logger_default.get("library", "ops", "allAction");
var wrapAllActionOperation = (toWrap, options, coordinate) => {
const { allActions } = options || {};
const allAction = async (allActionKey, allActionParams, locations) => {
const locs = locations ?? [];
logger14.debug("AllAction operation started", { allActionKey, allActionParams, locations: locs });
if (!allActions?.[allActionKey]) {
const availableActions = allActions ? Object.keys(allActions) : [];
throw new ValidationError6(
`AllAction "${allActionKey}" not found`,
availableActions,
"Use one of the available actions"
);
}
const context = {
itemType: coordinate.kta[0],
operationType: "allAction",
operationName: allActionKey,
params: allActionParams || {},
locations: locs
};
try {
return await executeWithContext12(
async () => {
const allActionMethod = allActions[allActionKey];
const result = await allActionMethod(allActionParams || {}, locs);
logger14.debug("AllAction operation completed", { allActionKey, result });
return result;
},
context
);
} catch (error) {
const errorDetails = {
operation: "allAction",
actionKey: allActionKey,
coordinate: coordinate.kta,
locations: locs
};
if (typeof error.message === "string") {
errorDetails.message = error.message;
}
if (typeof error.name === "string") {
errorDetails.name = error.name;
}
if (typeof error.code === "string" || typeof error.code === "number") {
errorDetails.code = error.code;
}
if (typeof error.stack === "string") {
errorDetails.stack = error.stack;
}
if (typeof error.constraint === "string") {
errorDetails.constraint = error.constraint;
}
if (typeof error.detail === "string") {
errorDetails.detail = error.detail;
}
if (error.errors && Array.isArray(error.errors)) {
errorDetails.validationErrors = error.errors.map((e) => ({
message: e.message,
type: e.type,
path: e.path
}));
}
logger14.error(`AllAction "${allActionKey}" failed`, errorDetails);
throw error;
}
};
return allAction;
};
// src/ops/allFacet.ts
import {
executeWithContext as executeWithContext13,
ValidationError as ValidationError7
} from "@fjell/core";
var logger15 = logger_default.get("library", "ops", "allFacet");
var wrapAllFacetOperation = (toWrap, options, coordinate, registry) => {
const { allFacets } = options || {};
const allFacet = async (allFacetKey, allFacetParams, locations) => {
const locs = locations ?? [];
logger15.debug("AllFacet operation started", { allFacetKey, allFacetParams, locations: locs });
if (!allFacets?.[allFacetKey]) {
const availableFacets = allFacets ? Object.keys(allFacets) : [];
throw new ValidationError7(
`AllFacet "${allFacetKey}" not found`,
availableFacets,
"Use one of the available facets"
);
}
const context = {
itemType: coordinate.kta[0],
operationType: "allFacet",
operationName: allFacetKey,
params: allFacetParams || {},
locations: locs
};
return executeWithContext13(
() => {
const allFacetMethod = allFacets[allFacetKey];
const result = allFacetMethod(allFacetParams || {}, locs);
logger15.debug("AllFacet operation completed", { allFacetKey, result });
return result;
},
context
);
};
return allFacet;
};
// src/Operations.ts
var logger16 = logger_default.get("Operations");
var wrapOperations = (toWrap, options, coordinate, registry) => {
logger16.default("\u{1F517} [LIB] Wrapping operations with hooks and validation", {
coordinate: coordinate.kta,
hasHooks: !!options.hooks,
hasValidators: !!options.validators,
toWrapType: toWrap.constructor?.name || "Unknown"
});
const operations = {};
logger16.default("\u{1F517} [LIB] Wrapping create operation");
operations.create = wrapCreateOperation(toWrap, options, coordinate, registry);
logger16.default("\u{1F517} [LIB] Wrapping update operation");
operations.update = wrapUpdateOperation(toWrap, options, coordinate, registry);
logger16.default("\u{1F517} [LIB] Wrapping remove operation");
operations.remove = wrapRemoveOperation(toWrap, options, coordinate, registry);
logger16.default("\u{1F517} [LIB] Wrapping other operations");
operations.all = wrapAllOperation(toWrap, options, coordinate, registry);
operations.one = wrapOneOperation(toWrap, options, coordinate, registry);
operations.get = wrapGetOperation(toWrap, options, coordinate, registry);
operations.find = wrapFindOperation(toWrap, options, coordinate, registry);
operations.findOne = wrapFindOneOperation(toWrap, options, coordinate, registry);
operations.upsert = wrapUpsertOperation(operations, coordinate, registry);
operations.action = wrapActionOperation(toWrap, options, coordinate);
operations.facet = wrapFacetOperation(toWrap, options, coordinate, registry);
operations.allAction = wrapAllActionOperation(toWrap, options, coordinate);
operations.allFacet = wrapAllFacetOperation(toWrap, options, coordinate, registry);
operations.finders = { ...toWrap.finders || {}, ...options.finders || {} };
operations.actions = { ...toWrap.actions || {}, ...options.actions || {} };
operations.facets = { ...toWrap.facets || {}, ...options.facets || {} };
operations.allActions = { ...toWrap.allActions || {}, ...options.allActions || {} };
operations.allFacets = { ...toWrap.allFacets || {}, ...options.allFacets || {} };
logger16.default("\u{1F517} [LIB] Operations wrapping completed", {
coordinate: coordinate.kta,
wrappedOperations: Object.keys(operations)
});
return operations;
};
var createReadOnlyOperations = (toWrap) => {
logger16.debug("createReadOnlyOperations", { toWrap });
const create = async (item, options) => {
logger16.warning("create", "Cannot Create in a ReadOnly Library, Returning Empty Item");
return {};
};
const update = async (key, item) => {
logger16.warning("update", "Cannot Update in a ReadOnly Library, Returning Empty Item");
return {};
};
const upsert = async (key, itemProperties, locations) => {
logger16.warning("upsert", "Cannot Upsert in a ReadOnly Library, Returning Empty Item");
return {};
};
const remove = async (key) => {
logger16.warning("remove", "Cannot Remove in a ReadOnly Library, Returning Empty Item");
return {};
};
return {
...toWrap,
create,
update,
upsert,
remove
};
};
// src/Options.ts
import deepmerge from "deepmerge";
var logger17 = logger_default.get("Options");
var createDefaultOptions = () => {
logger17.debug("createDefaultOptions");
function clearAggs(item) {
delete item.aggs;
return item;
}
return {
hooks: {
// TODO: "We need to figure out how to make this an array of hooks..."
preCreate: async (item, options) => {
const retItem = clearAggs(item);
return retItem;
},
// TODO: "We need to figure out how to make this an array of hooks..."
preUpdate: async (key, item) => {
const retItem = clearAggs(item);
return retItem;
}
}
};
};
var createOptions = (options) => {
const defaultOptions = createDefaultOptions();
return deepmerge(defaultOptions, options ?? {});
};
// src/Registry.ts
import {
createRegistry as createBaseRegistry
} from "@fjell/registry";
var logger18 = logger_default.get("LibRegistry");
var createRegistryFactory = () => {
return (type, registryHub) => {
if (type !== "lib") {
throw new Error(`LibRegistryFactory can only create 'lib' type registries, got: ${type}`);
}
logger18.debug("Creating lib registry", { type, registryHub });
const baseRegistry = createBaseRegistry(type, registryHub);
return baseRegistry;
};
};
var createRegistry = (registryHub) => {
const baseRegistry = createBaseRegistry("lib", registryHub);
return {
...baseRegistry
};
};
// src/processing/OperationContext.ts
import { AsyncLocalStorage } from "async_hooks";
var serializeKey = (key) => {
if (key && "pk" in key && "kt" in key && !("loc" in key)) {
return `${key.kt}:${key.pk}`;
} else if (key && "pk" in key && "kt" in key && "loc" in key && key.loc) {
const locStr = key.loc.map((l) => `${l.kt}:${l.lk}`).join(",");
return `${key.kt}:${key.pk}|${locStr}`;
}
throw new Error(`Unsupported key type: ${JSON.stringify(key)}`);
};
var createOperationContext = () => {
const inProgress = /* @__PURE__ */ new Set();
const cache = /* @__PURE__ */ new Map();
return {
inProgress,
cache,
markInProgress(key) {
const serialized = serializeKey(key);
logger_default.debug("Marking key as in progress", { key, serialized });
inProgress.add(serialized);
},
markComplete(key) {
const serialized = serializeKey(key);
logger_default.debug("Marking key as complete", { key, serialized });
inProgress.delete(serialized);
},
isInProgress(key) {
const serialized = serializeKey(key);
const result = inProgress.has(serialized);
logger_default.debug("Checking if key is in progress", { key, serialized, result });
return result;
},
getCached(key) {
const serialized = serializeKey(key);
const result = cache.get(serialized);
logger_default.debug("Getting cached item", { key, serialized, found: !!result });
return result;
},
setCached(key, item) {
const serialized = serializeKey(key);
logger_default.debug("Caching item", { key, serialized });
cache.set(serialized, item);
},
isCached(key) {
const serialized = serializeKey(key);
const result = cache.has(serialized);
logger_default.debug("Checking if key is cached", { key, serialized, result });
return result;
}
};
};
var createProcessingContext = createOperationContext;
var ContextManager = class {
asyncLocalStorage = new AsyncLocalStorage();
/**
* Get the current context if one is set
*/
getCurrentContext() {
const context = this.asyncLocalStorage.getStore();
if (context) {
logger_default.debug("Got current context from AsyncLocalStorage");
}
return context;
}
/**
* Execute a function with a specific context set as current
* The context will be available to all async operations within the function
*/
async withContext(context, fn) {
logger_default.debug("Running with context in AsyncLocalStorage");
return this.asyncLocalStorage.run(context, fn);
}
};
var contextManager = new ContextManager();
// src/processing/AggregationBuilder.ts
import { ikToLKA, isComKey as isComKey3 } from "@fjell/core";
var logger19 = logger_default.get("lib", "processing", "AggregationBuilder");
var buildAggregation = async (item, aggregationDefinition, registry, context) => {
logger19.debug("\u{1F50D} AggregationBuilder START", {
itemKeyType: item.key.kt,
itemKeyPk: item.key.pk,
itemKeyFull: JSON.stringify(item.key),
targetKta: aggregationDefinition.kta,
aggregationProperty: aggregationDefinition.property,
cardinality: aggregationDefinition.cardinality
});
const currentItemType = item.key.kt;
const targetKta = aggregationDefinition.kta;
const targetKtaSliced = targetKta.slice(1);
const isChildAggregation = targetKta.length > 1 && targetKtaSliced.includes(currentItemType);
logger19.debug("\u{1F50D} AggregationBuilder DETECTION", {
currentItemType,
targetKta,
targetKtaSliced,
includesCurrentItem: targetKtaSliced.includes(currentItemType),
isChildAggregation,
isComKeyResult: isComKey3(item.key)
});
let location;
if (isComKey3(item.key)) {
const comKey = item.key;
logger19.debug("\u{1F50D} AggregationBuilder COMKEY", {
comKeyKt: comKey.kt,
comKeyPk: comKey.pk,
comKeyLoc: JSON.stringify(comKey.loc),
comKeyLocLength: comKey.loc?.length || 0
});
if (isChildAggregation) {
const currentItemLocKey = {
kt: comKey.kt,
lk: comKey.pk
};
location = [currentItemLocKey, ...comKey.loc];
logger19.debug("\u{1F50D} AggregationBuilder CHILD AGGREGATION", {
currentItemLocKey,
parentLocs: JSON.stringify(comKey.loc),
constructedLocation: JSON.stringify(location),
locationLength: location.length
});
} else {
location = ikToLKA(comKey);
logger19.debug("\u{1F50D} AggregationBuilder SIBLING AGGREGATION", {
ikToLKAResult: JSON.stringify(location),
locationLength: location.length
});
}
} else {
location = ikToLKA(item.key);
logger19.debug("\u{1F50D} AggregationBuilder PRIMARY KEY", {
ikToLKAResult: JSON.stringify(location),
locationLength: location.length
});
}
logger19.debug("\u{1F50D} AggregationBuilder FINAL LOCATION", {
finalLocation: JSON.stringify(location),
finalLocationLength: location.length,
operationType: aggregationDefinition.cardinality,
targetLibraryKta: aggregationDefinition.kta
});
const libraryInstance = registry.get(aggregationDefinition.kta);
if (!libraryInstance) {
throw new Error(`Library instance not found for key type array: ${aggregationDefinition.kta.join(", ")}`);
}
const aggregationCacheKey = `${aggregationDefinition.kta.join(".")}_${aggregationDefinition.cardinality}_${serializeKey(item.key)}`;
if (context) {
if (context.cache.has(aggregationCacheKey)) {
const cachedResult = context.cache.get(aggregationCacheKey);
logger19.debug("Using cached aggregation result", {
aggregationCacheKey,
property: aggregationDefinition.property
});
item[aggregationDefinition.property] = cachedResult;
return item;
}
}
return contextManager.withContext(context || contextManager.getCurrentContext() || createOperationContext(), async () => {
if (aggregationDefinition.cardinality === "one") {
return libraryInstance.operations.one({}, location).then((result) => {
if (context) {
context.cache.set(aggregationCacheKey, result);
}
item[aggregationDefinition.property] = result;
return item;
});
} else {
return libraryInstance.operations.all({}, location).then((results) => {
if (context) {
context.cache.set(aggregationCacheKey, results);
}
item[aggregationDefinition.property] = results;
return item;
});
}
});
};
// src/processing/ReferenceBuilder.ts
var buildReference = async (item, referenceDefinition, registry, context) => {
throw new Error(
"buildReference() from @fjell/lib is deprecated and no longer functional. Use implementation-specific builders instead:\n- buildSequelizeReference from @fjell/lib-sequelize for SQL databases\n- buildFirestoreReference from @fjell/lib-firestore for Firestore"
);
};
// src/wrapImplementationOperations.ts
function wrapImplementationOperations(implOps, options) {
return {
// Spread the implementation operations
...implOps,
// Add stub implementations for extended operations
// These return "no-op" values since they're not implemented by default
facet: async () => null,
allFacet: async () => null,
action: async () => [{}, []],
allAction: async () => [[], []],
// Add metadata dictionaries from options
finders: { ...options.finders || {} },
actions: { ...options.actions || {} },
facets: { ...options.facets || {} },
allActions: { ...options.allActions || {} },
allFacets: { ...options.allFacets || {} }
};
}
// src/primary/index.ts
var primary_exports = {};
__export(primary_exports, {
createLibrary: () => createLibrary2,
wrapOperations: () => wrapOperations2
});
// src/primary/Library.ts
var logger20 = logger_default.get("primary", "Instance");
var createLibrary2 = (registry, coordinate, operations, options) => {
logger20.debug("createLibrary", { coordinate, operations, registry, options });
const library = createLibrary(registry, coordinate, operations, options);
if (!library) {
return library;
}
return {
...library,
operations
};
};
// src/primary/Operations.ts
var logger21 = logger_default.get("primary", "Operations");
var wrapOperations2 = (toWrap, options, coordinate, registry) => {
logger21.debug("wrapOperations", { toWrap, options, coordinate, registry });
const operations = wrapOperations(toWrap, options, coordinate, registry);
return operations;
};
// src/contained/index.ts
var contained_exports = {};
__export(contained_exports, {
createLibrary: () => createLibrary3,
createOptions: () => createOptions2,
wrapOperations: () => wrapOperations3
});
// src/contained/Library.ts
var createLibrary3 = (parent, registry, coordinate, operations, options) => {
const library = createLibrary(registry, coordinate, operations, options);
return {
...library,
parent
};
};
// src/contained/Operations.ts
var wrapOperations3 = (toWrap, options, coordinate, registry) => {
logger_default.debug("wrapOperations", { toWrap, options, coordinate, registry });
const operations = wrapOperations(toWrap, options, coordinate, registry);
return operations;
};
// src/contained/Options.ts
var createOptions2 = (libOptions) => {
const options = createOptions(libOptions);
return {
...options
};
};
export {
contained_exports as Contained,
CreateValidationError,
HookError,
InvalidKeyTypeError,
LibError,
LocationKeyOrderErr