Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .campaign/tgz/SHA256SUMS
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
abdbff87d78af3f605b465b1104c9a57fa859fbc14d8bb9ba396a49869269d99 antelopejs-api-6afe3e2.tgz
0a51c9c2da58c2586bfa5095b1da92a7b74130acf1e64820bfd5f98eaa4200f9 antelopejs-core-184eb90.tgz
8e017528574748687b246df07affa6cbcfbe931cc6086fcb5dd2c7a28c1f0d8e antelopejs-interface-api-015a93a.tgz
1a29379d519467fdd438ee411a93519956f1e024d4f457f0d447cac67af61099 antelopejs-interface-api-fcc62d2.tgz
fa0a3cfa243d0c78cd29d45839ae61bbbce5615a7c8d8247755b0cd91c8bedc6 antelopejs-interface-api-util-639f38b.tgz
30391718e65481815db5b5bb10e7eabb1feac13165005ee46e54b592f3676c69 antelopejs-interface-core-f7df541.tgz
ccdc833729f50825d3567047a22bf9c68f47a38ca3527117756fbd80f380fabd antelopejs-interface-database-724ff72.tgz
Expand Down
Binary file removed .campaign/tgz/antelopejs-interface-api-015a93a.tgz
Binary file not shown.
Binary file not shown.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ The package declares these `@antelopejs/*` peer dependencies:

