@atscript/moost-db 0.1.60 → 0.1.61

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -199,9 +199,6 @@ function isAsValueHelpControllerSubclass(ctor) {
199
199
  if (!asValueHelpCtor) return false;
200
200
  return asValueHelpCtor.prototype.isPrototypeOf(ctor.prototype);
201
201
  }
202
- function isAsDbReadableControllerInstance(value) {
203
- return !!asDbReadableCtor && value instanceof asDbReadableCtor;
204
- }
205
202
  //#endregion
206
203
  //#region src/actions/keys.ts
207
204
  /** Log-message prefix for warnings emitted from the actions subsystem. */
@@ -873,14 +870,12 @@ let AsDbReadableController = class AsDbReadableController extends AsReadableCont
873
870
  readable;
874
871
  _gates;
875
872
  _preferredIdSet;
876
- _compositeIdShapes;
877
873
  _overlayIsNoOp;
878
874
  constructor(readable, app) {
879
875
  super(readable.type, readable.tableName, app, readable.isView ? "view" : "table");
880
876
  this.readable = readable;
881
877
  this._gates = this._buildGates();
882
878
  this._preferredIdSet = new Set(readable.preferredId ?? []);
883
- this._compositeIdShapes = (readable.identifications ?? []).filter((id) => id.fields.length >= 2);
884
879
  const defaultOverlay = AsReadableController.prototype.applyMetaOverlay;
885
880
  this._overlayIsNoOp = this.applyMetaOverlay === defaultOverlay;
886
881
  }
@@ -1104,12 +1099,9 @@ let AsDbReadableController = class AsDbReadableController extends AsReadableCont
1104
1099
  });
1105
1100
  return result;
1106
1101
  }
