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.
@@ -1,4 +1,4 @@
1
- import type { Flat, Range } from "atom.io/internal"
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 Range<E[`length`]>]: E[P] extends [K, infer V] ? V : never
11
- }[Range<E[`length`]>]
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.29.4",
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.8",
60
+ "@types/react": "18.3.11",
61
61
  "@types/tmp": "0.2.6",
62
- "@typescript-eslint/parser": "8.6.0",
63
- "@typescript-eslint/rule-tester": "8.6.0",
64
- "@vitest/coverage-v8": "2.1.1",
65
- "@vitest/ui": "2.1.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.0",
70
- "framer-motion": "11.5.6",
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.0",
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.7.5",
80
- "socket.io-client": "4.7.5",
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.7",
85
+ "vite": "5.4.8",
86
86
  "vite-tsconfig-paths": "5.0.1",
87
- "vitest": "2.1.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((token) => `key` in token && !token.key.startsWith(`\u{1F441}\u200D\u{1F5E8}`)).map((update, index) => {
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((token) => `key` in token && !token.key.startsWith(`\u{1F441}\u200D\u{1F5E8}`)).map((update, index) => {
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) => `key` in update ? /* @__PURE__ */ jsxs(Fragment$1, { children: [
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
- `key` in update ? (
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((token) => `key` in token && !token.key.startsWith(`👁‍🗨`))
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((token) => `key` in token && !token.key.startsWith(`👁‍🗨`))
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
  }
@@ -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<MoleculeCreation<M> | MoleculeDisposal>
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 makeRootMolecule(
126
+ export function makeRootMoleculeInStore(
125
127
  key: string,
126
128
  store: Store = IMPLICIT.STORE,
127
129
  ): MoleculeToken<ObjectConstructor> {
@@ -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 MoleculeCreation<M extends MoleculeConstructor> = {
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 MoleculeDisposal = {
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>