| Package | Supported | Tested with |
| ------------------------------------------- | ----------------- | ------------------------------------------------------------------------- |
| `@antelopejs/interface-api` | `>=0.3.0 <1.0.0` | `file:.campaign/tgz/antelopejs-interface-api-015a93a.tgz` |
| `@antelopejs/interface-api` | `>=0.3.0 <1.0.0` | `file:.campaign/tgz/antelopejs-interface-api-fcc62d2.tgz` |
| `@antelopejs/interface-api-util` | `>=0.1.3 <1.0.0` | `file:.campaign/tgz/antelopejs-interface-api-util-639f38b.tgz` |
| `@antelopejs/interface-core` | `>=0.2.4 <1.0.0` | `file:.campaign/tgz/antelopejs-interface-core-f7df541.tgz` |
| `@antelopejs/interface-database` | `>=0.1.10 <1.0.0` | `file:.campaign/tgz/antelopejs-interface-database-724ff72.tgz` |
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
"devDependencies": {
"@antelopejs/api": "file:.campaign/tgz/antelopejs-api-6afe3e2.tgz",
"@antelopejs/core": "file:.campaign/tgz/antelopejs-core-184eb90.tgz",
"@antelopejs/interface-api": "file:.campaign/tgz/antelopejs-interface-api-015a93a.tgz",
"@antelopejs/interface-api": "file:.campaign/tgz/antelopejs-interface-api-fcc62d2.tgz",
"@antelopejs/interface-api-util": "file:.campaign/tgz/antelopejs-interface-api-util-639f38b.tgz",
"@antelopejs/interface-core": "file:.campaign/tgz/antelopejs-interface-core-f7df541.tgz",
"@antelopejs/interface-database": "file:.campaign/tgz/antelopejs-interface-database-724ff72.tgz",
Expand Down Expand Up @@ -109,7 +109,7 @@
"overrides": {
"@antelopejs/interface-database": "file:.campaign/tgz/antelopejs-interface-database-724ff72.tgz",
"@antelopejs/interface-database-decorators": "file:.campaign/tgz/antelopejs-interface-database-decorators-1234214.tgz",
"@antelopejs/interface-api": "file:.campaign/tgz/antelopejs-interface-api-015a93a.tgz",
"@antelopejs/interface-api": "file:.campaign/tgz/antelopejs-interface-api-fcc62d2.tgz",
"@antelopejs/interface-api-util": "file:.campaign/tgz/antelopejs-interface-api-util-639f38b.tgz",
"@antelopejs/api": "file:.campaign/tgz/antelopejs-api-6afe3e2.tgz",
"@antelopejs/interface-core": "file:.campaign/tgz/antelopejs-interface-core-f7df541.tgz",
Expand Down
28 changes: 14 additions & 14 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pnpm-workspace.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
overrides:
"@antelopejs/interface-database": "file:.campaign/tgz/antelopejs-interface-database-724ff72.tgz"
"@antelopejs/interface-database-decorators": "file:.campaign/tgz/antelopejs-interface-database-decorators-1234214.tgz"
"@antelopejs/interface-api": "file:.campaign/tgz/antelopejs-interface-api-015a93a.tgz"
"@antelopejs/interface-api": "file:.campaign/tgz/antelopejs-interface-api-fcc62d2.tgz"
"@antelopejs/interface-api-util": "file:.campaign/tgz/antelopejs-interface-api-util-639f38b.tgz"
"@antelopejs/api": "file:.campaign/tgz/antelopejs-api-6afe3e2.tgz"
"@antelopejs/interface-core": "file:.campaign/tgz/antelopejs-interface-core-f7df541.tgz"
Expand Down
3 changes: 2 additions & 1 deletion src/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import { isString } from "./internal/guards";
import * as joined from "./internal/joined";
import * as list from "./internal/list";
import * as properties from "./internal/properties";
import { fieldsOf } from "./internal/prototypes";
import { ReadProperties as readProperties } from "./internal/read-properties";
import * as tables from "./internal/tables";
import * as validation from "./internal/validation";
Expand Down Expand Up @@ -271,7 +272,7 @@ export namespace Query {
meta: DataAPIMeta,
): ModelInstance {
assert(meta.modelKey, 500, "Missing model key.");
const model: unknown = obj[meta.modelKey];
const model = fieldsOf(obj)[meta.modelKey];
assert(
tables.isModelInstance(model),
500,
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export function DataController<
? { endpoint: key, callback: val }
: { endpoint: key, ...val };
meta.addEndpoint(key, entry);
c.prototype[key] = entry.callback.func;
Object.assign(c.prototype, { [key]: entry.callback.func });
}
// SAFETY: for every own key of def, the loop above installs the function ExtractCallback<P[key]> names (def[key].func, or def[key].callback.func for an entry with options) on c.prototype, which covers the members of ExtractDefCallbacks<P>; c extends base and declares table itself.
return c as Class<ExtractDefCallbacks<P> & TableHolder<C>> & Base;
Expand Down
6 changes: 3 additions & 3 deletions src/internal/properties.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import type {
RowFields,
WritableAccessFields,
} from "../metadata";
import { prototypeOf } from "./prototypes";
import { fieldsOf, prototypeOf } from "./prototypes";

function writableInBody(
writable: WritableAccessFields,
Expand All @@ -20,7 +20,7 @@ function writableInBody(
}

function writeTargetDefaults(
instance: Parameters<typeof Query.WriteProperties>[0],
instance: RowFields,
meta: DataAPIMeta,
dbData: RowFields,
) {
Expand All @@ -47,7 +47,7 @@ export function WriteProperties(
const writable = existingDBData
? writableInBody(actionWritable, bodyData)
: actionWritable;
const instance = { ...obj };
const instance = fieldsOf({ ...obj });
const dbData: RowFields = existingDBData || {};
Object.setPrototypeOf(dbData, prototypeOf(meta.tableClass));
if (!existingDBData) {
Expand Down
17 changes: 17 additions & 0 deletions src/internal/prototypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,25 @@
import type { Class } from "@antelopejs/interface-core/decorators";

import type { DataControllerInstance } from "./controller-types";
import type { RowFields } from "./field-types";
import { isClass, isObject } from "./guards";

/**
* A controller instance read by field name: an object answers every string key,
* with `undefined` for a field it lacks, and inherited accessors run on the
* instance, because this is the instance itself.
*/
export function fieldsOf(instance: DataControllerInstance): RowFields {
if (!holdsFields(instance)) {
throw new TypeError("A data controller must be an object.");
}
return instance;
}

function holdsFields(instance: DataControllerInstance): instance is RowFields {
return isObject(instance);
}

/** The object the instances of a class inherit from. */
export function prototypeOf(target: Class): object {
const prototype: unknown = target.prototype;
Expand Down
4 changes: 2 additions & 2 deletions src/internal/read-properties.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type {
RowFields,
} from "../metadata";
import { isFunction } from "./guards";
import { prototypeOf } from "./prototypes";
import { fieldsOf, prototypeOf } from "./prototypes";

export async function ReadProperties(
obj: DataControllerInstance,
Expand All @@ -17,7 +17,7 @@ export async function ReadProperties(
) {
const readable =
meta.readable[action ?? "_default"] ?? meta.readable._default;
const instance = { ...obj };
const instance = fieldsOf({ ...obj });
const res: RowFields = {};
for (const [key, field] of readable.props) {
if (onlyList && !field.listable) {
Expand Down
7 changes: 4 additions & 3 deletions src/internal/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import type {
DataControllerInstance,
RowFields,
} from "../metadata";
import { fieldsOf } from "./prototypes";
import { isFieldValues, isObject } from "./guards";

type RequestBody = Validation.RequestBody;
Expand Down Expand Up @@ -52,7 +53,7 @@ export function Lock(
) {
for (const [modifier, field] of meta.modifierKeys.entries()) {
const keyed: KeyedModifier = modifier;
const key: unknown = obj[field];
const key = fieldsOf(obj)[field];
lock(data, keyed, written, key);
}
}
Expand All @@ -72,7 +73,7 @@ export function Unlock(
}
for (const [modifier, field] of meta.modifierKeys.entries()) {
const keyed: KeyedModifier = modifier;
const key: unknown = obj[field];
const key = fieldsOf(obj)[field];
unlock(dbData, keyed, undefined, key);
for (const [foreign, fieldData] of Object.entries(meta.fields)) {
const related = dbData[foreign];
Expand All @@ -92,7 +93,7 @@ export function UnlockRequest<T extends {}, K extends keyof T>(
const modifiers = Array.from(meta.modifierKeys.entries()).map(
([modifier, field]) => ({
modifier,
args: [obj[field]],
args: [fieldsOf(obj)[field]],
}),
);
return unlockrequest<T, K>(meta.tableClass, row, field, modifiers);
Expand Down
28 changes: 28 additions & 0 deletions src/tests/internal/controller-fields.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import assert from "node:assert";

import { fieldsOf } from "../../internal/prototypes";

class Holder {
own = "own value";

get inherited(): string {
return `read from ${this.own}`;
}
}

describe("fieldsOf", () => {
it("answers own fields and inherited accessors by name", () => {
const fields = fieldsOf(new Holder());

assert.strictEqual(fields.own, "own value");
assert.strictEqual(fields.inherited, "read from own value");
assert.strictEqual(fields.absent, undefined);
});

it("refuses a value that is not an object", () => {
assert.throws(
() => fieldsOf(() => undefined),
/A data controller must be an object/,
);
});
});
Loading