@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,168 @@
1
+ import { b as TagLinkChange, T as TagRecord, a as TagStore } from '../../types-C81I1Teg.mjs';
2
+ export { c as TagApplyResult, d as TagCreateLimits, e as TagCreateResult, f as TagRemoveResult, g as TagUpdateLimits, h as TagUpdatePatch, i as TagUpdateResult } from '../../types-C81I1Teg.mjs';
3
+ import { b as TagLimits } from '../../constants-DSRJ-l7Z.mjs';
4
+
5
+ type TagChangePlan = {
6
+ ok: true;
7
+ change: TagLinkChange;
8
+ } | {
9
+ ok: false;
10
+ reason: 'too-many-changes';
11
+ count: number;
12
+ };
13
+ /**
14
+ * An id in both lists stays ADDED (the user's last word is "keep it"): it is dropped from `remove`. The count that is capped is the number
15
+ * of distinct changes after that (adds plus removes).
16
+ */
17
+ declare function planTagChanges(change: TagLinkChange, maxChanges: number): TagChangePlan;
18
+
19
+ /** Why a call did not do what was asked, with the context an app needs for its message. */
20
+ type TagFailure = {
21
+ reason: 'invalid-name';
22
+ problem: 'empty' | 'too-long';
23
+ length: number;
24
+ } | {
25
+ reason: 'invalid-color';
26
+ color: string;
27
+ } | {
28
+ reason: 'duplicate';
29
+ } | {
30
+ reason: 'limit';
31
+ } | {
32
+ reason: 'pin-limit';
33
+ } | {
34
+ reason: 'not-found';
35
+ } | {
36
+ reason: 'target-gone';
37
+ } | {
38
+ reason: 'tag-gone';
39
+ } | {
40
+ reason: 'too-many-changes';
41
+ count: number;
42
+ };
43
+ type TagResult<V> = ({
44
+ ok: true;
45
+ } & V) | ({
46
+ ok: false;
47
+ } & TagFailure);
48
+ interface TagOption {
49
+ id: string;
50
+ name: string;
51
+ color: string;
52
+ }
53
+ interface TagService<T extends TagRecord, Target extends string> {
54
+ /** Every tag, sorted by name. */
55
+ list(): Promise<T[]>;
56
+ /** Just what a picker needs, sorted by name. */
57
+ options(): Promise<TagOption[]>;
58
+ /** Pinned tags (at most `maxPinned`), sorted by name. */
59
+ pinned(): Promise<T[]>;
60
+ get(id: string): Promise<TagResult<{
61
+ tag: T;
62
+ }>>;
63
+ create(input: {
64
+ name: string;
65
+ color?: string;
66
+ }): Promise<TagResult<{
67
+ tag: T;
68
+ }>>;
69
+ /** The existing tag when the name is already taken (any case), else a new one. */
70
+ findOrCreate(name: string): Promise<TagResult<{
71
+ tag: T;
72
+ created: boolean;
73
+ }>>;
74
+ update(id: string, input: {
75
+ name?: string;
76
+ color?: string;
77
+ pinned?: boolean;
78
+ }): Promise<TagResult<{
79
+ before: T;
80
+ after: T;
81
+ }>>;
82
+ remove(id: string): Promise<TagResult<{
83
+ tag: T;
84
+ }>>;
85
+ /** Applies ONLY what the user changed. An empty change succeeds without touching the store (not even to check the item exists). */
86
+ applyChanges(target: Target, targetId: string, change: {
87
+ add: string[];
88
+ remove: string[];
89
+ }, actorId: string): Promise<TagResult<{
90
+ added: string[];
91
+ removed: string[];
92
+ skipped: string[];
93
+ }>>;
94
+ forTarget(target: Target, targetId: string): Promise<TagResult<{
95
+ tags: T[];
96
+ }>>;
97
+ forTargets(target: Target, targetIds: string[]): Promise<Map<string, T[]>>;
98
+ counts(ids?: string[]): Promise<Map<string, Record<Target, number>>>;
99
+ }
100
+ declare function createTagService<T extends TagRecord, Target extends string>(store: TagStore<T, Target>, suppliedLimits?: TagLimits): TagService<T, Target>;
101
+
102
+ interface TagContractRunner {
103
+ describe: (name: string, body: () => void) => void;
104
+ it: ((name: string, body: () => Promise<void>) => void) & {
105
+ skip?: (name: string, body: () => Promise<void>) => void;
106
+ };
107
+ expect: (value: unknown) => {
108
+ toBe(expected: unknown): void;
109
+ toEqual(expected: unknown): void;
110
+ toBeNull(): void;
111
+ };
112
+ }
113
+ interface TagStoreHarness<Target extends string = string> {
114
+ /** The taggable kinds under test. The suite REQUIRES at least three (it throws otherwise); four or more prove that a new kind needs no change to the logic. */
115
+ targetTypes: readonly Target[];
116
+ /** A fresh, empty workspace id. Every test uses its own, so tests never see each other's rows. */
117
+ newWorkspace(): Promise<string>;
118
+ /** The store for one workspace. Called repeatedly for the same id: it must give the same data. */
119
+ storeFor(workspace: string): TagStore<TagRecord, Target>;
120
+ /** Creates a real taggable thing (a person, a task ...) in the workspace and returns its id. */
121
+ addTarget(workspace: string, type: Target): Promise<string>;
122
+ /**
123
+ * OPTIONAL. Creates a target of `type` with exactly this id, so a person and a task can share one id (as they do when ids are per-table
124
+ * integers). Offer it if your storage allows; the tests that need it are skipped (or reported as skipped) without it.
125
+ */
126
+ addTargetWithId?(workspace: string, type: Target, id: string): Promise<string>;
127
+ /**
128
+ * Deletes it with everything that points at it. NOTE: the contract also passes ids that do not exist, such as 'missing', 'ghost-1' or
129
+ * 'nothing', for tags and targets: an adapter with uuid or integer id columns must answer them as "not found" instead of failing the cast.
130
+ */
131
+ removeTarget(workspace: string, type: Target, id: string): Promise<void>;
132
+ /**
133
+ * OPTIONAL test seam. Arms a ONE-SHOT hook that the next `applyLinks` runs after its existence checks and before its write, so a test can
134
+ * delete a tag or a target at exactly the dangerous moment. Without it the two delete-while-applying checks are shown as skipped when your
135
+ * runner has `it.skip`; if it does not, they run and fail, so supply the hook.
136
+ */
137
+ armBeforeLinkWrite?(hook: () => Promise<void>): void;
138
+ /** Removes everything the tests created. */
139
+ cleanup(): Promise<void>;
140
+ }
141
+
142
+ interface MemoryTagWorldOptions<Target extends string> {
143
+ targetTypes: readonly Target[];
144
+ /** Default true. False removes the one-at-a-time chain so races become visible (for proving the contract's concurrency tests). */
145
+ serialise?: boolean;
146
+ }
147
+ interface MemoryTagWorld<Target extends string> {
148
+ store(workspace: string): TagStore<TagRecord, Target>;
149
+ /** Registers a taggable thing (a person, a task ...) and returns its id. */
150
+ addTarget(workspace: string, type: Target, id?: string): string;
151
+ /** Removes a target and every link to it. */
152
+ removeTarget(workspace: string, type: Target, id: string): Promise<void>;
153
+ /** Arms a ONE-SHOT hook run by the next `applyLinks` IN THIS WORLD (any workspace) between its existence check and its write (to delete a tag or target mid-flight). */
154
+ armBeforeLinkWrite(hook: () => Promise<void>): void;
155
+ /** Who linked it (the `actorId` passed to `applyLinks`), or undefined when there is no such link. */
156
+ actorOf(workspace: string, type: Target, targetId: string, tagId: string): string | undefined;
157
+ }
158
+ declare function createMemoryTagWorld<Target extends string>(options: MemoryTagWorldOptions<Target>): MemoryTagWorld<Target>;
159
+ /** The contract harness around a fresh in-memory world: what this package runs its own contract with, and a starting point for an app's fake. */
160
+ declare function createMemoryTagHarness<Target extends string>(targetTypes: readonly Target[], options?: {
161
+ serialise?: boolean;
162
+ }): TagStoreHarness<Target> & {
163
+ world: MemoryTagWorld<Target>;
164
+ };
165
+
166
+ declare function defineTagStoreContract<Target extends string>(api: TagContractRunner, harness: TagStoreHarness<Target>): void;
167
+
168
+ export { type MemoryTagWorld, type MemoryTagWorldOptions, type TagChangePlan, type TagContractRunner, type TagFailure, TagLinkChange, type TagOption, TagRecord, type TagResult, type TagService, TagStore, type TagStoreHarness, createMemoryTagHarness, createMemoryTagWorld, createTagService, defineTagStoreContract, planTagChanges };