@ordinatio/entities 1.1.0 → 1.3.0

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.
@@ -0,0 +1,117 @@
1
+ /** The fields the rules need. An adapter may return a wider row (createdAt ...): the store is generic over it. */
2
+ interface TagRecord {
3
+ id: string;
4
+ name: string;
5
+ /** The case-insensitive identity of the name (unique per workspace): see `tagNameKey` in `@ordinatio/entities/tags`. */
6
+ nameKey: string;
7
+ color: string;
8
+ pinned: boolean;
9
+ }
10
+ /** Named object types so a future limit is an additive field, not a signature change. */
11
+ interface TagCreateLimits {
12
+ maxTags: number;
13
+ }
14
+ interface TagUpdateLimits {
15
+ maxPinned: number;
16
+ }
17
+ type TagCreateResult<T extends TagRecord> = {
18
+ kind: 'created';
19
+ tag: T;
20
+ } | {
21
+ kind: 'duplicate';
22
+ } | {
23
+ kind: 'limit';
24
+ };
25
+ interface TagUpdatePatch {
26
+ /** Name and key travel TOGETHER so they cannot disagree. */
27
+ rename?: {
28
+ name: string;
29
+ nameKey: string;
30
+ };
31
+ color?: string;
32
+ pinned?: boolean;
33
+ }
34
+ type TagUpdateResult<T extends TagRecord> = {
35
+ kind: 'updated';
36
+ before: T;
37
+ after: T;
38
+ } | {
39
+ kind: 'notFound';
40
+ } | {
41
+ kind: 'pinLimit';
42
+ } | {
43
+ kind: 'duplicate';
44
+ };
45
+ type TagRemoveResult<T extends TagRecord> = {
46
+ kind: 'removed';
47
+ tag: T;
48
+ } | {
49
+ kind: 'notFound';
50
+ };
51
+ interface TagLinkChange {
52
+ add: string[];
53
+ remove: string[];
54
+ }
55
+ type TagApplyResult = {
56
+ kind: 'applied';
57
+ added: string[];
58
+ removed: string[];
59
+ skipped: string[];
60
+ } | {
61
+ kind: 'targetGone';
62
+ } | {
63
+ kind: 'tagGone';
64
+ };
65
+ interface TagStore<T extends TagRecord = TagRecord, Target extends string = string> {
66
+ /** Every tag of the workspace. ORDER IS UNSPECIFIED: the service sorts. */
67
+ list(): Promise<T[]>;
68
+ /**
69
+ * Pinned tags only, at most `limit`, in ONE bounded query (an app may run this on every page). Order unspecified, but when more are pinned
70
+ * than `limit` the SAME ones should come back each time (for example by name), or the bookmarks would change from page to page.
71
+ */
72
+ listPinned(limit: number): Promise<T[]>;
73
+ /** The tags of THIS workspace among `ids`; foreign or deleted ids are simply absent. */
74
+ getMany(ids: string[]): Promise<T[]>;
75
+ findByKey(nameKey: string): Promise<T | null>;
76
+ get(id: string): Promise<T | null>;
77
+ /**
78
+ * ATOMIC: the limit is enforced together with the insert, so simultaneous creates can never overshoot it. Precedence: `limit` wins over
79
+ * `duplicate` (a duplicate name when the workspace is full is `limit`).
80
+ */
81
+ create(input: {
82
+ name: string;
83
+ nameKey: string;
84
+ color: string;
85
+ }, limits: TagCreateLimits): Promise<TagCreateResult<T>>;
86
+ /**
87
+ * ATOMIC. Precedence: `notFound`, then `pinLimit`, then `duplicate`. Pinning a tag that is ALREADY pinned never counts against the limit
88
+ * (the pinned flag is re-read under the lock); unpinning needs no limit. Renaming a tag to the same key it already has (a case change) is
89
+ * not a duplicate. An empty patch is a no-op that returns `updated` with `before` equal to `after`.
90
+ * `before` is read in the same transaction as the write, so `before`/`after` are consistent under concurrent edits.
91
+ */
92
+ update(id: string, patch: TagUpdatePatch, limits: TagUpdateLimits): Promise<TagUpdateResult<T>>;
93
+ /** The tag's links go with it. Ten parallel removes of one tag give exactly one `removed`. */
94
+ remove(id: string): Promise<TagRemoveResult<T>>;
95
+ targetExists(target: Target, targetId: string): Promise<boolean>;
96
+ /**
97
+ * One transaction. Frozen result shapes:
98
+ * - `skipped` = ids in `add` that are NOT tags of this workspace at the START of the call. Ids in `remove` that are not this workspace's
99
+ * tags are IGNORED silently (never reported, never touched). An `add` that is already linked is in neither `added` nor `skipped`.
100
+ * - A tag that existed at the start but is gone when the write happens: `tagGone`, nothing applied. A target that vanished: `targetGone`.
101
+ * - `added` / `removed` come from rows the database REALLY inserted / deleted, so concurrent saves never double-report one change.
102
+ * An id in BOTH `add` and `remove` is undefined behaviour: the service never sends it (`planTagChanges` drops it from `remove`).
103
+ * `actorId` is stored with each new link (who tagged it); it is opaque to this package.
104
+ */
105
+ applyLinks(target: Target, targetId: string, change: TagLinkChange, actorId: string): Promise<TagApplyResult>;
106
+ /** Tags on one target. Order unspecified. */
107
+ tagsForTarget(target: Target, targetId: string): Promise<T[]>;
108
+ /** Tags for many targets in ONE round trip. Only targets with at least one tag appear in the map. */
109
+ tagsForTargets(target: Target, targetIds: string[]): Promise<Map<string, T[]>>;
110
+ /**
111
+ * Items per tag per target type, for EVERY tag of the workspace (or only `ids`): a tag with no links appears with zero for every target
112
+ * type. One grouped query per target type in a real adapter, not one per tag. An EMPTY `ids` asks for nothing: it returns an empty map.
113
+ */
114
+ counts(ids?: string[]): Promise<Map<string, Record<Target, number>>>;
115
+ }
116
+
117
+ export type { TagRecord as T, TagStore as a, TagLinkChange as b, TagApplyResult as c, TagCreateLimits as d, TagCreateResult as e, TagRemoveResult as f, TagUpdateLimits as g, TagUpdatePatch as h, TagUpdateResult as i };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ordinatio/entities",
3
- "version": "1.1.0",
3
+ "version": "1.3.0",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "description": "Entity knowledge, agent intelligence, notes, and contacts",
@@ -19,6 +19,16 @@
19
19
  "types": "./dist/tags/index.d.mts",
20
20
  "import": "./dist/tags/index.mjs",
21
21
  "default": "./dist/tags/index.mjs"
22
+ },
23
+ "./tags/server": {
24
+ "types": "./dist/tags/server/index.d.mts",
25
+ "import": "./dist/tags/server/index.mjs",
26
+ "default": "./dist/tags/server/index.mjs"
27
+ },
28
+ "./tags/prisma": {
29
+ "types": "./dist/tags/prisma/index.d.mts",
30
+ "import": "./dist/tags/prisma/index.mjs",
31
+ "default": "./dist/tags/prisma/index.mjs"
22
32
  }
23
33
  },
24
34
  "publishConfig": {
@@ -49,7 +59,7 @@
49
59
  }
50
60
  },
51
61
  "scripts": {
52
- "build": "tsup src/index.ts src/tags/index.ts --format esm --dts --clean",
62
+ "build": "tsup src/index.ts src/tags/index.ts src/tags/server/index.ts src/tags/prisma/index.ts --format esm --dts --clean",
53
63
  "test": "vitest",
54
64
  "test:run": "vitest run"
55
65
  }