@akanjs/cli 3.0.0-alpha.111 → 3.0.0-alpha.113
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/.build-stamp +1 -1
- package/guidelines/conventions/conventions.instruction.md +9 -2
- package/guidelines/queryRule/queryRule.instruction.md +6 -1
- package/guidelines/workspaceOnboarding/workspaceOnboarding.instruction.md +6 -2
- package/index.js +1 -1
- package/package.json +2 -2
- package/{subspace.command-35a3t6vv.js → subspace.command-fwjfcts7.js} +4 -2
package/.build-stamp
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
b043c91f5476cad8f328dc840b74670a9e53078810ca74b011b5b38b05f79242
|
|
@@ -403,7 +403,9 @@ framework has no hard delete for a model table, and `delete` is deliberately lef
|
|
|
403
403
|
The facade spells `Many`/`One` out on its writes (`updateOne` / `updateMany` / `removeOne` / `removeMany`); only the
|
|
404
404
|
count was shortened to `count(query)`, with `countDocuments` kept as `@deprecated`. `updateById(id, update)` and
|
|
405
405
|
`removeById(id)` are those same query-level writes narrowed to one id, **not** the document path: they fire no hooks,
|
|
406
|
-
so a model whose removal cascades or carries a `_postRemove` still goes through `remove<Model>(id)`.
|
|
406
|
+
so a model whose removal cascades or carries a `_postRemove` still goes through `remove<Model>(id)`. The
|
|
407
|
+
write-through pair is `pickAndWrite(id, data)` / `pickOneAndWrite(query, data)` — pick the document, write into it
|
|
408
|
+
and save it, so the save hooks do run; reach for those when the write has to be seen by them.
|
|
407
409
|
|
|
408
410
|
**`<model>.service.ts`** — keep methods to a few lines: load → chain → `return await ….save()`. Write `return await`
|
|
409
411
|
explicitly in tail position; do not "optimize" it away. Side effects belong in `override async _preUpdate` /
|
|
@@ -515,7 +517,12 @@ Full contract — filter-arg `ref` pickers, `getQueryMeta` summary counters, `la
|
|
|
515
517
|
`[object Object]`. It is an admin API: `root:` is always `Admin`.
|
|
516
518
|
- A slice's `exec` returns a `QueryOf` (an opaque query descriptor); you **cannot** chain `.sort()`/`.limit()` on
|
|
517
519
|
it. Apply ordering/paging via the store `init` fetch option instead: `initX(..., { sort, page, limit })`. For a
|
|
518
|
-
chainable builder use the model facade's `
|
|
520
|
+
chainable builder use the model facade's `find`/`findOne`.
|
|
521
|
+
- **A projection is bare on the facade and nested under `select` on a filter accessor.** `pickById(id, { secret:
|
|
522
|
+
true })` and `find`/`findOne`/`findById`/`pickOne` take the projection as their second argument; a generated
|
|
523
|
+
`findBy<Filter>(...args, { select: { secret: true } })` takes it inside the option object. The shapes do not swap
|
|
524
|
+
— `{ select: … }` handed to the facade projects a field named `select`, which no model has, and the read comes
|
|
525
|
+
back empty with no error. This is the only way to read a `field.secret(...)` value.
|
|
519
526
|
- **A slice list is either one window or an accumulated one.** `setPageOf<Model>` swaps the window;
|
|
520
527
|
`loadMoreOf<Model>()` appends the rows after the ones loaded and takes **no page number** — it skips by
|
|
521
528
|
`<model>List.length`, so the offset cannot drift from what is on screen when a live insertion moves the server's
|
|
@@ -43,7 +43,12 @@ with it. `conventions` carries the invariants — this is the full contract behi
|
|
|
43
43
|
the server just returned, so it survives `{ insight: false }` — which makes no count query at all — and cannot
|
|
44
44
|
drift from the count a live event moved. `loadMoreOf<Model>()` is a no-op once it is false, and `Load.Units`
|
|
45
45
|
draws no infinite-scroll sentinel.
|
|
46
|
-
- Generated list accessors like `listBy(...)` return `Promise<Doc[]>`. For a chainable builder (`.sort().skip().limit().select()`) use the model facade's `
|
|
46
|
+
- Generated list accessors like `listBy(...)` return `Promise<Doc[]>`. For a chainable builder (`.sort().skip().limit().select()`) use the model facade's `find`/`findOne` (`FindManyChain`, `pkgs/akanjs/document/into.ts`).
|
|
47
|
+
- **A projection is bare on the facade and nested under `select` on a filter accessor.** `pickById(id, { secretField:
|
|
48
|
+
true })` / `find(query, { secretField: true })` take the projection as their second argument; a generated
|
|
49
|
+
`findBy<Filter>(...args, { select: { secretField: true } })` takes it inside the option object. The shapes do not
|
|
50
|
+
swap: `{ select: … }` handed to the facade projects a field named `select`, which no model has, and the read comes
|
|
51
|
+
back empty with no error. This is the only way to read a `field.secret(...)` value, which is otherwise stripped.
|
|
47
52
|
- **Hydrated vs raw:** server queries return hydrated `cnst.<Model>` instances (with `set`/`save`/`refresh`); client fetch results are raw `GetStateObject` plain data (functions stripped, `pkgs/akanjs/base/types.ts`).
|
|
48
53
|
- Every filter generates fourteen methods: `list` · `listIds` · `find` · `findId` · `pick` · `pickId` · `exists` ·
|
|
49
54
|
`count` · `insight` · `query` · **`remove`** · **`removeOne`** · **`update`** · **`updateOne`**. The last four are
|
|
@@ -233,8 +233,12 @@ query-level writes are in the convention set above; these are the shapes that bu
|
|
|
233
233
|
already exist. A collision can build green and fail only at runtime — pick a distinct verb.
|
|
234
234
|
- **Numbers are `Int` or `Float`, never `Number`.** `field(Number)` / `.body("x", Number)` fail to typecheck.
|
|
235
235
|
- **Array fields use `field([T])`** — `tags: field([String])`, `images: field([File])`.
|
|
236
|
-
- **Reading a secret field needs an explicit select
|
|
237
|
-
by default
|
|
236
|
+
- **Reading a secret field needs an explicit select, and the two call sites spell it differently.**
|
|
237
|
+
`field(...).secret()` values are stripped from query results by default. The model facade takes the projection
|
|
238
|
+
**bare** — `this.userModel.pickById(id, { passwordHash: true })`, likewise `pickOne` / `find` / `findOne` /
|
|
239
|
+
`findById`. A generated filter accessor takes it **inside the option object** —
|
|
240
|
+
`this.userModel.findByAccountId(accountId, statuses, { select: { passwordHash: true } })`. Handing `{ select: … }`
|
|
241
|
+
to the facade selects a field named `select`, which no model has: the value arrives empty, with no error.
|
|
238
242
|
- **`cascade` names a direction, and the wrong one is a data loss** — `removeRef` on the relation the owner holds,
|
|
239
243
|
`removeWith` on the child's reference to its owner. A query-level removal fires no hooks and therefore no cascade.
|
|
240
244
|
- **`q.search()` is a filter node, not a slice requirement.** `bySearch: filter().arg("text", String).query((text,
|
package/index.js
CHANGED
|
@@ -24,7 +24,7 @@ var commandModules = {
|
|
|
24
24
|
page: async () => (await import("./page.command-vs9s3zrh.js")).PageCommand,
|
|
25
25
|
context: async () => (await import("./context.command-ez8a0zyz.js")).ContextCommand,
|
|
26
26
|
cloud: async () => (await import("./cloud.command-gxqp8xdv.js")).CloudCommand,
|
|
27
|
-
subspace: async () => (await import("./subspace.command-
|
|
27
|
+
subspace: async () => (await import("./subspace.command-fwjfcts7.js")).SubspaceCommand,
|
|
28
28
|
guideline: async () => (await import("./guideline.command-xmqwx8zm.js")).GuidelineCommand,
|
|
29
29
|
scalar: async () => (await import("./scalar.command-6k1vhe9s.js")).ScalarCommand,
|
|
30
30
|
primitive: async () => (await import("./primitive.command-cbxmqbtc.js")).PrimitiveCommand,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akanjs/cli",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.113",
|
|
4
4
|
"sourceType": "module",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"@langchain/openai": "^1.4.6",
|
|
34
34
|
"@tailwindcss/node": "^4.3.0",
|
|
35
35
|
"@trapezedev/project": "^7.1.4",
|
|
36
|
-
"akanjs": "3.0.0-alpha.
|
|
36
|
+
"akanjs": "3.0.0-alpha.113",
|
|
37
37
|
"chalk": "^5.6.2",
|
|
38
38
|
"commander": "^14.0.3",
|
|
39
39
|
"dayjs": "^1.11.20",
|
|
@@ -147,7 +147,7 @@ class Subspace {
|
|
|
147
147
|
}
|
|
148
148
|
const clone = new Executor(`subspace-${this.name}`, this.#clonePath);
|
|
149
149
|
await clone.spawn("git", ["fetch", "--quiet", "origin", branch]);
|
|
150
|
-
await clone.spawn("git", ["checkout", "--quiet", "-B", branch, `origin/${branch}`]);
|
|
150
|
+
await clone.spawn("git", ["checkout", "--quiet", "-f", "-B", branch, `origin/${branch}`]);
|
|
151
151
|
await clone.spawn("git", ["clean", "-qfd"]);
|
|
152
152
|
return clone;
|
|
153
153
|
}
|
|
@@ -324,7 +324,9 @@ class Subspace {
|
|
|
324
324
|
"--",
|
|
325
325
|
`libs/${lib}`
|
|
326
326
|
]);
|
|
327
|
-
const
|
|
327
|
+
const candidates = listed.split("\x00").filter((file) => !!file && !this.#isSubspaceOwned(file)).sort();
|
|
328
|
+
const present = await Promise.all(candidates.map(async (file) => await FileSys.entryExists(path.join(this.#clonePath, file))));
|
|
329
|
+
const files = candidates.filter((_, index) => present[index]);
|
|
328
330
|
const hasher = new Bun.CryptoHasher("sha256");
|
|
329
331
|
for (const file of files) {
|
|
330
332
|
const content = await FileSys.readText(path.join(this.#clonePath, file));
|