@substrat-run/contract-tests 0.138.0 → 0.139.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.
- package/dist/attachment-text-fixtures.d.ts +17 -0
- package/dist/attachment-text-fixtures.d.ts.map +1 -1
- package/dist/attachment-text-fixtures.js +344 -4
- package/dist/attachment-text-fixtures.js.map +1 -1
- package/dist/attachment-text-suite.d.ts +16 -1
- package/dist/attachment-text-suite.d.ts.map +1 -1
- package/dist/attachment-text-suite.js +212 -28
- package/dist/attachment-text-suite.js.map +1 -1
- package/dist/caused-by-module.d.ts +23 -0
- package/dist/caused-by-module.d.ts.map +1 -0
- package/dist/caused-by-module.js +56 -0
- package/dist/caused-by-module.js.map +1 -0
- package/dist/caused-by-suite.d.ts +28 -0
- package/dist/caused-by-suite.d.ts.map +1 -0
- package/dist/caused-by-suite.js +179 -0
- package/dist/caused-by-suite.js.map +1 -0
- package/dist/entity-state-module.d.ts +134 -0
- package/dist/entity-state-module.d.ts.map +1 -0
- package/dist/entity-state-module.js +140 -0
- package/dist/entity-state-module.js.map +1 -0
- package/dist/entity-state-suite.d.ts +9 -0
- package/dist/entity-state-suite.d.ts.map +1 -0
- package/dist/entity-state-suite.js +534 -0
- package/dist/entity-state-suite.js.map +1 -0
- package/dist/findings-atomic-suite.d.ts +21 -0
- package/dist/findings-atomic-suite.d.ts.map +1 -0
- package/dist/findings-atomic-suite.js +131 -0
- package/dist/findings-atomic-suite.js.map +1 -0
- package/dist/findings-suite.d.ts +11 -0
- package/dist/findings-suite.d.ts.map +1 -0
- package/dist/findings-suite.js +391 -0
- package/dist/findings-suite.js.map +1 -0
- package/dist/index.d.ts +9 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/membership-executor-suite.d.ts +6 -2
- package/dist/membership-executor-suite.d.ts.map +1 -1
- package/dist/membership-executor-suite.js +260 -14
- package/dist/membership-executor-suite.js.map +1 -1
- package/dist/modules.d.ts +217 -0
- package/dist/modules.d.ts.map +1 -1
- package/dist/modules.js +4 -0
- package/dist/modules.js.map +1 -1
- package/dist/permission-suite.d.ts.map +1 -1
- package/dist/permission-suite.js +75 -0
- package/dist/permission-suite.js.map +1 -1
- package/dist/scope-host-suite.d.ts.map +1 -1
- package/dist/scope-host-suite.js +48 -0
- package/dist/scope-host-suite.js.map +1 -1
- package/dist/vertical-events-suite.d.ts +14 -0
- package/dist/vertical-events-suite.d.ts.map +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|
2
|
+
import { orgId, platformActorId, principalId, scopeId, tenantId, } from '@substrat-run/contracts';
|
|
3
|
+
import { ulid } from '@substrat-run/kernel';
|
|
4
|
+
import { connectorMod } from './modules.js';
|
|
5
|
+
import { CAUSED_BY_HOLD, causedByMod, tick } from './caused-by-module.js';
|
|
6
|
+
/**
|
|
7
|
+
* #2055: an executor's admin rows name the event they are the effect of (K-22, `causedBy`),
|
|
8
|
+
* and no other admin row does — in particular not one some other caller writes on the same
|
|
9
|
+
* host while the handler is suspended. The SQLite host used to keep the event in a host-wide
|
|
10
|
+
* field set around the handler's `await`, so a staff call landing in that window was recorded
|
|
11
|
+
* as caused by an event it had nothing to do with.
|
|
12
|
+
*
|
|
13
|
+
* Each door an executor runs through is held here with a staff write landing inside the hold:
|
|
14
|
+
* a plain executor (`registerExecutor`, through the `admin` it is handed), an in-process
|
|
15
|
+
* connector (`registerConnector`, through `ctx.admin`), and a routed connector delivery
|
|
16
|
+
* (`dispatchConnector`). Every case asserts both halves — the staff row carries no cause, and
|
|
17
|
+
* the handler's own row still carries its event — because either alone is also what dropping
|
|
18
|
+
* `causedBy` everywhere looks like.
|
|
19
|
+
*/
|
|
20
|
+
export function causedByContractSuite(adapterName, makeFixture) {
|
|
21
|
+
describe(`causedBy is per call, never host-wide (#2055): ${adapterName}`, () => {
|
|
22
|
+
let fixture;
|
|
23
|
+
let host;
|
|
24
|
+
const staff = platformActorId.parse(ulid());
|
|
25
|
+
const alice = principalId.parse(ulid());
|
|
26
|
+
const t = tenantId.parse(ulid());
|
|
27
|
+
const s = scopeId.parse(ulid());
|
|
28
|
+
/** Every handler waits here, after it was entered and before it writes. */
|
|
29
|
+
let gate = Promise.resolve();
|
|
30
|
+
let release = () => undefined;
|
|
31
|
+
let entered = Promise.resolve();
|
|
32
|
+
let enter = () => undefined;
|
|
33
|
+
const shut = () => {
|
|
34
|
+
gate = new Promise((r) => (release = r));
|
|
35
|
+
entered = new Promise((r) => (enter = r));
|
|
36
|
+
};
|
|
37
|
+
/** The handlers' one write: an org named by the event's tag, so its row is findable. */
|
|
38
|
+
const heldWrite = async (admin, event) => {
|
|
39
|
+
enter();
|
|
40
|
+
await gate;
|
|
41
|
+
const tag = event.payload.tag;
|
|
42
|
+
await admin.createOrg(staff, { id: orgId.parse(ulid()), tenantId: event.tenantId, slug: tag, name: tag });
|
|
43
|
+
};
|
|
44
|
+
const tagOf = (kind) => `${kind}-${ulid().slice(-8).toLowerCase()}`;
|
|
45
|
+
/**
|
|
46
|
+
* Start a handler with `start` (handed a fresh tag; answers the id of the event the handler
|
|
47
|
+
* runs for once it is done), make a staff write while it is held, release it, and assert both
|
|
48
|
+
* halves: the staff row carries no cause, the handler's own row carries its event.
|
|
49
|
+
*/
|
|
50
|
+
const expectCausedPerCall = async (start, kind) => {
|
|
51
|
+
shut();
|
|
52
|
+
const tag = tagOf(kind);
|
|
53
|
+
const done = start(tag);
|
|
54
|
+
await entered;
|
|
55
|
+
const staffSlug = tagOf('staff');
|
|
56
|
+
await host.admin.createOrg(staff, { id: orgId.parse(ulid()), tenantId: t, slug: staffSlug, name: staffSlug });
|
|
57
|
+
release();
|
|
58
|
+
const eventId = await done;
|
|
59
|
+
const created = await host.admin.auditLog(staff, { tenantId: t, action: 'createOrg', limit: 500 });
|
|
60
|
+
const causeOf = (slug) => created.find((r) => r.after?.slug === slug)?.causedBy;
|
|
61
|
+
expect(causeOf(staffSlug)).toBeNull();
|
|
62
|
+
expect(causeOf(tag)).toBe(eventId);
|
|
63
|
+
};
|
|
64
|
+
/** Invoke `operation` for `tag`; answers the event id the one executor delivered. */
|
|
65
|
+
const invoked = (operation) => async (tag) => {
|
|
66
|
+
const outcomes = [];
|
|
67
|
+
await (await host.getScope(alice, t, s)).invoke(operation, { tag }, { onExecutorOutcomes: (o) => outcomes.push(...o) });
|
|
68
|
+
expect(outcomes.map((o) => o.outcome)).toEqual(['delivered']);
|
|
69
|
+
return outcomes[0].eventId;
|
|
70
|
+
};
|
|
71
|
+
beforeAll(async () => {
|
|
72
|
+
fixture = await makeFixture();
|
|
73
|
+
host = fixture.host;
|
|
74
|
+
host.registerModule(connectorMod);
|
|
75
|
+
host.registerExecutor('held-effector', 'effect.requested', (admin, event) => heldWrite(admin, event));
|
|
76
|
+
host.registerConnector('held-caller', 'outbound.requested', (ctx, event) => heldWrite(ctx.admin, event));
|
|
77
|
+
await host.admin.createTenant(staff, { id: t, slug: `caused-${t.slice(-10).toLowerCase()}`, name: 'Caused' });
|
|
78
|
+
await host.admin.grantEntitlement(staff, t, 'connector');
|
|
79
|
+
await host.provisionScope(staff, { tenantId: t, scopeId: s, vertical: 'connector-vertical' });
|
|
80
|
+
await host.admin.activateScope(staff, t, s);
|
|
81
|
+
});
|
|
82
|
+
afterAll(async () => {
|
|
83
|
+
await fixture.cleanup();
|
|
84
|
+
});
|
|
85
|
+
it('an executor: a staff write while its handler awaits carries no cause; the handler\'s own row carries its event', async () => {
|
|
86
|
+
await expectCausedPerCall(invoked('connector/request-effect'), 'effect');
|
|
87
|
+
});
|
|
88
|
+
it('an in-process connector: the same, through `ctx.admin`', async () => {
|
|
89
|
+
await expectCausedPerCall(invoked('connector/request-outbound'), 'outbound');
|
|
90
|
+
});
|
|
91
|
+
it('a routed connector delivery (`dispatchConnector`): the same', async () => {
|
|
92
|
+
await expectCausedPerCall(async (tag) => {
|
|
93
|
+
const event = { id: ulid(), tenantId: t, type: 'outbound.requested', payload: { tag } };
|
|
94
|
+
await host.dispatchConnector(t, s, (ctx, e) => heldWrite(ctx.admin, e), event);
|
|
95
|
+
return event.id;
|
|
96
|
+
}, 'routed');
|
|
97
|
+
});
|
|
98
|
+
it('two views with two causes, their writes interleaved across awaits: each row carries its own', async () => {
|
|
99
|
+
// A null cause beside a non-null one cannot catch two causes trading places, so both are
|
|
100
|
+
// set here. Both views are made before either's admin is first read (it is built lazily),
|
|
101
|
+
// and each is written through twice.
|
|
102
|
+
const e1 = ulid();
|
|
103
|
+
const e2 = ulid();
|
|
104
|
+
const a = host.attributed({ principal: alice, tenantId: t }, { causedBy: e1 });
|
|
105
|
+
const b = host.attributed({ principal: alice, tenantId: t }, { causedBy: e2 });
|
|
106
|
+
const [a1, a2, b1, b2] = [tagOf('a1'), tagOf('a2'), tagOf('b1'), tagOf('b2')];
|
|
107
|
+
const write = (view, slug) => view.admin.createOrg(staff, { id: orgId.parse(ulid()), tenantId: t, slug, name: slug });
|
|
108
|
+
const first = write(a, a1); // A starts…
|
|
109
|
+
await write(b, b1); // …B writes while A's write is in flight…
|
|
110
|
+
await first;
|
|
111
|
+
await write(a, a2); // …A writes again…
|
|
112
|
+
await write(b, b2); // …and B again.
|
|
113
|
+
const created = await host.admin.auditLog(staff, { tenantId: t, action: 'createOrg', limit: 500 });
|
|
114
|
+
const causeOf = (slug) => created.find((r) => r.after?.slug === slug)?.causedBy;
|
|
115
|
+
expect([a1, a2, b1, b2].map(causeOf)).toEqual([e1, e1, e2, e2]);
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* #2055, the scope's half: what a consumer emits names the event it consumed (#1237), and no
|
|
121
|
+
* other emit in the scope does — in particular not one another call makes while the consumer
|
|
122
|
+
* awaits. Each adapter serializes a scope's emitting work (the SQLite `ScopeActor`, the
|
|
123
|
+
* Durable Object's `OperationQueue`), and the cause is passed into the consumer's context
|
|
124
|
+
* rather than read off a field, so neither guarantee leans on the other.
|
|
125
|
+
*
|
|
126
|
+
* The fixture's host must carry `causedByMod` in the scope it reaches: the suite registers it
|
|
127
|
+
* on the host, and a code-time module set (a Cloudflare ScopeDO's) must already include it.
|
|
128
|
+
*/
|
|
129
|
+
export function scopeCausedByContractSuite(adapterName, makeFixture) {
|
|
130
|
+
describe(`a consumer's cause stamps its own emits, never another call's (#2055): ${adapterName}`, () => {
|
|
131
|
+
let fixture;
|
|
132
|
+
let host;
|
|
133
|
+
const staff = platformActorId.parse(ulid());
|
|
134
|
+
const alice = principalId.parse(ulid());
|
|
135
|
+
const bob = principalId.parse(ulid());
|
|
136
|
+
const t = tenantId.parse(ulid());
|
|
137
|
+
const s = scopeId.parse(ulid());
|
|
138
|
+
beforeAll(async () => {
|
|
139
|
+
fixture = await makeFixture();
|
|
140
|
+
host = fixture.host;
|
|
141
|
+
host.registerModule(causedByMod);
|
|
142
|
+
await host.admin.createTenant(staff, { id: t, slug: `scope-caused-${t.slice(-10).toLowerCase()}`, name: 'Scope caused' });
|
|
143
|
+
await host.admin.grantEntitlement(staff, t, 'causedby');
|
|
144
|
+
await host.provisionScope(staff, { tenantId: t, scopeId: s });
|
|
145
|
+
await host.admin.activateScope(staff, t, s);
|
|
146
|
+
});
|
|
147
|
+
afterAll(async () => {
|
|
148
|
+
delete globalThis[CAUSED_BY_HOLD];
|
|
149
|
+
await fixture.cleanup();
|
|
150
|
+
});
|
|
151
|
+
it('a call emitting while a consumer awaits is caused by nothing; the consumer\'s own emit is caused by its event', async () => {
|
|
152
|
+
const hold = { shut: true, entered: false };
|
|
153
|
+
globalThis[CAUSED_BY_HOLD] = hold;
|
|
154
|
+
const tag = ulid().slice(-8).toLowerCase();
|
|
155
|
+
const starting = (await host.getScope(alice, t, s)).invoke('causedby/start', { tag });
|
|
156
|
+
while (!hold.entered)
|
|
157
|
+
await tick();
|
|
158
|
+
// Another caller, the same scope, while the consumer is suspended. Where the scope's
|
|
159
|
+
// work is serialized it waits for the consumer; either way, its event is its own.
|
|
160
|
+
// Not awaited before the release: on a host whose scope work is queued, reaching the
|
|
161
|
+
// scope at all waits for the consumer.
|
|
162
|
+
const other = host.getScope(bob, t, s).then((stub) => stub.invoke('causedby/other', { tag }));
|
|
163
|
+
await tick(20);
|
|
164
|
+
hold.shut = false;
|
|
165
|
+
await Promise.all([starting, other]);
|
|
166
|
+
const events = await host.admin.readUndrainedEvents(staff, t, s, 200);
|
|
167
|
+
const one = (type) => {
|
|
168
|
+
const found = events.filter((e) => e.type === type && e.entity.entityId === tag);
|
|
169
|
+
expect(found).toHaveLength(1);
|
|
170
|
+
return found[0];
|
|
171
|
+
};
|
|
172
|
+
const requested = one('causedby.requested');
|
|
173
|
+
expect(one('causedby.other').causedBy).toBeNull();
|
|
174
|
+
expect(one('causedby.effected').causedBy).toBe(requested.id);
|
|
175
|
+
expect(requested.causedBy).toBeNull();
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
//# sourceMappingURL=caused-by-suite.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"caused-by-suite.js","sourceRoot":"","sources":["../src/caused-by-suite.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { type ModuleRegistration } from '@substrat-run/kernel';
|
|
2
|
+
export declare const stateModManifest: {
|
|
3
|
+
id: string & import("zod").$brand<"ModuleId">;
|
|
4
|
+
version: string;
|
|
5
|
+
kernelContract: string;
|
|
6
|
+
permissions: {
|
|
7
|
+
key: string & import("zod").$brand<"PermissionKey">;
|
|
8
|
+
description: string;
|
|
9
|
+
}[];
|
|
10
|
+
events: {
|
|
11
|
+
emits: {
|
|
12
|
+
type: string;
|
|
13
|
+
schemaVersion: number;
|
|
14
|
+
}[];
|
|
15
|
+
consumes: {
|
|
16
|
+
type: string;
|
|
17
|
+
schemaVersion: number;
|
|
18
|
+
from?: string | undefined;
|
|
19
|
+
}[];
|
|
20
|
+
exports?: {
|
|
21
|
+
type: string;
|
|
22
|
+
schemaVersion: number;
|
|
23
|
+
readPermission: string & import("zod").$brand<"PermissionKey">;
|
|
24
|
+
}[] | undefined;
|
|
25
|
+
};
|
|
26
|
+
migrations: {
|
|
27
|
+
journalDir: string;
|
|
28
|
+
compatibleFrom: string;
|
|
29
|
+
};
|
|
30
|
+
attachmentTargets: {
|
|
31
|
+
entityType: string;
|
|
32
|
+
readPermission: string & import("zod").$brand<"PermissionKey">;
|
|
33
|
+
writePermission?: (string & import("zod").$brand<"PermissionKey">) | undefined;
|
|
34
|
+
}[];
|
|
35
|
+
liveTargets?: {
|
|
36
|
+
entityType: string;
|
|
37
|
+
readPermission: string & import("zod").$brand<"PermissionKey">;
|
|
38
|
+
}[] | undefined;
|
|
39
|
+
entityRelations?: {
|
|
40
|
+
entityType: string;
|
|
41
|
+
parentType: string;
|
|
42
|
+
}[] | undefined;
|
|
43
|
+
guards?: {
|
|
44
|
+
before: string;
|
|
45
|
+
predicate: string;
|
|
46
|
+
config: Record<string, unknown>;
|
|
47
|
+
}[] | undefined;
|
|
48
|
+
schedules?: {
|
|
49
|
+
operation: string;
|
|
50
|
+
cadence: {
|
|
51
|
+
everyMinutes: number;
|
|
52
|
+
};
|
|
53
|
+
input?: Record<string, unknown> | undefined;
|
|
54
|
+
permissions: (string & import("zod").$brand<"PermissionKey">)[];
|
|
55
|
+
}[] | undefined;
|
|
56
|
+
peers?: {
|
|
57
|
+
vertical: string;
|
|
58
|
+
operations: string[];
|
|
59
|
+
permissions: (string & import("zod").$brand<"PermissionKey">)[];
|
|
60
|
+
}[] | undefined;
|
|
61
|
+
freshness?: {
|
|
62
|
+
eventType: string;
|
|
63
|
+
within: {
|
|
64
|
+
hours: number;
|
|
65
|
+
};
|
|
66
|
+
}[] | undefined;
|
|
67
|
+
withdraws?: string[] | undefined;
|
|
68
|
+
entitlementKey: string;
|
|
69
|
+
envSpec?: {
|
|
70
|
+
key: string;
|
|
71
|
+
label?: string | undefined;
|
|
72
|
+
description: string;
|
|
73
|
+
placeholder?: string | undefined;
|
|
74
|
+
required: boolean;
|
|
75
|
+
secret: boolean;
|
|
76
|
+
default?: string | undefined;
|
|
77
|
+
group?: string | undefined;
|
|
78
|
+
}[] | undefined;
|
|
79
|
+
ownerGrants?: (string & import("zod").$brand<"PermissionKey">)[] | undefined;
|
|
80
|
+
entitlements?: string[] | undefined;
|
|
81
|
+
provides?: string[] | undefined;
|
|
82
|
+
requires?: string[] | undefined;
|
|
83
|
+
api?: string | undefined;
|
|
84
|
+
searchables?: {
|
|
85
|
+
entityType: string;
|
|
86
|
+
fields: string[];
|
|
87
|
+
table?: string | undefined;
|
|
88
|
+
idColumn?: string | undefined;
|
|
89
|
+
tokenizer?: "prefix" | "substring" | undefined;
|
|
90
|
+
}[] | undefined;
|
|
91
|
+
lists?: {
|
|
92
|
+
entityType: string;
|
|
93
|
+
sortable: string[];
|
|
94
|
+
filterable?: string[] | undefined;
|
|
95
|
+
table?: string | undefined;
|
|
96
|
+
idColumn?: string | undefined;
|
|
97
|
+
}[] | undefined;
|
|
98
|
+
entityStates?: {
|
|
99
|
+
archivePermission?: (string & import("zod").$brand<"PermissionKey">) | undefined;
|
|
100
|
+
trashPermission?: (string & import("zod").$brand<"PermissionKey">) | undefined;
|
|
101
|
+
table?: string | undefined;
|
|
102
|
+
idColumn?: string | undefined;
|
|
103
|
+
entityType: string;
|
|
104
|
+
}[] | undefined;
|
|
105
|
+
ui?: {
|
|
106
|
+
routes?: {
|
|
107
|
+
path: string;
|
|
108
|
+
screen: string;
|
|
109
|
+
permission: string & import("zod").$brand<"PermissionKey">;
|
|
110
|
+
}[] | undefined;
|
|
111
|
+
nav?: {
|
|
112
|
+
label: string;
|
|
113
|
+
icon?: string | undefined;
|
|
114
|
+
to: string;
|
|
115
|
+
permission: string & import("zod").$brand<"PermissionKey">;
|
|
116
|
+
}[] | undefined;
|
|
117
|
+
entityViews?: {
|
|
118
|
+
entityType: string;
|
|
119
|
+
view: string;
|
|
120
|
+
}[] | undefined;
|
|
121
|
+
widgets?: {
|
|
122
|
+
slot: string;
|
|
123
|
+
component: string;
|
|
124
|
+
permission: string & import("zod").$brand<"PermissionKey">;
|
|
125
|
+
}[] | undefined;
|
|
126
|
+
settingsPanels?: {
|
|
127
|
+
label: string;
|
|
128
|
+
component: string;
|
|
129
|
+
permission: string & import("zod").$brand<"PermissionKey">;
|
|
130
|
+
}[] | undefined;
|
|
131
|
+
} | undefined;
|
|
132
|
+
};
|
|
133
|
+
export declare const stateMod: ModuleRegistration;
|
|
134
|
+
//# sourceMappingURL=entity-state-module.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entity-state-module.d.ts","sourceRoot":"","sources":["../src/entity-state-module.ts"],"names":[],"mappings":"AAaA,OAAO,EAIL,KAAK,kBAAkB,EAExB,MAAM,sBAAsB,CAAC;AAE9B,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6B3B,CAAC;AAQH,eAAO,MAAM,QAAQ,EAAE,kBA2FtB,CAAC"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The fixture behind `entityStateContractSuite` (#119): a module whose entities differ in
|
|
3
|
+
* exactly what they declare.
|
|
4
|
+
*
|
|
5
|
+
* - `stdoc` — archive AND trash, each with its own key; paged, filterable and searchable.
|
|
6
|
+
* - `stnote` — archive only, so the suite can see a trash refused on it and a one-column
|
|
7
|
+
* partial index.
|
|
8
|
+
* - `stplain` — neither, so the verbs and the views are refused on it rather than guessed at.
|
|
9
|
+
*
|
|
10
|
+
* Every operation is a thin pass-through to the verb or read under test, and checks nothing
|
|
11
|
+
* itself: the KERNEL's check of the declared key is what the suite is asserting on.
|
|
12
|
+
*/
|
|
13
|
+
import { moduleManifest } from '@substrat-run/contracts';
|
|
14
|
+
import { listIndexPlans, listQuery, readHistory, } from '@substrat-run/kernel';
|
|
15
|
+
export const stateModManifest = moduleManifest.parse({
|
|
16
|
+
id: '@test/state',
|
|
17
|
+
version: '1.0.0',
|
|
18
|
+
kernelContract: '^0.0.1',
|
|
19
|
+
permissions: [
|
|
20
|
+
{ key: 'doc:read', description: 'read the documents' },
|
|
21
|
+
{ key: 'doc:archive', description: 'archive a document or bring it back' },
|
|
22
|
+
{ key: 'doc:trash', description: 'bin a document or restore it' },
|
|
23
|
+
],
|
|
24
|
+
events: { emits: [], consumes: [] },
|
|
25
|
+
migrations: { journalDir: './migrations', compatibleFrom: '1.0.0' },
|
|
26
|
+
attachmentTargets: [],
|
|
27
|
+
entitlementKey: 'state',
|
|
28
|
+
searchables: [{ entityType: 'stdoc', fields: ['title'], table: 'state_docs', idColumn: 'id' }],
|
|
29
|
+
lists: [
|
|
30
|
+
{ entityType: 'stdoc', sortable: ['title', 'id'], filterable: ['owner'], table: 'state_docs', idColumn: 'id' },
|
|
31
|
+
{ entityType: 'stnote', sortable: ['title'], table: 'state_notes', idColumn: 'id' },
|
|
32
|
+
{ entityType: 'stplain', sortable: ['title'], table: 'state_plain', idColumn: 'id' },
|
|
33
|
+
],
|
|
34
|
+
entityStates: [
|
|
35
|
+
{
|
|
36
|
+
entityType: 'stdoc',
|
|
37
|
+
archivePermission: 'doc:archive',
|
|
38
|
+
trashPermission: 'doc:trash',
|
|
39
|
+
table: 'state_docs',
|
|
40
|
+
idColumn: 'id',
|
|
41
|
+
},
|
|
42
|
+
{ entityType: 'stnote', archivePermission: 'doc:archive', table: 'state_notes', idColumn: 'id' },
|
|
43
|
+
],
|
|
44
|
+
});
|
|
45
|
+
const ref = (input) => {
|
|
46
|
+
const i = input;
|
|
47
|
+
return { entityType: i.entityType ?? 'stdoc', entityId: i.id };
|
|
48
|
+
};
|
|
49
|
+
export const stateMod = {
|
|
50
|
+
manifest: stateModManifest,
|
|
51
|
+
migrations: [
|
|
52
|
+
{
|
|
53
|
+
version: '0001-init',
|
|
54
|
+
sql: `CREATE TABLE state_docs (id TEXT PRIMARY KEY, title TEXT NOT NULL, owner TEXT NOT NULL);
|
|
55
|
+
CREATE TABLE state_notes (id TEXT PRIMARY KEY, title TEXT NOT NULL);
|
|
56
|
+
CREATE TABLE state_plain (id TEXT PRIMARY KEY, title TEXT NOT NULL);`,
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
operations: {
|
|
60
|
+
'state/add': (async (ctx, input) => {
|
|
61
|
+
const i = input;
|
|
62
|
+
const table = { stdoc: 'state_docs', stnote: 'state_notes', stplain: 'state_plain' }[i.entityType ?? 'stdoc'];
|
|
63
|
+
if (table === 'state_docs') {
|
|
64
|
+
ctx.sql.exec('INSERT INTO state_docs (id, title, owner) VALUES (?, ?, ?)', [i.id, i.title, i.owner ?? 'o1']);
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
ctx.sql.exec(`INSERT INTO ${table} (id, title) VALUES (?, ?)`, [i.id, i.title]);
|
|
68
|
+
}
|
|
69
|
+
return { id: i.id };
|
|
70
|
+
}),
|
|
71
|
+
'state/archive': (async (ctx, input) => {
|
|
72
|
+
await ctx.archive(ref(input));
|
|
73
|
+
return ctx.entityState(ref(input));
|
|
74
|
+
}),
|
|
75
|
+
'state/unarchive': (async (ctx, input) => {
|
|
76
|
+
await ctx.unarchive(ref(input));
|
|
77
|
+
return ctx.entityState(ref(input));
|
|
78
|
+
}),
|
|
79
|
+
'state/trash': (async (ctx, input) => {
|
|
80
|
+
await ctx.trash(ref(input));
|
|
81
|
+
return ctx.entityState(ref(input));
|
|
82
|
+
}),
|
|
83
|
+
'state/restore': (async (ctx, input) => {
|
|
84
|
+
await ctx.restore(ref(input));
|
|
85
|
+
return ctx.entityState(ref(input));
|
|
86
|
+
}),
|
|
87
|
+
// Transactional with the operation: an archive whose operation then throws never happened.
|
|
88
|
+
'state/archive-then-throw': (async (ctx, input) => {
|
|
89
|
+
await ctx.archive(ref(input));
|
|
90
|
+
throw new Error('the operation failed after archiving');
|
|
91
|
+
}),
|
|
92
|
+
'state/state': (async (ctx, input) => ctx.entityState(ref(input))),
|
|
93
|
+
'state/page': (async (ctx, input) => {
|
|
94
|
+
const i = input;
|
|
95
|
+
return ctx.page(i.entityType ?? 'stdoc', {
|
|
96
|
+
limit: i.limit ?? 50,
|
|
97
|
+
cursor: i.cursor,
|
|
98
|
+
filters: i.filters,
|
|
99
|
+
total: i.total,
|
|
100
|
+
view: i.view,
|
|
101
|
+
});
|
|
102
|
+
}),
|
|
103
|
+
'state/page-trashed': (async (ctx, input) => {
|
|
104
|
+
const i = input;
|
|
105
|
+
return ctx.pageTrashed(i.entityType ?? 'stdoc', {
|
|
106
|
+
limit: i.limit ?? 50,
|
|
107
|
+
cursor: i.cursor,
|
|
108
|
+
...(i.total ? { total: true } : {}),
|
|
109
|
+
});
|
|
110
|
+
}),
|
|
111
|
+
'state/search': (async (ctx, input) => {
|
|
112
|
+
const i = input;
|
|
113
|
+
return ctx.search('stdoc', i.term, i.view ? { view: i.view } : undefined);
|
|
114
|
+
}),
|
|
115
|
+
'state/search-trashed': (async (ctx, input) => ctx.searchTrashed('stdoc', input.term)),
|
|
116
|
+
'state/history': (async (ctx, input) => readHistory({ sql: ctx.sql }, ref(input)).entries),
|
|
117
|
+
// The guard's subject: any statement, run through the module's own `ctx.sql`.
|
|
118
|
+
'state/sql': (async (ctx, input) => {
|
|
119
|
+
const i = input;
|
|
120
|
+
// A write goes through `exec`, as module code would send it; a read through `query`.
|
|
121
|
+
if (!/^\s*select\b/i.test(i.sql))
|
|
122
|
+
return ctx.sql.exec(i.sql, i.params ?? []);
|
|
123
|
+
return ctx.sql.query(i.sql, i.params ?? []);
|
|
124
|
+
}),
|
|
125
|
+
/**
|
|
126
|
+
* The plan SQLite chooses for the walk `ctx.page` composes — the same `listQuery`, over the
|
|
127
|
+
* same plan, so the suite reads which index the real query uses rather than trusting that
|
|
128
|
+
* the partial index exists.
|
|
129
|
+
*/
|
|
130
|
+
'state/explain': (async (ctx, input) => {
|
|
131
|
+
const i = input;
|
|
132
|
+
const plan = listIndexPlans(stateModManifest.id, stateModManifest.lists, stateModManifest.entityStates).find((p) => p.entityType === (i.entityType ?? 'stdoc'));
|
|
133
|
+
const q = listQuery(plan, { limit: 10, view: i.view, filters: i.filters });
|
|
134
|
+
return ctx.sql
|
|
135
|
+
.query(`EXPLAIN QUERY PLAN ${q.sql}`, q.params)
|
|
136
|
+
.map((row) => row.detail);
|
|
137
|
+
}),
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
//# sourceMappingURL=entity-state-module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entity-state-module.js","sourceRoot":"","sources":["../src/entity-state-module.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type ScopeId, type TenantId } from '@substrat-run/contracts';
|
|
2
|
+
import type { ScopeHostFixture } from './scope-host-suite.js';
|
|
3
|
+
/**
|
|
4
|
+
* Raw SQL on one scope's own database, past `ctx.sql` and its guard — the adapter's test
|
|
5
|
+
* harness supplies it. Used once: to prove the trigger under the guard holds on its own.
|
|
6
|
+
*/
|
|
7
|
+
export type RawScopeSql = (tenant: TenantId, scope: ScopeId, sql: string, params?: readonly unknown[]) => Promise<void>;
|
|
8
|
+
export declare function entityStateContractSuite(adapterName: string, makeFixture: () => Promise<ScopeHostFixture>, raw: RawScopeSql): void;
|
|
9
|
+
//# sourceMappingURL=entity-state-suite.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entity-state-suite.d.ts","sourceRoot":"","sources":["../src/entity-state-suite.ts"],"names":[],"mappings":"AAYA,OAAO,EAYL,KAAK,OAAO,EACZ,KAAK,QAAQ,EACd,MAAM,yBAAyB,CAAC;AAEjC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAS9D;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,OAAO,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAExH,wBAAgB,wBAAwB,CACtC,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,OAAO,CAAC,gBAAgB,CAAC,EAC5C,GAAG,EAAE,WAAW,GACf,IAAI,CA0lBN"}
|