atom.io 0.29.4 → 0.30.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/{chunk-TCINPEYE.js → chunk-7PUUHSXC.js} +314 -104
- package/dist/chunk-ZKG6ZA4I.js +20 -0
- package/dist/index.d.ts +23 -6
- package/dist/index.js +3 -17
- package/internal/dist/index.d.ts +15 -7
- package/internal/dist/index.js +2 -1
- package/internal/src/atom/dispose-atom.ts +4 -8
- package/internal/src/index.ts +1 -1
- package/internal/src/ingest-updates/ingest-creation-disposal.ts +71 -27
- package/internal/src/molecule/create-molecule-family.ts +2 -2
- package/internal/src/molecule/dispose-molecule.ts +4 -2
- package/internal/src/molecule/make-molecule-in-store.ts +8 -8
- package/internal/src/molecule/molecule-internal.ts +11 -7
- package/internal/src/store/store.ts +6 -2
- package/internal/src/timeline/create-timeline.ts +99 -71
- package/internal/src/utility-types.ts +9 -2
- package/json/dist/index.d.ts +3 -3
- package/json/dist/index.js +2 -1
- package/json/src/entries.ts +3 -3
- package/package.json +13 -13
- package/react-devtools/dist/index.js +9 -5
- package/react-devtools/src/TimelineIndex.tsx +4 -1
- package/react-devtools/src/Updates.tsx +18 -3
- package/realtime-testing/dist/index.d.ts +2 -2
- package/realtime-testing/dist/index.js +6 -6
- package/realtime-testing/src/setup-realtime-test.tsx +8 -9
- package/src/allocate.ts +278 -0
- package/src/molecule.ts +4 -2
- package/src/transaction.ts +22 -4
package/json/src/entries.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Count, Flat } from "atom.io/internal"
|
|
2
2
|
|
|
3
3
|
export type Entries<K extends PropertyKey = keyof any, V = any> = [K, V][]
|
|
4
4
|
|
|
@@ -7,8 +7,8 @@ export type KeyOfEntries<E extends Entries> = E extends [infer K, any][]
|
|
|
7
7
|
: never
|
|
8
8
|
|
|
9
9
|
export type ValueOfEntry<E extends Entries, K extends KeyOfEntries<E>> = {
|
|
10
|
-
[P in
|
|
11
|
-
}[
|
|
10
|
+
[P in Count<E[`length`]>]: E[P] extends [K, infer V] ? V : never
|
|
11
|
+
}[Count<E[`length`]>]
|
|
12
12
|
|
|
13
13
|
export type FromEntries<E extends Entries> = Flat<{
|
|
14
14
|
[K in KeyOfEntries<E>]: ValueOfEntry<E, K>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "atom.io",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.30.0",
|
|
4
4
|
"description": "Composable and testable reactive data library.",
|
|
5
5
|
"homepage": "https://atom.io.fyi",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -57,34 +57,34 @@
|
|
|
57
57
|
"@types/estree": "1.0.6",
|
|
58
58
|
"@types/http-proxy": "1.17.15",
|
|
59
59
|
"@types/npmlog": "7.0.0",
|
|
60
|
-
"@types/react": "18.3.
|
|
60
|
+
"@types/react": "18.3.11",
|
|
61
61
|
"@types/tmp": "0.2.6",
|
|
62
|
-
"@typescript-eslint/parser": "8.
|
|
63
|
-
"@typescript-eslint/rule-tester": "8.
|
|
64
|
-
"@vitest/coverage-v8": "2.1.
|
|
65
|
-
"@vitest/ui": "2.1.
|
|
62
|
+
"@typescript-eslint/parser": "8.8.0",
|
|
63
|
+
"@typescript-eslint/rule-tester": "8.8.0",
|
|
64
|
+
"@vitest/coverage-v8": "2.1.2",
|
|
65
|
+
"@vitest/ui": "2.1.2",
|
|
66
66
|
"concurrently": "9.0.1",
|
|
67
67
|
"drizzle-kit": "0.24.2",
|
|
68
68
|
"drizzle-orm": "0.33.0",
|
|
69
|
-
"eslint": "9.11.
|
|
70
|
-
"framer-motion": "11.
|
|
69
|
+
"eslint": "9.11.1",
|
|
70
|
+
"framer-motion": "11.11.0",
|
|
71
71
|
"happy-dom": "15.7.4",
|
|
72
72
|
"http-proxy": "1.18.1",
|
|
73
73
|
"npmlog": "7.0.1",
|
|
74
74
|
"postgres": "3.4.4",
|
|
75
|
-
"preact": "10.24.
|
|
75
|
+
"preact": "10.24.2",
|
|
76
76
|
"react": "18.3.1",
|
|
77
77
|
"react-dom": "18.3.1",
|
|
78
78
|
"react-router-dom": "6.26.2",
|
|
79
|
-
"socket.io": "4.
|
|
80
|
-
"socket.io-client": "4.
|
|
79
|
+
"socket.io": "4.8.0",
|
|
80
|
+
"socket.io-client": "4.8.0",
|
|
81
81
|
"tmp": "0.2.3",
|
|
82
82
|
"tsup": "8.3.0",
|
|
83
83
|
"tsx": "4.19.1",
|
|
84
84
|
"typescript": "5.6.2",
|
|
85
|
-
"vite": "5.4.
|
|
85
|
+
"vite": "5.4.8",
|
|
86
86
|
"vite-tsconfig-paths": "5.0.1",
|
|
87
|
-
"vitest": "2.1.
|
|
87
|
+
"vitest": "2.1.2",
|
|
88
88
|
"zod": "3.23.8"
|
|
89
89
|
},
|
|
90
90
|
"main": "dist/index.js",
|
|
@@ -6,7 +6,7 @@ import { undo, redo, getState } from 'atom.io';
|
|
|
6
6
|
import { IMPLICIT, createStandaloneAtom, createAtomFamily, findInStore, become } from 'atom.io/internal';
|
|
7
7
|
import { jsonRefinery, attachIntrospectionStates, primitiveRefinery, prettyJson, discoverType } from 'atom.io/introspection';
|
|
8
8
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
9
|
-
import { isJson, fromEntries, toEntries, JSON_DEFAULTS } from 'atom.io/json';
|
|
9
|
+
import { isJson, fromEntries, toEntries, JSON_DEFAULTS, stringifyJson } from 'atom.io/json';
|
|
10
10
|
import { persistSync } from 'atom.io/web';
|
|
11
11
|
|
|
12
12
|
var OpenClose = ({ isOpen, setIsOpen, disabled, testid }) => {
|
|
@@ -1131,7 +1131,9 @@ var TransactionUpdateFC = ({ serialNumber, transactionUpdate }) => {
|
|
|
1131
1131
|
] }),
|
|
1132
1132
|
/* @__PURE__ */ jsxs("section", { className: "transaction_impact", children: [
|
|
1133
1133
|
/* @__PURE__ */ jsx("span", { className: "detail", children: "impact: " }),
|
|
1134
|
-
transactionUpdate.updates.filter(
|
|
1134
|
+
transactionUpdate.updates.filter(
|
|
1135
|
+
(token) => token.type !== `molecule_creation` && token.type !== `molecule_disposal` && token.type !== `state_creation` && token.type !== `state_disposal` && !token.key.startsWith(`\u{1F441}\u200D\u{1F5E8}`)
|
|
1136
|
+
).map((update, index) => {
|
|
1135
1137
|
switch (update.type) {
|
|
1136
1138
|
case `atom_update`:
|
|
1137
1139
|
case `selector_update`:
|
|
@@ -1165,7 +1167,7 @@ var TimelineUpdateFC = ({ timelineUpdate, serialNumber }) => {
|
|
|
1165
1167
|
"article",
|
|
1166
1168
|
{
|
|
1167
1169
|
className: "node timeline_update",
|
|
1168
|
-
"data-testid": `timeline-update-${timelineUpdate.key}-${serialNumber}`,
|
|
1170
|
+
"data-testid": `timeline-update-${typeof timelineUpdate.key === `string` ? timelineUpdate.key : stringifyJson(timelineUpdate.key)}-${serialNumber}`,
|
|
1169
1171
|
children: [
|
|
1170
1172
|
/* @__PURE__ */ jsx("header", { children: /* @__PURE__ */ jsxs("h4", { children: [
|
|
1171
1173
|
timelineUpdate.timestamp,
|
|
@@ -1175,7 +1177,9 @@ var TimelineUpdateFC = ({ timelineUpdate, serialNumber }) => {
|
|
|
1175
1177
|
timelineUpdate.key,
|
|
1176
1178
|
")"
|
|
1177
1179
|
] }) }),
|
|
1178
|
-
/* @__PURE__ */ jsx("main", { children: timelineUpdate.type === `transaction_update` ? timelineUpdate.updates.filter(
|
|
1180
|
+
/* @__PURE__ */ jsx("main", { children: timelineUpdate.type === `transaction_update` ? timelineUpdate.updates.filter(
|
|
1181
|
+
(token) => token.type !== `molecule_creation` && token.type !== `molecule_disposal` && token.type !== `state_creation` && token.type !== `state_disposal` && !token.key.startsWith(`\u{1F441}\u200D\u{1F5E8}`)
|
|
1182
|
+
).map((update, index) => {
|
|
1179
1183
|
switch (update.type) {
|
|
1180
1184
|
case `atom_update`:
|
|
1181
1185
|
case `selector_update`:
|
|
@@ -1276,7 +1280,7 @@ var TimelineLog = ({ token, isOpenState, timelineState }) => {
|
|
|
1276
1280
|
] })
|
|
1277
1281
|
] }),
|
|
1278
1282
|
isOpen ? /* @__PURE__ */ jsx("main", { children: timeline.history.map(
|
|
1279
|
-
(update, index) => `
|
|
1283
|
+
(update, index) => update.type !== `molecule_creation` && update.type !== `molecule_disposal` && update.type !== `state_creation` && update.type !== `state_disposal` ? /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
1280
1284
|
index === timeline.at ? /* @__PURE__ */ jsx(YouAreHere, {}) : null,
|
|
1281
1285
|
/* @__PURE__ */ jsx(
|
|
1282
1286
|
article.TimelineUpdate,
|
|
@@ -64,7 +64,10 @@ export const TimelineLog: FC<{
|
|
|
64
64
|
{isOpen ? (
|
|
65
65
|
<main>
|
|
66
66
|
{timeline.history.map((update, index) =>
|
|
67
|
-
`
|
|
67
|
+
update.type !== `molecule_creation` &&
|
|
68
|
+
update.type !== `molecule_disposal` &&
|
|
69
|
+
update.type !== `state_creation` &&
|
|
70
|
+
update.type !== `state_disposal` ? (
|
|
68
71
|
<Fragment key={update.key + index + timeline.at}>
|
|
69
72
|
{index === timeline.at ? <YouAreHere /> : null}
|
|
70
73
|
<article.TimelineUpdate
|
|
@@ -5,6 +5,7 @@ import type {
|
|
|
5
5
|
} from "atom.io"
|
|
6
6
|
import type { Func } from "atom.io/internal"
|
|
7
7
|
import { discoverType, prettyJson } from "atom.io/introspection"
|
|
8
|
+
import { stringifyJson } from "atom.io/json"
|
|
8
9
|
import * as React from "react"
|
|
9
10
|
|
|
10
11
|
/* eslint-disable no-console */
|
|
@@ -90,7 +91,14 @@ const TransactionUpdateFC: React.FC<{
|
|
|
90
91
|
<section className="transaction_impact">
|
|
91
92
|
<span className="detail">impact: </span>
|
|
92
93
|
{transactionUpdate.updates
|
|
93
|
-
.filter(
|
|
94
|
+
.filter(
|
|
95
|
+
(token) =>
|
|
96
|
+
token.type !== `molecule_creation` &&
|
|
97
|
+
token.type !== `molecule_disposal` &&
|
|
98
|
+
token.type !== `state_creation` &&
|
|
99
|
+
token.type !== `state_disposal` &&
|
|
100
|
+
!token.key.startsWith(`👁🗨`),
|
|
101
|
+
)
|
|
94
102
|
.map((update, index) => {
|
|
95
103
|
switch (update.type) {
|
|
96
104
|
case `atom_update`:
|
|
@@ -125,7 +133,7 @@ export const TimelineUpdateFC: React.FC<{
|
|
|
125
133
|
return `key` in timelineUpdate ? (
|
|
126
134
|
<article
|
|
127
135
|
className="node timeline_update"
|
|
128
|
-
data-testid={`timeline-update-${timelineUpdate.key}-${serialNumber}`}
|
|
136
|
+
data-testid={`timeline-update-${typeof timelineUpdate.key === `string` ? timelineUpdate.key : stringifyJson(timelineUpdate.key)}-${serialNumber}`}
|
|
129
137
|
>
|
|
130
138
|
<header>
|
|
131
139
|
<h4>
|
|
@@ -136,7 +144,14 @@ export const TimelineUpdateFC: React.FC<{
|
|
|
136
144
|
<main>
|
|
137
145
|
{timelineUpdate.type === `transaction_update` ? (
|
|
138
146
|
timelineUpdate.updates
|
|
139
|
-
.filter(
|
|
147
|
+
.filter(
|
|
148
|
+
(token) =>
|
|
149
|
+
token.type !== `molecule_creation` &&
|
|
150
|
+
token.type !== `molecule_disposal` &&
|
|
151
|
+
token.type !== `state_creation` &&
|
|
152
|
+
token.type !== `state_disposal` &&
|
|
153
|
+
!token.key.startsWith(`👁🗨`),
|
|
154
|
+
)
|
|
140
155
|
.map((update, index) => {
|
|
141
156
|
switch (update.type) {
|
|
142
157
|
case `atom_update`:
|
|
@@ -35,12 +35,12 @@ type RealtimeTestClientBuilder = {
|
|
|
35
35
|
init: () => RealtimeTestClient;
|
|
36
36
|
};
|
|
37
37
|
type RealtimeTestServer = RealtimeTestTools & {
|
|
38
|
-
dispose: () => void
|
|
38
|
+
dispose: () => Promise<void>;
|
|
39
39
|
port: number;
|
|
40
40
|
};
|
|
41
41
|
type RealtimeTestAPI = {
|
|
42
42
|
server: RealtimeTestServer;
|
|
43
|
-
teardown: () => void
|
|
43
|
+
teardown: () => Promise<void>;
|
|
44
44
|
};
|
|
45
45
|
type RealtimeTestAPI__SingleClient = RealtimeTestAPI & {
|
|
46
46
|
client: RealtimeTestClientBuilder;
|
|
@@ -81,8 +81,8 @@ var setupRealtimeTestServer = (options) => {
|
|
|
81
81
|
console.log(`${userKey} disconnected`);
|
|
82
82
|
});
|
|
83
83
|
});
|
|
84
|
-
const dispose = () => {
|
|
85
|
-
server.close();
|
|
84
|
+
const dispose = async () => {
|
|
85
|
+
await server.close();
|
|
86
86
|
const roomKeys = getFromStore(silo.store, RT.roomIndex);
|
|
87
87
|
for (const roomKey of roomKeys) {
|
|
88
88
|
const roomState = findInStore(silo.store, RTS.roomSelectors, roomKey);
|
|
@@ -157,8 +157,8 @@ var singleClient = (options) => {
|
|
|
157
157
|
return {
|
|
158
158
|
client,
|
|
159
159
|
server,
|
|
160
|
-
teardown: () => {
|
|
161
|
-
server.dispose();
|
|
160
|
+
teardown: async () => {
|
|
161
|
+
await server.dispose();
|
|
162
162
|
client.dispose();
|
|
163
163
|
}
|
|
164
164
|
};
|
|
@@ -179,8 +179,8 @@ var multiClient = (options) => {
|
|
|
179
179
|
return {
|
|
180
180
|
clients,
|
|
181
181
|
server,
|
|
182
|
-
teardown: () => {
|
|
183
|
-
server.dispose();
|
|
182
|
+
teardown: async () => {
|
|
183
|
+
await server.dispose();
|
|
184
184
|
for (const [, client] of toEntries(clients)) {
|
|
185
185
|
client.dispose();
|
|
186
186
|
}
|
|
@@ -76,14 +76,13 @@ export type RealtimeTestClientBuilder = {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
export type RealtimeTestServer = RealtimeTestTools & {
|
|
79
|
-
dispose: () => void
|
|
79
|
+
dispose: () => Promise<void>
|
|
80
80
|
port: number
|
|
81
|
-
// enableLogging: () => void
|
|
82
81
|
}
|
|
83
82
|
|
|
84
83
|
export type RealtimeTestAPI = {
|
|
85
84
|
server: RealtimeTestServer
|
|
86
|
-
teardown: () => void
|
|
85
|
+
teardown: () => Promise<void>
|
|
87
86
|
}
|
|
88
87
|
export type RealtimeTestAPI__SingleClient = RealtimeTestAPI & {
|
|
89
88
|
client: RealtimeTestClientBuilder
|
|
@@ -152,8 +151,8 @@ export const setupRealtimeTestServer = (
|
|
|
152
151
|
})
|
|
153
152
|
})
|
|
154
153
|
|
|
155
|
-
const dispose = () => {
|
|
156
|
-
server.close()
|
|
154
|
+
const dispose = async () => {
|
|
155
|
+
await server.close()
|
|
157
156
|
const roomKeys = getFromStore(silo.store, RT.roomIndex)
|
|
158
157
|
for (const roomKey of roomKeys) {
|
|
159
158
|
const roomState = findInStore(silo.store, RTS.roomSelectors, roomKey)
|
|
@@ -245,8 +244,8 @@ export const singleClient = (
|
|
|
245
244
|
return {
|
|
246
245
|
client,
|
|
247
246
|
server,
|
|
248
|
-
teardown: () => {
|
|
249
|
-
server.dispose()
|
|
247
|
+
teardown: async () => {
|
|
248
|
+
await server.dispose()
|
|
250
249
|
client.dispose()
|
|
251
250
|
},
|
|
252
251
|
}
|
|
@@ -271,8 +270,8 @@ export const multiClient = <ClientNames extends string>(
|
|
|
271
270
|
return {
|
|
272
271
|
clients,
|
|
273
272
|
server,
|
|
274
|
-
teardown: () => {
|
|
275
|
-
server.dispose()
|
|
273
|
+
teardown: async () => {
|
|
274
|
+
await server.dispose()
|
|
276
275
|
for (const [, client] of toEntries(clients)) {
|
|
277
276
|
client.dispose()
|
|
278
277
|
}
|
package/src/allocate.ts
ADDED
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import type { Each, Store } from "atom.io/internal"
|
|
2
|
+
import {
|
|
3
|
+
disposeFromStore,
|
|
4
|
+
isChildStore,
|
|
5
|
+
Molecule,
|
|
6
|
+
newest,
|
|
7
|
+
} from "atom.io/internal"
|
|
8
|
+
import type { Canonical } from "atom.io/json"
|
|
9
|
+
import { stringifyJson } from "atom.io/json"
|
|
10
|
+
|
|
11
|
+
import { makeRootMoleculeInStore } from "./molecule"
|
|
12
|
+
import type {
|
|
13
|
+
MoleculeCreationModern,
|
|
14
|
+
MoleculeDisposalModern,
|
|
15
|
+
} from "./transaction"
|
|
16
|
+
|
|
17
|
+
export const $provenance = Symbol(`provenance`)
|
|
18
|
+
export type Claim<
|
|
19
|
+
H extends Hierarchy,
|
|
20
|
+
V extends Vassal<H>,
|
|
21
|
+
A extends Above<V, H>,
|
|
22
|
+
> = V & {
|
|
23
|
+
[$provenance]?: A
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function allocateIntoStore<
|
|
27
|
+
H extends Hierarchy,
|
|
28
|
+
V extends Vassal<H>,
|
|
29
|
+
A extends Above<V, H>,
|
|
30
|
+
>(store: Store, provenance: A, key: V): Claim<H, V, A> {
|
|
31
|
+
const stringKey = stringifyJson(key)
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const above: Molecule<any>[] = []
|
|
35
|
+
|
|
36
|
+
let allocationAttachmentStyle: `all` | `any`
|
|
37
|
+
if (provenance === `root`) {
|
|
38
|
+
// biome-ignore lint/style/noNonNullAssertion: let's assume we made the root molecule to get here
|
|
39
|
+
above.push(store.molecules.get(`"root"`)!)
|
|
40
|
+
allocationAttachmentStyle = `all`
|
|
41
|
+
} else if (provenance[0][0] === T$) {
|
|
42
|
+
allocationAttachmentStyle = `any`
|
|
43
|
+
const provenanceKey = stringifyJson(provenance as Canonical)
|
|
44
|
+
const provenanceMolecule = store.molecules.get(provenanceKey)
|
|
45
|
+
if (!provenanceMolecule) {
|
|
46
|
+
throw new Error(
|
|
47
|
+
`Molecule ${provenanceKey} not found in store "${store.config.name}"`,
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
above.push(provenanceMolecule)
|
|
51
|
+
} else {
|
|
52
|
+
const allocationIsCompound = key[0][0] === T$
|
|
53
|
+
if (allocationIsCompound) {
|
|
54
|
+
allocationAttachmentStyle = `all`
|
|
55
|
+
for (const claim of provenance as SingularTypedKey[]) {
|
|
56
|
+
const provenanceKey = stringifyJson(claim)
|
|
57
|
+
const provenanceMolecule = store.molecules.get(provenanceKey)
|
|
58
|
+
if (!provenanceMolecule) {
|
|
59
|
+
throw new Error(
|
|
60
|
+
`Molecule ${provenanceKey} not found in store "${store.config.name}"`,
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
above.push(provenanceMolecule)
|
|
64
|
+
}
|
|
65
|
+
} else {
|
|
66
|
+
allocationAttachmentStyle = `any`
|
|
67
|
+
const provenanceKey = stringifyJson(provenance as Canonical)
|
|
68
|
+
const provenanceMolecule = store.molecules.get(provenanceKey)
|
|
69
|
+
if (!provenanceMolecule) {
|
|
70
|
+
throw new Error(
|
|
71
|
+
`Molecule ${provenanceKey} not found in store "${store.config.name}"`,
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
above.push(provenanceMolecule)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const molecule = new Molecule(above, key)
|
|
79
|
+
molecule._dependsOn = allocationAttachmentStyle
|
|
80
|
+
|
|
81
|
+
store.molecules.set(stringKey, molecule)
|
|
82
|
+
|
|
83
|
+
for (const aboveMolecule of above) {
|
|
84
|
+
aboveMolecule.below.set(molecule.stringKey, molecule)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const creationEvent: MoleculeCreationModern = {
|
|
88
|
+
type: `molecule_creation`,
|
|
89
|
+
subType: `modern`,
|
|
90
|
+
key: molecule.key,
|
|
91
|
+
provenance: provenance as Canonical,
|
|
92
|
+
}
|
|
93
|
+
const target = newest(store)
|
|
94
|
+
const isTransaction =
|
|
95
|
+
isChildStore(target) && target.transactionMeta.phase === `building`
|
|
96
|
+
if (isTransaction) {
|
|
97
|
+
target.transactionMeta.update.updates.push(creationEvent)
|
|
98
|
+
} else {
|
|
99
|
+
target.on.moleculeCreationStart.next(creationEvent)
|
|
100
|
+
}
|
|
101
|
+
} catch (thrown) {
|
|
102
|
+
if (thrown instanceof Error) {
|
|
103
|
+
store.logger.error(
|
|
104
|
+
`❌`,
|
|
105
|
+
`molecule`,
|
|
106
|
+
stringKey,
|
|
107
|
+
`allocation failed:`,
|
|
108
|
+
thrown.message,
|
|
109
|
+
)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return key as Claim<H, V, A>
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function deallocateFromStore<
|
|
117
|
+
H extends Hierarchy,
|
|
118
|
+
V extends Vassal<H>,
|
|
119
|
+
A extends Above<V, H>,
|
|
120
|
+
>(store: Store, claim: Claim<H, V, A>): void {
|
|
121
|
+
const stringKey = stringifyJson(claim)
|
|
122
|
+
const molecule = store.molecules.get(stringKey)
|
|
123
|
+
if (!molecule) {
|
|
124
|
+
throw new Error(
|
|
125
|
+
`Molecule ${stringKey} not found in store "${store.config.name}"`,
|
|
126
|
+
)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
for (const join of molecule.joins.values()) {
|
|
130
|
+
join.relations.delete(molecule.key)
|
|
131
|
+
join.molecules.delete(molecule.stringKey)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
let provenance: Canonical
|
|
135
|
+
if (molecule.above.size === 1) {
|
|
136
|
+
const above = molecule.above.values().next().value
|
|
137
|
+
provenance = above.key
|
|
138
|
+
} else {
|
|
139
|
+
provenance = [...molecule.above.values()].map(({ key }) => key)
|
|
140
|
+
}
|
|
141
|
+
const values: [string, any][] = []
|
|
142
|
+
for (const stateToken of molecule.tokens.values()) {
|
|
143
|
+
// biome-ignore lint/style/noNonNullAssertion: tokens of molecules must have a family
|
|
144
|
+
const tokenFamily = stateToken.family!
|
|
145
|
+
values.push([tokenFamily.key, store.valueMap.get(stateToken.key)])
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
for (const state of molecule.tokens.values()) {
|
|
149
|
+
disposeFromStore(store, state)
|
|
150
|
+
}
|
|
151
|
+
for (const child of molecule.below.values()) {
|
|
152
|
+
if (child.dependsOn === `all`) {
|
|
153
|
+
deallocateFromStore(store, child.key)
|
|
154
|
+
} else {
|
|
155
|
+
child.above.delete(molecule.stringKey)
|
|
156
|
+
if (child.above.size === 0) {
|
|
157
|
+
deallocateFromStore(store, child.key)
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
molecule.below.clear()
|
|
162
|
+
|
|
163
|
+
const disposalEvent: MoleculeDisposalModern = {
|
|
164
|
+
type: `molecule_disposal`,
|
|
165
|
+
subType: `modern`,
|
|
166
|
+
key: molecule.key,
|
|
167
|
+
values,
|
|
168
|
+
provenance,
|
|
169
|
+
}
|
|
170
|
+
const target = newest(store)
|
|
171
|
+
const isTransaction =
|
|
172
|
+
isChildStore(target) && target.transactionMeta.phase === `building`
|
|
173
|
+
if (isTransaction) {
|
|
174
|
+
target.transactionMeta.update.updates.push(disposalEvent)
|
|
175
|
+
} else {
|
|
176
|
+
target.on.moleculeDisposal.next(disposalEvent)
|
|
177
|
+
}
|
|
178
|
+
target.molecules.delete(molecule.stringKey)
|
|
179
|
+
|
|
180
|
+
for (const parent of molecule.above.values()) {
|
|
181
|
+
parent.below.delete(molecule.stringKey)
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
186
|
+
export function realm<H extends Hierarchy>(store: Store) {
|
|
187
|
+
const root = makeRootMoleculeInStore(`root`, store)
|
|
188
|
+
return {
|
|
189
|
+
root,
|
|
190
|
+
allocate: <V extends Vassal<H>, A extends Above<V, H>>(
|
|
191
|
+
provenance: A,
|
|
192
|
+
key: V,
|
|
193
|
+
): Claim<H, V, A> => {
|
|
194
|
+
return allocateIntoStore(store, provenance, key)
|
|
195
|
+
},
|
|
196
|
+
deallocate: <V extends Vassal<H>, A extends Above<V, H>>(
|
|
197
|
+
claim: Claim<H, V, A>,
|
|
198
|
+
): void => {
|
|
199
|
+
deallocateFromStore(store, claim)
|
|
200
|
+
},
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export const T$ = `T$`
|
|
205
|
+
export type T$ = typeof T$
|
|
206
|
+
export type TypeTag<T extends string> = [T$, T]
|
|
207
|
+
export type SingularTypedKey<T extends string = string> = [T, string]
|
|
208
|
+
export type CompoundTypedKey<
|
|
209
|
+
A extends string = string,
|
|
210
|
+
B extends string = string,
|
|
211
|
+
C extends string = string,
|
|
212
|
+
> = [TypeTag<A>, TypedKey<B>, TypedKey<C>]
|
|
213
|
+
export type TypedKey<
|
|
214
|
+
A extends string = string,
|
|
215
|
+
B extends string = string,
|
|
216
|
+
C extends string = string,
|
|
217
|
+
> = CompoundTypedKey<A, B, C> | SingularTypedKey<A>
|
|
218
|
+
type Scope = SingularTypedKey[]
|
|
219
|
+
type MutualFealty = {
|
|
220
|
+
above: Scope
|
|
221
|
+
below: CompoundTypedKey
|
|
222
|
+
style: `all` | `any`
|
|
223
|
+
}
|
|
224
|
+
type ExclusiveFealty = {
|
|
225
|
+
above: TypedKey | `root`
|
|
226
|
+
below: Scope
|
|
227
|
+
}
|
|
228
|
+
type Fealty = ExclusiveFealty | MutualFealty
|
|
229
|
+
|
|
230
|
+
export type Hierarchy<F extends Fealty[] = Fealty[]> = Each<F>
|
|
231
|
+
|
|
232
|
+
export type Vassal<H extends Hierarchy> = {
|
|
233
|
+
[K in keyof H]: H[K] extends MutualFealty
|
|
234
|
+
? H[K][`below`]
|
|
235
|
+
: H[K] extends { below: Array<infer V> }
|
|
236
|
+
? V extends TypedKey
|
|
237
|
+
? V
|
|
238
|
+
: never
|
|
239
|
+
: never
|
|
240
|
+
}[keyof H]
|
|
241
|
+
|
|
242
|
+
export type Above<TK extends TypedKey, H extends Hierarchy> = {
|
|
243
|
+
[K in keyof H]: H[K] extends MutualFealty
|
|
244
|
+
? TK extends H[K][`below`]
|
|
245
|
+
? H[K][`above`]
|
|
246
|
+
: never
|
|
247
|
+
: H[K] extends { below: Array<infer V> }
|
|
248
|
+
? TK extends V
|
|
249
|
+
? H[K] extends ExclusiveFealty
|
|
250
|
+
? H[K][`above`]
|
|
251
|
+
: never
|
|
252
|
+
: never
|
|
253
|
+
: never
|
|
254
|
+
}[keyof H]
|
|
255
|
+
|
|
256
|
+
export type Below<TK extends TypedKey | TypedKey[], H extends Hierarchy> = {
|
|
257
|
+
[K in keyof H]: H[K] extends MutualFealty
|
|
258
|
+
? TK extends H[K][`above`]
|
|
259
|
+
? H[K][`below`]
|
|
260
|
+
: TK extends H[K][`above`][number]
|
|
261
|
+
? H[K][`below`]
|
|
262
|
+
: never
|
|
263
|
+
: H[K] extends { above: infer V }
|
|
264
|
+
? TK extends V
|
|
265
|
+
? H[K] extends ExclusiveFealty
|
|
266
|
+
? H[K][`below`][number]
|
|
267
|
+
: never
|
|
268
|
+
: never
|
|
269
|
+
: never
|
|
270
|
+
}[keyof H]
|
|
271
|
+
|
|
272
|
+
export type Mutuals<TK extends TypedKey | TypedKey[], H extends Hierarchy> = {
|
|
273
|
+
[K in keyof H]: H[K] extends MutualFealty
|
|
274
|
+
? TK extends H[K][`above`][number]
|
|
275
|
+
? [mutual: Exclude<H[K][`above`][number], TK>, below: H[K][`below`]]
|
|
276
|
+
: never
|
|
277
|
+
: never
|
|
278
|
+
}[keyof H]
|
package/src/molecule.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
ActorToolkit,
|
|
3
3
|
MoleculeCreation,
|
|
4
|
+
MoleculeCreationClassic,
|
|
4
5
|
MoleculeDisposal,
|
|
6
|
+
MoleculeDisposalClassic,
|
|
5
7
|
MutableAtomFamilyToken,
|
|
6
8
|
MutableAtomToken,
|
|
7
9
|
ReadableFamilyToken,
|
|
@@ -86,7 +88,7 @@ export type MoleculeFamilyToken<M extends MoleculeConstructor> = {
|
|
|
86
88
|
}
|
|
87
89
|
export type MoleculeFamily<M extends MoleculeConstructor> = Flat<
|
|
88
90
|
MoleculeFamilyToken<M> & {
|
|
89
|
-
subject: Subject<
|
|
91
|
+
subject: Subject<MoleculeCreationClassic<M> | MoleculeDisposalClassic>
|
|
90
92
|
dependsOn: `all` | `any`
|
|
91
93
|
new: M
|
|
92
94
|
}
|
|
@@ -121,7 +123,7 @@ export type MoleculeType<T extends MoleculeFamilyToken<any>> =
|
|
|
121
123
|
: never
|
|
122
124
|
export type MoleculeKey<M extends MoleculeConstructor> = InstanceType<M>[`key`]
|
|
123
125
|
|
|
124
|
-
export function
|
|
126
|
+
export function makeRootMoleculeInStore(
|
|
125
127
|
key: string,
|
|
126
128
|
store: Store = IMPLICIT.STORE,
|
|
127
129
|
): MoleculeToken<ObjectConstructor> {
|
package/src/transaction.ts
CHANGED
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
createTransaction,
|
|
17
17
|
IMPLICIT,
|
|
18
18
|
} from "atom.io/internal"
|
|
19
|
-
import type { Json } from "atom.io/json"
|
|
19
|
+
import type { Canonical, Json } from "atom.io/json"
|
|
20
20
|
|
|
21
21
|
import type {
|
|
22
22
|
disposeState,
|
|
@@ -25,7 +25,6 @@ import type {
|
|
|
25
25
|
ReadableToken,
|
|
26
26
|
TokenType,
|
|
27
27
|
WritableSelectorToken,
|
|
28
|
-
WritableToken,
|
|
29
28
|
} from "."
|
|
30
29
|
|
|
31
30
|
export type TransactionToken<F extends Func> = {
|
|
@@ -44,20 +43,39 @@ export type StateDisposal<Token extends ReadableToken<any>> = {
|
|
|
44
43
|
value?: TokenType<Token>
|
|
45
44
|
}
|
|
46
45
|
|
|
47
|
-
export type
|
|
46
|
+
export type MoleculeCreationClassic<M extends MoleculeConstructor> = {
|
|
48
47
|
type: `molecule_creation`
|
|
48
|
+
subType: `classic`
|
|
49
49
|
token: MoleculeToken<M>
|
|
50
50
|
family: MoleculeFamilyToken<M>
|
|
51
51
|
context: MoleculeToken<any>[]
|
|
52
52
|
params: MoleculeParams<M>
|
|
53
53
|
}
|
|
54
|
-
export type
|
|
54
|
+
export type MoleculeCreationModern = {
|
|
55
|
+
type: `molecule_creation`
|
|
56
|
+
subType: `modern`
|
|
57
|
+
key: Canonical
|
|
58
|
+
provenance: Canonical
|
|
59
|
+
}
|
|
60
|
+
export type MoleculeCreation<M extends MoleculeConstructor> =
|
|
61
|
+
| MoleculeCreationClassic<M>
|
|
62
|
+
| MoleculeCreationModern
|
|
63
|
+
export type MoleculeDisposalClassic = {
|
|
55
64
|
type: `molecule_disposal`
|
|
65
|
+
subType: `classic`
|
|
56
66
|
token: MoleculeToken<any>
|
|
57
67
|
family: MoleculeFamilyToken<any>
|
|
58
68
|
context: MoleculeToken<any>[]
|
|
59
69
|
values: [key: string, value: any][]
|
|
60
70
|
}
|
|
71
|
+
export type MoleculeDisposalModern = {
|
|
72
|
+
type: `molecule_disposal`
|
|
73
|
+
subType: `modern`
|
|
74
|
+
key: Canonical
|
|
75
|
+
provenance: Canonical
|
|
76
|
+
values: [key: string, value: any][]
|
|
77
|
+
}
|
|
78
|
+
export type MoleculeDisposal = MoleculeDisposalClassic | MoleculeDisposalModern
|
|
61
79
|
|
|
62
80
|
export type TransactionUpdateContent =
|
|
63
81
|
| KeyedStateUpdate<unknown>
|