1107
- /**
1108
- * Extracts a composite identifier object from query params.
1109
- * Tries composite primary key first, then compound unique indexes.
1110
- */
1111
- extractCompositeId(query) {
1112
- for (const id of this._compositeIdShapes) {
1102
+ /** Pick the first identification (PK or unique index) whose fields are all present in the query. */
1103
+ extractIdShape(query) {
1104
+ for (const id of this.readable.identifications) {
1113
1105
  const idObj = {};
1114
1106
  let allPresent = true;
1115
1107
  for (const field of id.fields) {
@@ -1121,7 +1113,7 @@ let AsDbReadableController = class AsDbReadableController extends AsReadableCont
1121
1113
  }
1122
1114
  if (allPresent) return idObj;
1123
1115
  }
1124
- return new _moostjs_event_http.HttpError(400, "Query params do not match any composite primary key or compound unique index");
1116
+ return new _moostjs_event_http.HttpError(400, "Query params do not match any primary key or unique index");
1125
1117
  }
1126
1118
  /**
1127
1119
  * **GET /query** — returns an array of records or a count.
@@ -1237,7 +1229,7 @@ let AsDbReadableController = class AsDbReadableController extends AsReadableCont
1237
1229
  * (composite primary key or compound unique index).
1238
1230
  */
1239
1231
  async getOneComposite(query, url) {
1240
- const idObj = this.extractCompositeId(query);
1232
+ const idObj = this.extractIdShape(query);
1241
1233
  if (idObj instanceof _moostjs_event_http.HttpError) return idObj;
1242
1234
  const parsed = this.parseQueryString(url);
1243
1235
  this._coerceActionsControl(parsed.controls);
@@ -1437,7 +1429,7 @@ let AsDbController = class AsDbController extends AsDbReadableController {
1437
1429
  * (composite primary key or compound unique index).
1438
1430
  */
1439
1431
  async removeComposite(query) {
1440
- const idObj = this.extractCompositeId(query);
1432
+ const idObj = this.extractIdShape(query);
1441
1433
  if (idObj instanceof _moostjs_event_http.HttpError) return idObj;
1442
1434
  const resolvedId = await this.onRemove(idObj);
1443
1435
  if (resolvedId === void 0) return new _moostjs_event_http.HttpError(500, "Not deleted");
@@ -1860,7 +1852,7 @@ const SOURCE_CACHE = /* @__PURE__ */ new WeakMap();
1860
1852
  function getSourceCache(source) {
1861
1853
  let cache = SOURCE_CACHE.get(source);
1862
1854
  if (cache) return cache;
1863
- const identifications = source.getIdentifications();
1855
+ const identifications = source.identifications;
1864
1856
  const byKeySig = /* @__PURE__ */ new Map();
1865
1857
  for (const ident of identifications) byKeySig.set(fieldsSig(ident.fields), ident);
1866
1858
  const fieldByName = /* @__PURE__ */ new Map();
@@ -1879,7 +1871,7 @@ function fieldsSig(fields) {
1879
1871
  function isIdValidationSource(value) {
1880
1872
  if (!value || typeof value !== "object") return false;
1881
1873
  const v = value;
1882
- return typeof v.getIdentifications === "function" && Array.isArray(v.fieldDescriptors);
1874
+ return Array.isArray(v.identifications) && Array.isArray(v.fieldDescriptors);
1883
1875
  }
1884
1876
  function validateSingleId(body, source, path = "") {
1885
1877
  const errors = collectIdErrors(body, source, path);
@@ -1947,15 +1939,27 @@ function isPlainObject(value) {
1947
1939
  //#endregion
1948
1940
  //#region src/actions/id-cache.ts
1949
1941
  const boundTableKey = (0, _wooksjs_event_core.key)("atscript_db_action_bound_table");
1950
- function getActionTable(ctx) {
1951
- const fromSlot = ctx.has(boundTableKey) ? ctx.get(boundTableKey) : void 0;
1952
- if (fromSlot) return fromSlot;
1942
+ function controllerTable(ctx) {
1953
1943
  const ctrl = (0, moost.useControllerContext)(ctx).getController();
1954
1944
  return ctrl?.readable ?? ctrl?.table ?? null;
1955
1945
  }
1946
+ function getActionTable(ctx) {
1947
+ return (ctx.has(boundTableKey) ? ctx.get(boundTableKey) : void 0) ?? controllerTable(ctx);
1948
+ }
1949
+ const warnedTags = /* @__PURE__ */ new Set();
1956
1950
  function noTableError(ctx) {
1957
1951
  const actionName = readCurrentActionMeta(ctx)?.name;
1958
- return new _moostjs_event_http.HttpError(500, `${WARN_PREFIX} ${actionName ? `"${actionName}"` : "<unknown>"}: controller has no readable/table property and the action declares no opts.table. Either expose readable/table on the controller, extend AsDbReadableController, or pass opts.table on @DbAction.`);
1952
+ const tag = actionName ? `"${actionName}"` : "<unknown>";
1953
+ if (!warnedTags.has(tag)) {
1954
+ warnedTags.add(tag);
1955
+ console.warn(`${WARN_PREFIX} ${tag}: controller has no readable/table property and the action declares no opts.table. Either expose readable/table on the controller, extend AsDbReadableController, or pass opts.table on @DbAction.`);
1956
+ }
1957
+ return new _moostjs_event_http.HttpError(500, {
1958
+ statusCode: 500,
1959
+ error: "Internal Server Error",
1960
+ message: "Internal server error",
1961
+ code: "ACTION_TABLE_NOT_BOUND"
1962
+ });
1959
1963
  }
1960
1964
  async function resolveValidatedId(ctx, validate) {
1961
1965
  const table = getActionTable(ctx);
@@ -2062,15 +2066,11 @@ const useDbActionRows = (0, _wooksjs_event_core.defineWook)((ctx) => ({ load: ()
2062
2066
  //#endregion
2063
2067
  //#region src/actions/gate-interceptor.ts
2064
2068
  const GATE_PRIORITY = moost.TInterceptorPriority.AFTER_GUARD;
2065
- function injectBoundTable(table) {
2069
+ function injectBoundTable(fallback) {
2066
2070
  const ctx = (0, _wooksjs_event_core.current)();
2067
2071
  if (ctx.has(boundTableKey)) return;
2068
- const controller = (0, moost.useControllerContext)(ctx).getController();
2069
- if (isAsDbReadableControllerInstance(controller)) {
2070
- ctx.set(boundTableKey, controller.readable);
2071
- return;
2072
- }
2073
- if (table != null) ctx.set(boundTableKey, table);
2072
+ const t = controllerTable(ctx) ?? fallback;
2073
+ if (t != null) ctx.set(boundTableKey, t);
2074
2074
  }
2075
2075
  function buildGateInterceptor(opts) {
2076
2076
  const { action, level, disabled, onDisabledRows, table } = opts;
package/dist/index.d.cts CHANGED
@@ -155,7 +155,6 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
155
155
  protected readable: AtscriptDbReadable<T>;
156
156
  private readonly _gates;
157
157
  private readonly _preferredIdSet;
158
- private readonly _compositeIdShapes;
159
158
  private readonly _overlayIsNoOp;
160
159
  constructor(readable: AtscriptDbReadable<T>, app: Moost);
161
160
  private _buildGates;
@@ -200,11 +199,8 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
200
199
  * family (count vs no-count).
201
200
  */
202
201
  private _runReadWithActions;
203
- /**
204
- * Extracts a composite identifier object from query params.
205
- * Tries composite primary key first, then compound unique indexes.
206
- */
207
- protected extractCompositeId(query: Record<string, string>): Record<string, unknown> | HttpError;
202
+ /** Pick the first identification (PK or unique index) whose fields are all present in the query. */
203
+ protected extractIdShape(query: Record<string, string>): Record<string, unknown> | HttpError;
208
204
  /**
209
205
  * **GET /query** — returns an array of records or a count.
210
206
  */
@@ -734,9 +730,10 @@ interface TDbActionEnvelope {
734
730
  declare function discoverActions(controllerCtor: Function, app: Moost, logger: TConsoleBase): TDbActionEnvelope[];
735
731
  //#endregion
736
732
  //#region src/actions/id-validation.d.ts
733
+ /** Duck-typed shape; matches `AtscriptDbReadable`'s public surface. */
737
734
  interface IdValidationSource {
738
- getIdentifications(): readonly TIdentification[];
739
- fieldDescriptors: readonly TDbFieldMeta[];
735
+ readonly identifications: readonly TIdentification[];
736
+ readonly fieldDescriptors: readonly TDbFieldMeta[];
740
737
  }
741
738
  //#endregion
742
739
  //#region src/actions/id-cache.d.ts
package/dist/index.d.mts CHANGED
@@ -155,7 +155,6 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
155
155
  protected readable: AtscriptDbReadable<T>;
156
156
  private readonly _gates;
157
157
  private readonly _preferredIdSet;
158
- private readonly _compositeIdShapes;
159
158
  private readonly _overlayIsNoOp;
160
159
  constructor(readable: AtscriptDbReadable<T>, app: Moost);
161
160
  private _buildGates;
@@ -200,11 +199,8 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
200
199
  * family (count vs no-count).
201
200
  */
202
201
  private _runReadWithActions;
203
- /**
204
- * Extracts a composite identifier object from query params.
205
- * Tries composite primary key first, then compound unique indexes.
206
- */
207
- protected extractCompositeId(query: Record<string, string>): Record<string, unknown> | HttpError;
202
+ /** Pick the first identification (PK or unique index) whose fields are all present in the query. */
203
+ protected extractIdShape(query: Record<string, string>): Record<string, unknown> | HttpError;
208
204
  /**
209
205
  * **GET /query** — returns an array of records or a count.
210
206
  */
@@ -734,9 +730,10 @@ interface TDbActionEnvelope {
734
730
  declare function discoverActions(controllerCtor: Function, app: Moost, logger: TConsoleBase): TDbActionEnvelope[];
735
731
  //#endregion
736
732
  //#region src/actions/id-validation.d.ts
733
+ /** Duck-typed shape; matches `AtscriptDbReadable`'s public surface. */
737
734
  interface IdValidationSource {
738
- getIdentifications(): readonly TIdentification[];
739
- fieldDescriptors: readonly TDbFieldMeta[];
735
+ readonly identifications: readonly TIdentification[];
736
+ readonly fieldDescriptors: readonly TDbFieldMeta[];
740
737
  }
741
738
  //#endregion
742
739
  //#region src/actions/id-cache.d.ts
package/dist/index.mjs CHANGED
@@ -198,9 +198,6 @@ function isAsValueHelpControllerSubclass(ctor) {
198
198
  if (!asValueHelpCtor) return false;
199
199
  return asValueHelpCtor.prototype.isPrototypeOf(ctor.prototype);
200
200
  }
201
- function isAsDbReadableControllerInstance(value) {
202
- return !!asDbReadableCtor && value instanceof asDbReadableCtor;
203
- }
204
201
  //#endregion
205
202
  //#region src/actions/keys.ts
206
203
  /** Log-message prefix for warnings emitted from the actions subsystem. */
@@ -872,14 +869,12 @@ let AsDbReadableController = class AsDbReadableController extends AsReadableCont
872
869
  readable;
873
870
  _gates;
874
871
  _preferredIdSet;
875
- _compositeIdShapes;
876
872
  _overlayIsNoOp;
877
873
  constructor(readable, app) {
878
874
  super(readable.type, readable.tableName, app, readable.isView ? "view" : "table");
879
875
  this.readable = readable;
880
876
  this._gates = this._buildGates();
881
877
  this._preferredIdSet = new Set(readable.preferredId ?? []);
882
- this._compositeIdShapes = (readable.identifications ?? []).filter((id) => id.fields.length >= 2);
883
878
  const defaultOverlay = AsReadableController.prototype.applyMetaOverlay;
884
879
  this._overlayIsNoOp = this.applyMetaOverlay === defaultOverlay;
885
880
  }
@@ -1103,12 +1098,9 @@ let AsDbReadableController = class AsDbReadableController extends AsReadableCont
1103
1098
  });
1104
1099
  return result;
1105
1100
  }
1106
- /**
1107
- * Extracts a composite identifier object from query params.
1108
- * Tries composite primary key first, then compound unique indexes.
1109
- */
1110
- extractCompositeId(query) {
1111
- for (const id of this._compositeIdShapes) {
1101
+ /** Pick the first identification (PK or unique index) whose fields are all present in the query. */
1102
+ extractIdShape(query) {
1103
+ for (const id of this.readable.identifications) {
1112
1104
  const idObj = {};
1113
1105
  let allPresent = true;
1114
1106
  for (const field of id.fields) {
@@ -1120,7 +1112,7 @@ let AsDbReadableController = class AsDbReadableController extends AsReadableCont
1120
1112
  }
1121
1113
  if (allPresent) return idObj;
1122
1114
  }
1123
- return new HttpError(400, "Query params do not match any composite primary key or compound unique index");
1115
+ return new HttpError(400, "Query params do not match any primary key or unique index");
1124
1116
  }
1125
1117
  /**
1126
1118
  * **GET /query** — returns an array of records or a count.
@@ -1236,7 +1228,7 @@ let AsDbReadableController = class AsDbReadableController extends AsReadableCont
1236
1228
  * (composite primary key or compound unique index).
1237
1229
  */
1238
1230
  async getOneComposite(query, url) {
1239
- const idObj = this.extractCompositeId(query);
1231
+ const idObj = this.extractIdShape(query);
1240
1232
  if (idObj instanceof HttpError) return idObj;
1241
1233
  const parsed = this.parseQueryString(url);
1242
1234
  this._coerceActionsControl(parsed.controls);
@@ -1436,7 +1428,7 @@ let AsDbController = class AsDbController extends AsDbReadableController {
1436
1428
  * (composite primary key or compound unique index).
1437
1429
  */
1438
1430
  async removeComposite(query) {
1439
- const idObj = this.extractCompositeId(query);
1431
+ const idObj = this.extractIdShape(query);
1440
1432
  if (idObj instanceof HttpError) return idObj;
1441
1433
  const resolvedId = await this.onRemove(idObj);
1442
1434
  if (resolvedId === void 0) return new HttpError(500, "Not deleted");
@@ -1859,7 +1851,7 @@ const SOURCE_CACHE = /* @__PURE__ */ new WeakMap();
1859
1851
  function getSourceCache(source) {
1860
1852
  let cache = SOURCE_CACHE.get(source);
1861
1853
  if (cache) return cache;
1862
- const identifications = source.getIdentifications();
1854
+ const identifications = source.identifications;
1863
1855
  const byKeySig = /* @__PURE__ */ new Map();
1864
1856
  for (const ident of identifications) byKeySig.set(fieldsSig(ident.fields), ident);
1865
1857
  const fieldByName = /* @__PURE__ */ new Map();
@@ -1878,7 +1870,7 @@ function fieldsSig(fields) {
1878
1870
  function isIdValidationSource(value) {
1879
1871
  if (!value || typeof value !== "object") return false;
1880
1872
  const v = value;
1881
- return typeof v.getIdentifications === "function" && Array.isArray(v.fieldDescriptors);
1873
+ return Array.isArray(v.identifications) && Array.isArray(v.fieldDescriptors);
1882
1874
  }
1883
1875
  function validateSingleId(body, source, path = "") {
1884
1876
  const errors = collectIdErrors(body, source, path);
@@ -1946,15 +1938,27 @@ function isPlainObject(value) {
1946
1938
  //#endregion
1947
1939
  //#region src/actions/id-cache.ts
1948
1940
  const boundTableKey = key("atscript_db_action_bound_table");
1949
- function getActionTable(ctx) {
1950
- const fromSlot = ctx.has(boundTableKey) ? ctx.get(boundTableKey) : void 0;
1951
- if (fromSlot) return fromSlot;
1941
+ function controllerTable(ctx) {
1952
1942
  const ctrl = useControllerContext(ctx).getController();
1953
1943
  return ctrl?.readable ?? ctrl?.table ?? null;
1954
1944
  }
1945
+ function getActionTable(ctx) {
1946
+ return (ctx.has(boundTableKey) ? ctx.get(boundTableKey) : void 0) ?? controllerTable(ctx);
1947
+ }
1948
+ const warnedTags = /* @__PURE__ */ new Set();
1955
1949
  function noTableError(ctx) {
1956
1950
  const actionName = readCurrentActionMeta(ctx)?.name;
1957
- return new HttpError(500, `${WARN_PREFIX} ${actionName ? `"${actionName}"` : "<unknown>"}: controller has no readable/table property and the action declares no opts.table. Either expose readable/table on the controller, extend AsDbReadableController, or pass opts.table on @DbAction.`);
1951
+ const tag = actionName ? `"${actionName}"` : "<unknown>";
1952
+ if (!warnedTags.has(tag)) {
1953
+ warnedTags.add(tag);
1954
+ console.warn(`${WARN_PREFIX} ${tag}: controller has no readable/table property and the action declares no opts.table. Either expose readable/table on the controller, extend AsDbReadableController, or pass opts.table on @DbAction.`);
1955
+ }
1956
+ return new HttpError(500, {
1957
+ statusCode: 500,
1958
+ error: "Internal Server Error",
1959
+ message: "Internal server error",
1960
+ code: "ACTION_TABLE_NOT_BOUND"
1961
+ });
1958
1962
  }
1959
1963
  async function resolveValidatedId(ctx, validate) {
1960
1964
  const table = getActionTable(ctx);
@@ -2061,15 +2065,11 @@ const useDbActionRows = defineWook((ctx) => ({ load: () => ctx.get(dbActionRowsS
2061
2065
  //#endregion
2062
2066
  //#region src/actions/gate-interceptor.ts
2063
2067
  const GATE_PRIORITY = TInterceptorPriority.AFTER_GUARD;
2064
- function injectBoundTable(table) {
2068
+ function injectBoundTable(fallback) {
2065
2069
  const ctx = current();
2066
2070
  if (ctx.has(boundTableKey)) return;
2067
- const controller = useControllerContext(ctx).getController();
2068
- if (isAsDbReadableControllerInstance(controller)) {
2069
- ctx.set(boundTableKey, controller.readable);
2070
- return;
2071
- }
2072
- if (table != null) ctx.set(boundTableKey, table);
2071
+ const t = controllerTable(ctx) ?? fallback;
2072
+ if (t != null) ctx.set(boundTableKey, t);
2073
2073
  }
2074
2074
  function buildGateInterceptor(opts) {
2075
2075
  const { action, level, disabled, onDisabledRows, table } = opts;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atscript/moost-db",
3
- "version": "0.1.60",
3
+ "version": "0.1.61",
4
4
  "description": "Generic database controller for Moost with Atscript.",
5
5
  "keywords": [
6
6
  "annotations",
@@ -58,7 +58,7 @@
58
58
  "@wooksjs/event-core": "^0.7.10",
59
59
  "@wooksjs/http-body": "^0.7.10",
60
60
  "moost": "^0.6.8",
61
- "@atscript/db": "^0.1.60"
61
+ "@atscript/db": "^0.1.61"
62
62
  },
63
63
  "scripts": {
64
64
  "postinstall": "asc -f dts",