@akanjs/cli 3.0.0-alpha.1 → 3.0.0-alpha.3
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
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
5f4cb4962f5027c269281e372337f2349defcfca97ac7c862b17e452e9749c5d
|
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.3",
|
|
4
4
|
"sourceType": "module",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"@langchain/openai": "^1.4.6",
|
|
35
35
|
"@tailwindcss/node": "^4.3.0",
|
|
36
36
|
"@trapezedev/project": "^7.1.4",
|
|
37
|
-
"akanjs": "3.0.0-alpha.
|
|
37
|
+
"akanjs": "3.0.0-alpha.3",
|
|
38
38
|
"chalk": "^5.6.2",
|
|
39
39
|
"commander": "^14.0.3",
|
|
40
40
|
"dayjs": "^1.11.20",
|
package/templates/lib/db.ts
CHANGED
|
@@ -16,7 +16,9 @@ ${databaseModules.map((module) => `import * as ${module}Db from "./${module}/${m
|
|
|
16
16
|
${libs.map((lib) => `export { db as ${lib} } from "@libs/${lib}/server";`).join("\n")}
|
|
17
17
|
|
|
18
18
|
${databaseModules.map((module) => `class ${capitalize(module)}Input extends by(cnst.${capitalize(module)}Input) {}`).join("\n")}
|
|
19
|
+
${databaseModules.map((module) => `class ${capitalize(module)}Insight extends by(cnst.${capitalize(module)}Insight) {}`).join("\n")}
|
|
19
20
|
${databaseModules.length ? `export type { ${databaseModules.map((module) => `${capitalize(module)}Input`).join(", ")} };` : ""}
|
|
21
|
+
${databaseModules.length ? `export type { ${databaseModules.map((module) => `${capitalize(module)}Insight`).join(", ")} };` : ""}
|
|
20
22
|
|
|
21
23
|
${scalarModules.map((module) => `export type * from "./__scalar/${module}/${module}.document";`).join("\n")}
|
|
22
24
|
${databaseModules.map((module) => `export type * from "./${module}/${module}.document";`).join("\n")}
|
|
@@ -24,7 +26,7 @@ ${databaseModules.map((module) => `export type * from "./${module}/${module}.doc
|
|
|
24
26
|
${databaseModules
|
|
25
27
|
.map((module) => {
|
|
26
28
|
const names = { Module: module.charAt(0).toUpperCase() + module.slice(1) };
|
|
27
|
-
return `export const ${module} = DatabaseRegistry.buildModel("${module}" as const, ${names.Module}Input, ${module}Db.${names.Module}, ${module}Db.${names.Module}Model, cnst.${names.Module},
|
|
29
|
+
return `export const ${module} = DatabaseRegistry.buildModel("${module}" as const, ${names.Module}Input, ${module}Db.${names.Module}, ${module}Db.${names.Module}Model, cnst.${names.Module}, ${names.Module}Insight, ${module}Db.${names.Module}Filter);`;
|
|
28
30
|
})
|
|
29
31
|
.join("\n")}
|
|
30
32
|
${scalarModules.map((module) => `export const ${module} = DatabaseRegistry.buildScalar("${module}" as const, ${capitalize(module)});`).join("\n")}
|
|
@@ -650,7 +650,7 @@ export class TaskService extends serve(db.task, ({ plug }) => ({
|
|
|
650
650
|
For a custom adapter class (not a predefined role), pass the class itself, e.g. `ipfsApi: plug(IpfsApi)`
|
|
651
651
|
(see `libs/shared/lib/file/file.service.ts`). Injecting a file/image field is usually simpler than calling
|
|
652
652
|
storage directly: declare `image: field(File).optional()` (or `images: field([File])`) on the model and let the
|
|
653
|
-
store's generated `upload<Field>On<Model>(fileList)` action handle the upload. Add `{ cascade: "
|
|
653
|
+
store's generated `upload<Field>On<Model>(fileList)` action handle the upload. Add `{ cascade: "removeRef" }` to that
|
|
654
654
|
field when the file belongs to the model alone, and removing the model removes the file and its stored object.
|
|
655
655
|
|
|
656
656
|
---
|
|
@@ -868,12 +868,23 @@ A short list of things the type system does not always catch:
|
|
|
868
868
|
`secret` / `hidden` / `resolve()` fields with `text` throw at class-build time — the mirror is plaintext. Search
|
|
869
869
|
runs on sqlite/libsql only; `q.search()` against Postgres throws. `thumb` is mirrored for rendering and is not
|
|
870
870
|
indexed.
|
|
871
|
-
- **`cascade
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
871
|
+
- **`cascade` names a direction, and the wrong one is a data loss.** Both actions can sit on the same field shape,
|
|
872
|
+
so the value has to say which end goes away. `cascade: "removeRef"` on the relation an owner holds
|
|
873
|
+
(`image: field(File, { cascade: "removeRef" })`, arrays included) removes the target when the owner is removed;
|
|
874
|
+
only a relation accepts it. `cascade: "removeWith"` on a child's own reference to its owner
|
|
875
|
+
(`field(ID, { ref: "agentSession", cascade: "removeWith" })`, or a relation, or `refPath` for a polymorphic
|
|
876
|
+
owner whose type field must be an `enumOf`) removes the child when the owner is — the owner never learns its
|
|
877
|
+
children exist. The removal runs through the **target's service** so its `_postRemove` runs too, unless the
|
|
878
|
+
target provably has no removal side effect, in which case the boot-time plan collapses it into one query. A
|
|
879
|
+
`removeWith` field gets its index automatically. Nothing checks for other references to the same target, so
|
|
880
|
+
`removeRef` asserts exclusive ownership. Removal is soft but a storage delete is not, and a query-level removal
|
|
881
|
+
fires no hooks and therefore no cascade.
|
|
882
|
+
- **Removal is always soft, and `delete` is reserved.** `remove(id)`, the facade's `removeMany(query)`, and the
|
|
883
|
+
store's `removeManyByQuery` all stamp `removedAt`; the framework has no hard delete for a model table.
|
|
884
|
+
- **The model facade spells out `Many`/`One` on its writes** — `updateOne`, `updateMany`, `removeOne`, `removeMany`
|
|
885
|
+
— because a bare `update`/`remove` would read like the document-path `update(id)` / `doc.remove()` while hitting
|
|
886
|
+
every match. Reads keep the short `find`/`findOne` pair, and counting is `count(query)` (`countDocuments` is
|
|
887
|
+
`@deprecated`).
|
|
877
888
|
- **`q.search()` is a filter node, not a slice requirement.** Prefer
|
|
878
889
|
`bySearch: filter().arg("text", String).query((text, q) => q.search(text, { prefix: true }))` — the generated
|
|
879
890
|
`listBySearch` / `countBySearch` / `queryBySearch` / `insightBySearch` come for free. Only add a search slice when
|
|
@@ -951,8 +962,12 @@ akan sync automatically generates APIs across all layers. Only write custom logi
|
|
|
951
962
|
| `pick[Query](args)`, `pickId[Query](args)` | Find one (throw if not found) |
|
|
952
963
|
| `exists[Query](args)`, `count[Query](args)` | Existence check and count |
|
|
953
964
|
| `insight[Query](args)`, `query[Query](args)` | Insight and raw query |
|
|
965
|
+
| `remove[Query](args)`, `removeOne[Query](args)` | Query-level soft remove — all matches, or the newest one (`createdAt` desc, not caller-chosen) |
|
|
966
|
+
| `update[Query](args).set(patch)`, `updateOne[Query](args).set(patch)` | Query-level update — the patch lands on a terminal `.set()`, because a filter's trailing args may be optional |
|
|
954
967
|
|
|
955
|
-
**Rule**: Define `Filter` with `.query()` conditions in `document.ts`. akan sync auto-generates all
|
|
968
|
+
**Rule**: Define `Filter` with `.query()` conditions in `document.ts`. akan sync auto-generates all 14 query helper methods per filter. Write `Document` chain methods only for state transitions with validation.
|
|
969
|
+
|
|
970
|
+
**The four query-level writes fire no hooks**, so no `_pre`/`_postRemove` and no cascade run — same as `updateManyByQuery`. Reach for them when the model carries no removal side effect; otherwise remove documents one at a time. A filter keyed after its own model (filter `chat` on model `chat`) is rejected at boot, because `removeChat`/`updateChat` would otherwise shadow the generated single-document CRUD.
|
|
956
971
|
|
|
957
972
|
## Generated Context
|
|
958
973
|
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
interface Dict {
|
|
2
|
-
libName: string;
|
|
3
|
-
}
|
|
4
|
-
export default function getContent(scanInfo: null, dict: Dict) {
|
|
5
|
-
return `
|
|
6
|
-
// 현 디렉토리에서는 프로젝트 내 전체적으로 사용되는 로직을 구현하며, 따라서 백/프론트 비의존적인 pure js 코드만 구현 가능합니다.
|
|
7
|
-
// common폴더와 다른점은, base 코드는 시스템 전체에서 import되어 사용되므로, 가장 핵심적인 로직과 추상화된 기능만 구현해야합니다.
|
|
8
|
-
// @${dict.libName}/base에서는 서버/클라이언트 관련 라이브러리(@*/server, @*/server, @*/client, @*/client)를 모두 import할 수 없으며, @*/base 라이브러리만 import 가능합니다.
|
|
9
|
-
|
|
10
|
-
export const someBaseLogic = () => {
|
|
11
|
-
//
|
|
12
|
-
};
|
|
13
|
-
`;
|
|
14
|
-
}
|