atom.io 0.6.9 → 0.7.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.
Files changed (169) hide show
  1. package/README.md +21 -2
  2. package/dist/index.d.mts +34 -421
  3. package/dist/index.d.ts +34 -421
  4. package/dist/index.js +248 -23
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +209 -4
  7. package/dist/index.mjs.map +1 -1
  8. package/internal/dist/index.d.mts +342 -0
  9. package/internal/dist/index.d.ts +342 -0
  10. package/internal/dist/index.js +1873 -0
  11. package/internal/dist/index.js.map +1 -0
  12. package/internal/dist/index.mjs +1798 -0
  13. package/internal/dist/index.mjs.map +1 -0
  14. package/internal/package.json +15 -0
  15. package/internal/src/atom/create-atom.ts +75 -0
  16. package/internal/src/atom/delete-atom.ts +10 -0
  17. package/internal/src/atom/index.ts +3 -0
  18. package/internal/src/atom/is-default.ts +37 -0
  19. package/internal/src/caching.ts +21 -0
  20. package/internal/src/families/create-atom-family.ts +59 -0
  21. package/internal/src/families/create-readonly-selector-family.ts +45 -0
  22. package/internal/src/families/create-selector-family.ts +67 -0
  23. package/internal/src/families/index.ts +3 -0
  24. package/internal/src/get-state-internal.ts +23 -0
  25. package/internal/src/index.ts +13 -0
  26. package/internal/src/mutable/create-mutable-atom-family.ts +25 -0
  27. package/internal/src/mutable/create-mutable-atom.ts +49 -0
  28. package/internal/src/mutable/get-json-token.ts +22 -0
  29. package/internal/src/mutable/get-update-token.ts +20 -0
  30. package/internal/src/mutable/index.ts +17 -0
  31. package/internal/src/mutable/is-atom-token-mutable.ts +7 -0
  32. package/internal/src/mutable/tracker-family.ts +61 -0
  33. package/internal/src/mutable/tracker.ts +164 -0
  34. package/internal/src/mutable/transceiver.ts +110 -0
  35. package/internal/src/operation.ts +68 -0
  36. package/internal/src/selector/create-read-write-selector.ts +65 -0
  37. package/internal/src/selector/create-readonly-selector.ts +49 -0
  38. package/internal/src/selector/create-selector.ts +65 -0
  39. package/internal/src/selector/index.ts +5 -0
  40. package/internal/src/selector/lookup-selector-sources.ts +20 -0
  41. package/internal/src/selector/register-selector.ts +61 -0
  42. package/internal/src/selector/trace-selector-atoms.ts +45 -0
  43. package/internal/src/selector/update-selector-atoms.ts +34 -0
  44. package/internal/src/set-state/become.ts +10 -0
  45. package/internal/src/set-state/copy-mutable-if-needed.ts +23 -0
  46. package/internal/src/set-state/copy-mutable-in-transaction.ts +59 -0
  47. package/internal/src/set-state/copy-mutable-into-new-store.ts +34 -0
  48. package/internal/src/set-state/emit-update.ts +23 -0
  49. package/internal/src/set-state/evict-downstream.ts +39 -0
  50. package/internal/src/set-state/index.ts +2 -0
  51. package/internal/src/set-state/set-atom-state.ts +38 -0
  52. package/internal/src/set-state/set-selector-state.ts +19 -0
  53. package/internal/src/set-state/set-state-internal.ts +18 -0
  54. package/internal/src/set-state/stow-update.ts +42 -0
  55. package/internal/src/store/deposit.ts +43 -0
  56. package/internal/src/store/index.ts +5 -0
  57. package/internal/src/store/lookup.ts +26 -0
  58. package/internal/src/store/store.ts +154 -0
  59. package/internal/src/store/withdraw-new-family-member.ts +53 -0
  60. package/internal/src/store/withdraw.ts +113 -0
  61. package/internal/src/subject.ts +21 -0
  62. package/internal/src/subscribe/index.ts +1 -0
  63. package/internal/src/subscribe/recall-state.ts +19 -0
  64. package/internal/src/subscribe/subscribe-to-root-atoms.ts +47 -0
  65. package/internal/src/timeline/add-atom-to-timeline.ts +189 -0
  66. package/internal/src/timeline/index.ts +3 -0
  67. package/internal/src/timeline/time-travel-internal.ts +91 -0
  68. package/internal/src/timeline/timeline-internal.ts +115 -0
  69. package/internal/src/transaction/abort-transaction.ts +12 -0
  70. package/internal/src/transaction/apply-transaction.ts +64 -0
  71. package/internal/src/transaction/build-transaction.ts +39 -0
  72. package/internal/src/transaction/index.ts +26 -0
  73. package/internal/src/transaction/redo-transaction.ts +22 -0
  74. package/internal/src/transaction/transaction-internal.ts +64 -0
  75. package/internal/src/transaction/undo-transaction.ts +22 -0
  76. package/introspection/dist/index.d.mts +3 -197
  77. package/introspection/dist/index.d.ts +3 -197
  78. package/introspection/dist/index.js +329 -4
  79. package/introspection/dist/index.js.map +1 -1
  80. package/introspection/dist/index.mjs +310 -4
  81. package/introspection/dist/index.mjs.map +1 -1
  82. package/introspection/src/attach-atom-index.ts +84 -0
  83. package/introspection/src/attach-introspection-states.ts +38 -0
  84. package/introspection/src/attach-selector-index.ts +90 -0
  85. package/introspection/src/attach-timeline-family.ts +59 -0
  86. package/introspection/src/attach-timeline-index.ts +38 -0
  87. package/introspection/src/attach-transaction-index.ts +40 -0
  88. package/introspection/src/attach-transaction-logs.ts +43 -0
  89. package/introspection/src/index.ts +20 -0
  90. package/json/dist/index.d.mts +10 -2
  91. package/json/dist/index.d.ts +10 -2
  92. package/json/dist/index.js +83 -26
  93. package/json/dist/index.js.map +1 -1
  94. package/json/dist/index.mjs +74 -3
  95. package/json/dist/index.mjs.map +1 -1
  96. package/json/src/index.ts +5 -0
  97. package/json/src/select-json-family.ts +35 -0
  98. package/json/src/select-json.ts +22 -0
  99. package/package.json +103 -63
  100. package/react/dist/index.d.mts +9 -17
  101. package/react/dist/index.d.ts +9 -17
  102. package/react/dist/index.js +44 -27
  103. package/react/dist/index.js.map +1 -1
  104. package/react/dist/index.mjs +24 -4
  105. package/react/dist/index.mjs.map +1 -1
  106. package/react/src/index.ts +2 -0
  107. package/react/src/store-context.tsx +12 -0
  108. package/react/src/store-hooks.ts +36 -0
  109. package/react-devtools/dist/index.css +50 -1
  110. package/react-devtools/dist/index.css.map +1 -1
  111. package/react-devtools/dist/index.d.mts +104 -71
  112. package/react-devtools/dist/index.d.ts +104 -71
  113. package/react-devtools/dist/index.js +2806 -44
  114. package/react-devtools/dist/index.js.map +1 -1
  115. package/react-devtools/dist/index.mjs +2775 -10
  116. package/react-devtools/dist/index.mjs.map +1 -1
  117. package/react-devtools/src/AtomIODevtools.tsx +109 -0
  118. package/react-devtools/src/Button.tsx +23 -0
  119. package/react-devtools/src/StateEditor.tsx +75 -0
  120. package/react-devtools/src/StateIndex.tsx +159 -0
  121. package/react-devtools/src/TimelineIndex.tsx +88 -0
  122. package/react-devtools/src/TransactionIndex.tsx +70 -0
  123. package/react-devtools/src/Updates.tsx +150 -0
  124. package/react-devtools/src/devtools.scss +310 -0
  125. package/react-devtools/src/index.ts +72 -0
  126. package/realtime-react/dist/index.d.mts +8 -22
  127. package/realtime-react/dist/index.d.ts +8 -22
  128. package/realtime-react/dist/index.js +87 -32
  129. package/realtime-react/dist/index.js.map +1 -1
  130. package/realtime-react/dist/index.mjs +62 -6
  131. package/realtime-react/dist/index.mjs.map +1 -1
  132. package/realtime-react/src/index.ts +7 -0
  133. package/realtime-react/src/realtime-context.tsx +29 -0
  134. package/realtime-react/src/use-pull-family-member.ts +15 -0
  135. package/realtime-react/src/use-pull-mutable-family-member.ts +20 -0
  136. package/realtime-react/src/use-pull-mutable.ts +17 -0
  137. package/realtime-react/src/use-pull.ts +15 -0
  138. package/realtime-react/src/use-push.ts +19 -0
  139. package/realtime-react/src/use-server-action.ts +18 -0
  140. package/realtime-testing/dist/index.d.mts +49 -0
  141. package/realtime-testing/dist/index.d.ts +49 -0
  142. package/realtime-testing/dist/index.js +147 -0
  143. package/realtime-testing/dist/index.js.map +1 -0
  144. package/realtime-testing/dist/index.mjs +116 -0
  145. package/realtime-testing/dist/index.mjs.map +1 -0
  146. package/realtime-testing/src/index.ts +1 -0
  147. package/realtime-testing/src/setup-realtime-test.tsx +161 -0
  148. package/src/atom.ts +64 -9
  149. package/src/index.ts +36 -32
  150. package/src/logger.ts +3 -3
  151. package/src/selector.ts +3 -3
  152. package/src/silo.ts +29 -20
  153. package/src/subscribe.ts +3 -3
  154. package/src/timeline.ts +2 -2
  155. package/transceivers/set-rtx/dist/index.d.mts +39 -0
  156. package/transceivers/set-rtx/dist/index.d.ts +39 -0
  157. package/transceivers/set-rtx/dist/index.js +213 -0
  158. package/transceivers/set-rtx/dist/index.js.map +1 -0
  159. package/transceivers/set-rtx/dist/index.mjs +211 -0
  160. package/transceivers/set-rtx/dist/index.mjs.map +1 -0
  161. package/{realtime → transceivers/set-rtx}/package.json +1 -1
  162. package/transceivers/set-rtx/src/index.ts +1 -0
  163. package/transceivers/set-rtx/src/set-rtx.ts +242 -0
  164. package/realtime/dist/index.d.mts +0 -23
  165. package/realtime/dist/index.d.ts +0 -23
  166. package/realtime/dist/index.js +0 -32
  167. package/realtime/dist/index.js.map +0 -1
  168. package/realtime/dist/index.mjs +0 -7
  169. package/realtime/dist/index.mjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/setup-realtime-test.tsx","../../../anvl/src/object/entries.ts"],"names":["clients"],"mappings":"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","sourcesContent":["import * as http from \"http\"\n\nimport { type RenderResult, prettyDOM, render } from \"@testing-library/react\"\nimport * as AtomIO from \"atom.io\"\nimport * as Internal from \"atom.io/internal\"\nimport * as AR from \"atom.io/react\"\nimport * as RTC from \"atom.io/realtime-react\"\nimport * as Happy from \"happy-dom\"\nimport * as React from \"react\"\nimport * as SocketIO from \"socket.io\"\nimport type { Socket as ClientSocket } from \"socket.io-client\"\nimport { io } from \"socket.io-client\"\n\nimport { recordToEntries } from \"~/packages/anvl/src/object\"\n\nexport type TestSetupOptions = {\n\tserver: (tools: { socket: SocketIO.Socket; silo: AtomIO.Silo }) => void\n}\nexport type TestSetupOptions__SingleClient = TestSetupOptions & {\n\tclient: React.FC\n}\nexport type TestSetupOptions__MultiClient<ClientNames extends string> =\n\tTestSetupOptions & {\n\t\tclients: {\n\t\t\t[K in ClientNames]: React.FC\n\t\t}\n\t}\n\nexport type RealtimeTestTools = {\n\tname: string\n\tsilo: AtomIO.Silo\n\tdispose: () => void\n}\nexport type RealtimeTestClient = RealtimeTestTools & {\n\trenderResult: RenderResult\n\tprettyPrint: () => void\n\treconnect: () => void\n\tdisconnect: () => void\n}\nexport type RealtimeTestServer = RealtimeTestTools & {\n\tport: number\n}\n\nexport type RealtimeTestAPI = {\n\tserver: RealtimeTestServer\n\tteardown: () => void\n}\nexport type RealtimeTestAPI__SingleClient = RealtimeTestAPI & {\n\tclient: RealtimeTestClient\n}\nexport type RealtimeTestAPI__MultiClient<ClientNames extends string> =\n\tRealtimeTestAPI & {\n\t\tclients: Record<ClientNames, RealtimeTestClient>\n\t}\n\nexport const setupRealtimeTestServer = (\n\toptions: TestSetupOptions,\n): RealtimeTestServer => {\n\tconst httpServer = http.createServer((_, res) => res.end(`Hello World!`))\n\tconst address = httpServer.listen().address()\n\tconst port =\n\t\ttypeof address === `string` ? 80 : address === null ? null : address.port\n\tif (port === null) throw new Error(`Could not determine port for test server`)\n\tconst server = new SocketIO.Server(httpServer)\n\tconst silo = new AtomIO.Silo(`SERVER`, Internal.IMPLICIT.STORE)\n\n\tserver.on(`connection`, (socket: SocketIO.Socket) => {\n\t\toptions.server({ socket, silo })\n\t})\n\n\tconst dispose = () => {\n\t\tserver.close()\n\t\tInternal.clearStore(silo.store)\n\t}\n\n\treturn {\n\t\tname: `SERVER`,\n\t\tsilo,\n\t\tdispose,\n\t\tport,\n\t}\n}\nexport const setupRealtimeTestClient = (\n\toptions: TestSetupOptions__SingleClient,\n\tname: string,\n\tport: number,\n): RealtimeTestClient => {\n\tconst socket: ClientSocket = io(`http://localhost:${port}/`)\n\tconst silo = new AtomIO.Silo(name, Internal.IMPLICIT.STORE)\n\n\tconst { document } = new Happy.Window()\n\tdocument.body.innerHTML = `<div id=\"app\"></div>`\n\tconst renderResult = render(\n\t\t<AR.StoreProvider store={silo.store}>\n\t\t\t<RTC.RealtimeProvider socket={socket}>\n\t\t\t\t<options.client />\n\t\t\t</RTC.RealtimeProvider>\n\t\t</AR.StoreProvider>,\n\t\t{\n\t\t\tcontainer: document.querySelector(`#app`) as unknown as HTMLElement,\n\t\t},\n\t)\n\n\tconst prettyPrint = () => console.log(prettyDOM(renderResult.container))\n\n\tconst disconnect = () => socket.disconnect()\n\tconst reconnect = () => socket.connect()\n\n\tconst dispose = () => {\n\t\tsocket.disconnect()\n\t\tInternal.clearStore(silo.store)\n\t}\n\n\treturn {\n\t\tname,\n\t\tsilo,\n\t\trenderResult,\n\t\tprettyPrint,\n\t\tdisconnect,\n\t\treconnect,\n\t\tdispose,\n\t}\n}\n\nexport const singleClient = (\n\toptions: TestSetupOptions__SingleClient,\n): RealtimeTestAPI__SingleClient => {\n\tconst server = setupRealtimeTestServer(options)\n\tconst client = setupRealtimeTestClient(options, `CLIENT`, server.port)\n\n\treturn {\n\t\tclient,\n\t\tserver,\n\t\tteardown: () => {\n\t\t\tclient.dispose()\n\t\t\tserver.dispose()\n\t\t},\n\t}\n}\n\nexport const multiClient = <ClientNames extends string>(\n\toptions: TestSetupOptions__MultiClient<ClientNames>,\n): RealtimeTestAPI__MultiClient<ClientNames> => {\n\tconst server = setupRealtimeTestServer(options)\n\tconst clients = recordToEntries(options.clients).reduce(\n\t\t(clients, [name, client]) => ({\n\t\t\t...clients,\n\t\t\t[name]: setupRealtimeTestClient({ ...options, client }, name, server.port),\n\t\t}),\n\t\t{} as Record<ClientNames, RealtimeTestClient>,\n\t)\n\n\treturn {\n\t\tclients,\n\t\tserver,\n\t\tteardown: () => {\n\t\t\trecordToEntries(clients).forEach(([, client]) => client.dispose())\n\t\t\tserver.dispose()\n\t\t},\n\t}\n}\n","export type Entries<K extends keyof any, V> = [key: K, value: V][]\n\nexport const recordToEntries = <K extends keyof any, V>(\n\tobj: Record<K, V>,\n): Entries<K, V> => Object.entries(obj) as Entries<K, V>\n\nexport const entriesToRecord = <K extends keyof any, V>(\n\tentries: Entries<K, V>,\n): Record<K, V> => Object.fromEntries(entries) as Record<K, V>\n"]}
@@ -0,0 +1 @@
1
+ export * from "./setup-realtime-test"
@@ -0,0 +1,161 @@
1
+ import * as http from "http"
2
+
3
+ import { type RenderResult, prettyDOM, render } from "@testing-library/react"
4
+ import * as AtomIO from "atom.io"
5
+ import * as Internal from "atom.io/internal"
6
+ import * as AR from "atom.io/react"
7
+ import * as RTC from "atom.io/realtime-react"
8
+ import * as Happy from "happy-dom"
9
+ import * as React from "react"
10
+ import * as SocketIO from "socket.io"
11
+ import type { Socket as ClientSocket } from "socket.io-client"
12
+ import { io } from "socket.io-client"
13
+
14
+ import { recordToEntries } from "~/packages/anvl/src/object"
15
+
16
+ export type TestSetupOptions = {
17
+ server: (tools: { socket: SocketIO.Socket; silo: AtomIO.Silo }) => void
18
+ }
19
+ export type TestSetupOptions__SingleClient = TestSetupOptions & {
20
+ client: React.FC
21
+ }
22
+ export type TestSetupOptions__MultiClient<ClientNames extends string> =
23
+ TestSetupOptions & {
24
+ clients: {
25
+ [K in ClientNames]: React.FC
26
+ }
27
+ }
28
+
29
+ export type RealtimeTestTools = {
30
+ name: string
31
+ silo: AtomIO.Silo
32
+ dispose: () => void
33
+ }
34
+ export type RealtimeTestClient = RealtimeTestTools & {
35
+ renderResult: RenderResult
36
+ prettyPrint: () => void
37
+ reconnect: () => void
38
+ disconnect: () => void
39
+ }
40
+ export type RealtimeTestServer = RealtimeTestTools & {
41
+ port: number
42
+ }
43
+
44
+ export type RealtimeTestAPI = {
45
+ server: RealtimeTestServer
46
+ teardown: () => void
47
+ }
48
+ export type RealtimeTestAPI__SingleClient = RealtimeTestAPI & {
49
+ client: RealtimeTestClient
50
+ }
51
+ export type RealtimeTestAPI__MultiClient<ClientNames extends string> =
52
+ RealtimeTestAPI & {
53
+ clients: Record<ClientNames, RealtimeTestClient>
54
+ }
55
+
56
+ export const setupRealtimeTestServer = (
57
+ options: TestSetupOptions,
58
+ ): RealtimeTestServer => {
59
+ const httpServer = http.createServer((_, res) => res.end(`Hello World!`))
60
+ const address = httpServer.listen().address()
61
+ const port =
62
+ typeof address === `string` ? 80 : address === null ? null : address.port
63
+ if (port === null) throw new Error(`Could not determine port for test server`)
64
+ const server = new SocketIO.Server(httpServer)
65
+ const silo = new AtomIO.Silo(`SERVER`, Internal.IMPLICIT.STORE)
66
+
67
+ server.on(`connection`, (socket: SocketIO.Socket) => {
68
+ options.server({ socket, silo })
69
+ })
70
+
71
+ const dispose = () => {
72
+ server.close()
73
+ Internal.clearStore(silo.store)
74
+ }
75
+
76
+ return {
77
+ name: `SERVER`,
78
+ silo,
79
+ dispose,
80
+ port,
81
+ }
82
+ }
83
+ export const setupRealtimeTestClient = (
84
+ options: TestSetupOptions__SingleClient,
85
+ name: string,
86
+ port: number,
87
+ ): RealtimeTestClient => {
88
+ const socket: ClientSocket = io(`http://localhost:${port}/`)
89
+ const silo = new AtomIO.Silo(name, Internal.IMPLICIT.STORE)
90
+
91
+ const { document } = new Happy.Window()
92
+ document.body.innerHTML = `<div id="app"></div>`
93
+ const renderResult = render(
94
+ <AR.StoreProvider store={silo.store}>
95
+ <RTC.RealtimeProvider socket={socket}>
96
+ <options.client />
97
+ </RTC.RealtimeProvider>
98
+ </AR.StoreProvider>,
99
+ {
100
+ container: document.querySelector(`#app`) as unknown as HTMLElement,
101
+ },
102
+ )
103
+
104
+ const prettyPrint = () => console.log(prettyDOM(renderResult.container))
105
+
106
+ const disconnect = () => socket.disconnect()
107
+ const reconnect = () => socket.connect()
108
+
109
+ const dispose = () => {
110
+ socket.disconnect()
111
+ Internal.clearStore(silo.store)
112
+ }
113
+
114
+ return {
115
+ name,
116
+ silo,
117
+ renderResult,
118
+ prettyPrint,
119
+ disconnect,
120
+ reconnect,
121
+ dispose,
122
+ }
123
+ }
124
+
125
+ export const singleClient = (
126
+ options: TestSetupOptions__SingleClient,
127
+ ): RealtimeTestAPI__SingleClient => {
128
+ const server = setupRealtimeTestServer(options)
129
+ const client = setupRealtimeTestClient(options, `CLIENT`, server.port)
130
+
131
+ return {
132
+ client,
133
+ server,
134
+ teardown: () => {
135
+ client.dispose()
136
+ server.dispose()
137
+ },
138
+ }
139
+ }
140
+
141
+ export const multiClient = <ClientNames extends string>(
142
+ options: TestSetupOptions__MultiClient<ClientNames>,
143
+ ): RealtimeTestAPI__MultiClient<ClientNames> => {
144
+ const server = setupRealtimeTestServer(options)
145
+ const clients = recordToEntries(options.clients).reduce(
146
+ (clients, [name, client]) => ({
147
+ ...clients,
148
+ [name]: setupRealtimeTestClient({ ...options, client }, name, server.port),
149
+ }),
150
+ {} as Record<ClientNames, RealtimeTestClient>,
151
+ )
152
+
153
+ return {
154
+ clients,
155
+ server,
156
+ teardown: () => {
157
+ recordToEntries(clients).forEach(([, client]) => client.dispose())
158
+ server.dispose()
159
+ },
160
+ }
161
+ }
package/src/atom.ts CHANGED
@@ -1,8 +1,13 @@
1
- import type { Subject } from "atom.io/internal"
2
- import { atomFamily__INTERNAL, atom__INTERNAL } from "atom.io/internal"
3
- import type { Json } from "atom.io/json"
1
+ import type { Subject, Transceiver } from "atom.io/internal"
2
+ import {
3
+ createAtom,
4
+ createAtomFamily,
5
+ createMutableAtom,
6
+ createMutableAtomFamily,
7
+ } from "atom.io/internal"
8
+ import type { Json, JsonInterface } from "atom.io/json"
4
9
 
5
- import type { AtomToken } from "."
10
+ import type { AtomToken, MutableAtomToken } from "."
6
11
 
7
12
  export type Effectors<T> = {
8
13
  setSelf: <V extends T>(next: V | ((oldValue: T) => V)) => void
@@ -15,11 +20,27 @@ export type AtomOptions<T> = {
15
20
  key: string
16
21
  default: T | (() => T)
17
22
  effects?: AtomEffect<T>[]
18
- mutable?: boolean
19
23
  }
24
+ // biome-ignore format: complex intersection
25
+ export type MutableAtomOptions<T extends Transceiver<any>, J extends Json.Serializable> =
26
+ & JsonInterface<T, J>
27
+ & Omit<AtomOptions<T>, `default`>
28
+ & {
29
+ default: () => T
30
+ mutable: true
31
+ }
20
32
 
21
- export function atom<T>(options: AtomOptions<T>): AtomToken<T> {
22
- return atom__INTERNAL<T>(options)
33
+ export function atom<T extends Transceiver<any>, J extends Json.Serializable>(
34
+ options: MutableAtomOptions<T, J>,
35
+ ): MutableAtomToken<T, J>
36
+ export function atom<T>(options: AtomOptions<T>): AtomToken<T>
37
+ export function atom<T>(
38
+ options: AtomOptions<any> | MutableAtomOptions<any, any>,
39
+ ): AtomToken<any> {
40
+ if (`mutable` in options) {
41
+ return createMutableAtom(options)
42
+ }
43
+ return createAtom<T>(options)
23
44
  }
24
45
 
25
46
  export type AtomFamilyOptions<T, K extends Json.Serializable> = {
@@ -35,9 +56,43 @@ export type AtomFamily<T, K extends Json.Serializable = Json.Serializable> = ((
35
56
  type: `atom_family`
36
57
  subject: Subject<AtomToken<T>>
37
58
  }
59
+ // biome-ignore format: intersection
60
+ export type MutableAtomFamilyOptions<
61
+ T extends Transceiver<any>,
62
+ J extends Json.Serializable,
63
+ K extends Json.Serializable,
64
+ > =
65
+ & AtomFamilyOptions<T, K>
66
+ & JsonInterface<T, J>
67
+ & { mutable: true }
38
68
 
69
+ // biome-ignore format: intersection
70
+ export type MutableAtomFamily<
71
+ Core extends Transceiver<any>,
72
+ SerializableCore extends Json.Serializable,
73
+ Key extends Json.Serializable,
74
+ > =
75
+ & JsonInterface<Core, SerializableCore>
76
+ & ((key: Key) => MutableAtomToken<Core, SerializableCore>)
77
+ & {
78
+ key: `${string}::mutable`
79
+ type: `atom_family`
80
+ subject: Subject<MutableAtomToken<Core, SerializableCore>>
81
+ }
82
+
83
+ export function atomFamily<
84
+ T extends Transceiver<any>,
85
+ J extends Json.Serializable,
86
+ K extends Json.Serializable,
87
+ >(options: MutableAtomFamilyOptions<T, J, K>): MutableAtomFamily<T, J, K>
39
88
  export function atomFamily<T, K extends Json.Serializable>(
40
89
  options: AtomFamilyOptions<T, K>,
41
- ): AtomFamily<T, K> {
42
- return atomFamily__INTERNAL<T, K>(options)
90
+ ): AtomFamily<T, K>
91
+ export function atomFamily<T, K extends Json.Serializable>(
92
+ options: AtomFamilyOptions<T, K> | MutableAtomFamilyOptions<any, any, any>,
93
+ ): AtomFamily<T, K> | MutableAtomFamily<any, any, any> {
94
+ if (`mutable` in options) {
95
+ return createMutableAtomFamily(options)
96
+ }
97
+ return createAtomFamily<T, K>(options)
43
98
  }
package/src/index.ts CHANGED
@@ -1,31 +1,23 @@
1
- import {
2
- IMPLICIT,
3
- closeOperation,
4
- getState__INTERNAL,
5
- isAtomDefault,
6
- isSelectorDefault,
7
- openOperation,
8
- setState__INTERNAL,
9
- withdraw,
10
- } from "atom.io/internal"
11
- import * as __INTERNAL__ from "atom.io/internal"
12
- import type { Store } from "atom.io/internal"
13
-
14
- import type { ƒn } from "~/packages/anvl/src/function"
15
- import { capitalize } from "~/packages/anvl/src/string/capitalize"
16
-
17
- export { ƒn }
1
+ import * as IO from "atom.io/internal"
2
+ import type { Store, Transceiver } from "atom.io/internal"
3
+ import type { Json } from "atom.io/json"
18
4
 
19
5
  export * from "./atom"
20
6
  export * from "./logger"
21
7
  export * from "./selector"
22
8
  export * from "./silo"
23
- export * from "./subscribe"
9
+ export {
10
+ subscribe,
11
+ subscribeToTimeline,
12
+ subscribeToTransaction,
13
+ KeyedStateUpdate,
14
+ StateUpdate,
15
+ UpdateHandler,
16
+ } from "./subscribe"
24
17
  export * from "./timeline"
25
18
  export * from "./transaction"
26
- export { __INTERNAL__ }
27
- export type { Store } from "atom.io/internal"
28
- export type { Json } from "atom.io/json"
19
+
20
+ export type ƒn = (...parameters: any[]) => any
29
21
 
30
22
  export type AtomToken<_> = {
31
23
  key: string
@@ -33,6 +25,13 @@ export type AtomToken<_> = {
33
25
  family?: FamilyMetadata
34
26
  __brand?: _
35
27
  }
28
+ export interface MutableAtomToken<
29
+ T extends Transceiver<any>,
30
+ J extends Json.Serializable,
31
+ > extends AtomToken<T> {
32
+ __asJSON?: J
33
+ __update?: T extends Transceiver<infer Update> ? Update : never
34
+ }
36
35
  export type SelectorToken<_> = {
37
36
  key: string
38
37
  type: `selector`
@@ -53,11 +52,15 @@ export type FamilyMetadata = {
53
52
  subKey: string
54
53
  }
55
54
 
55
+ export const capitalize = (str: string): string =>
56
+ str[0].toUpperCase() + str.slice(1)
57
+
56
58
  export const getState = <T>(
57
59
  token: ReadonlySelectorToken<T> | StateToken<T>,
58
- store: Store = IMPLICIT.STORE,
60
+ store: Store = IO.IMPLICIT.STORE,
59
61
  ): T => {
60
- const state = withdraw<T>(token, store)
62
+ const state =
63
+ IO.withdraw(token, store) ?? IO.withdrawNewFamilyMember(token, store)
61
64
  if (state === null) {
62
65
  throw new Error(
63
66
  `${capitalize(token.type)} "${token.key}" not found in store "${
@@ -65,23 +68,24 @@ export const getState = <T>(
65
68
  }".`,
66
69
  )
67
70
  }
68
- return getState__INTERNAL(state, store)
71
+ return IO.getState__INTERNAL(state, store)
69
72
  }
70
73
 
71
74
  export const setState = <T, New extends T>(
72
75
  token: StateToken<T>,
73
76
  value: New | ((oldValue: T) => New),
74
- store: Store = IMPLICIT.STORE,
77
+ store: Store = IO.IMPLICIT.STORE,
75
78
  ): void => {
76
79
  try {
77
- openOperation(token, store)
80
+ IO.openOperation(token, store)
78
81
  } catch (thrown) {
79
82
  if (!(typeof thrown === `symbol`)) {
80
83
  throw thrown
81
84
  }
82
85
  return
83
86
  }
84
- const state = withdraw(token, store)
87
+ const state =
88
+ IO.withdraw(token, store) ?? IO.withdrawNewFamilyMember(token, store)
85
89
  if (state === null) {
86
90
  throw new Error(
87
91
  `${capitalize(token.type)} "${token.key}" not found in store "${
@@ -89,14 +93,14 @@ export const setState = <T, New extends T>(
89
93
  }".`,
90
94
  )
91
95
  }
92
- setState__INTERNAL(state, value, store)
93
- closeOperation(store)
96
+ IO.setState__INTERNAL(state, value, store)
97
+ IO.closeOperation(store)
94
98
  }
95
99
 
96
100
  export const isDefault = (
97
101
  token: ReadonlySelectorToken<unknown> | StateToken<unknown>,
98
- store: Store = IMPLICIT.STORE,
102
+ store: Store = IO.IMPLICIT.STORE,
99
103
  ): boolean =>
100
104
  token.type === `atom`
101
- ? isAtomDefault(token.key, store)
102
- : isSelectorDefault(token.key, store)
105
+ ? IO.isAtomDefault(token.key, store)
106
+ : IO.isSelectorDefault(token.key, store)
package/src/logger.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { IMPLICIT } from "atom.io/internal"
2
2
  import type { Store } from "atom.io/internal"
3
3
 
4
- import { doNothing } from "~/packages/anvl/src/function"
4
+ export const NO_OP = (): void => undefined
5
5
 
6
6
  export type Logger = Pick<Console, `error` | `info` | `warn`>
7
7
  export const LOG_LEVELS: ReadonlyArray<keyof Logger> = [
@@ -22,7 +22,7 @@ export const setLogLevel = (
22
22
  LOG_LEVELS.forEach((logLevel) => {
23
23
  if (LOG_LEVELS.indexOf(logLevel) < LOG_LEVELS.indexOf(preferredLevel)) {
24
24
  // biome-ignore lint/style/noNonNullAssertion: we just set it
25
- store.config.logger![logLevel] = doNothing
25
+ store.config.logger![logLevel] = NO_OP
26
26
  } else {
27
27
  // biome-ignore lint/style/noNonNullAssertion: we just set it
28
28
  store.config.logger![logLevel] = logger__INTERNAL[logLevel]
@@ -39,7 +39,7 @@ export const useLogger = (
39
39
  store.config.logger === null
40
40
  ? null
41
41
  : LOG_LEVELS.find(
42
- (logLevel) => store.config.logger?.[logLevel] !== doNothing,
42
+ (logLevel) => store.config.logger?.[logLevel] !== NO_OP,
43
43
  ) ?? null
44
44
  store.config.logger__INTERNAL = { ...logger }
45
45
  setLogLevel(currentLogLevel, store)
package/src/selector.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { Subject } from "atom.io/internal"
2
- import { selectorFamily__INTERNAL, selector__INTERNAL } from "atom.io/internal"
2
+ import { createSelector, createSelectorFamily } from "atom.io/internal"
3
3
  import type { Json } from "atom.io/json"
4
4
 
5
5
  import type { ReadonlySelectorToken, SelectorToken } from "."
@@ -22,7 +22,7 @@ export function selector<T>(
22
22
  export function selector<T>(
23
23
  options: ReadonlySelectorOptions<T> | SelectorOptions<T>,
24
24
  ): ReadonlySelectorToken<T> | SelectorToken<T> {
25
- return selector__INTERNAL(options)
25
+ return createSelector(options)
26
26
  }
27
27
 
28
28
  export type SelectorFamilyOptions<T, K extends Json.Serializable> = {
@@ -62,5 +62,5 @@ export function selectorFamily<T, K extends Json.Serializable>(
62
62
  export function selectorFamily<T, K extends Json.Serializable>(
63
63
  options: ReadonlySelectorFamilyOptions<T, K> | SelectorFamilyOptions<T, K>,
64
64
  ): ReadonlySelectorFamily<T, K> | SelectorFamily<T, K> {
65
- return selectorFamily__INTERNAL(options)
65
+ return createSelectorFamily(options)
66
66
  }
package/src/silo.ts CHANGED
@@ -1,10 +1,12 @@
1
1
  import {
2
2
  Store,
3
- atomFamily__INTERNAL,
4
- atom__INTERNAL,
3
+ createAtom,
4
+ createAtomFamily,
5
+ createMutableAtom,
6
+ createMutableAtomFamily,
7
+ createSelector,
8
+ createSelectorFamily,
5
9
  redo__INTERNAL,
6
- selectorFamily__INTERNAL,
7
- selector__INTERNAL,
8
10
  timeline__INTERNAL,
9
11
  transaction__INTERNAL,
10
12
  undo__INTERNAL,
@@ -30,21 +32,28 @@ export class Silo {
30
32
  public undo: typeof undo
31
33
  public redo: typeof redo
32
34
  public constructor(name: string, fromStore: Store | null = null) {
33
- const store = new Store(name, fromStore)
34
- this.store = store
35
- this.atom = (options) => atom__INTERNAL(options, undefined, store)
36
- this.atomFamily = (options) => atomFamily__INTERNAL(options, store)
37
- this.selector = (options) =>
38
- selector__INTERNAL(options, undefined, store) as any
39
- this.selectorFamily = (options) =>
40
- selectorFamily__INTERNAL(options, store) as any
41
- this.transaction = (options) => transaction__INTERNAL(options, store)
42
- this.timeline = (options) => timeline__INTERNAL(options, store)
43
- this.getState = (token) => getState(token, store)
44
- this.setState = (token, newValue) => setState(token, newValue, store)
45
- ;(this.subscribe = (token, handler, key) =>
46
- subscribe(token, handler, key, store)),
47
- (this.undo = (token) => undo__INTERNAL(token, store))
48
- this.redo = (token) => redo__INTERNAL(token, store)
35
+ const s = new Store(name, fromStore)
36
+ this.store = s
37
+ this.atom = (options) => {
38
+ if (`mutable` in options) {
39
+ return createMutableAtom(options, s)
40
+ }
41
+ return createAtom(options, undefined, s)
42
+ }
43
+ this.atomFamily = (options) => {
44
+ if (`mutable` in options) {
45
+ return createMutableAtomFamily(options, s) as any
46
+ }
47
+ return createAtomFamily(options, s)
48
+ }
49
+ this.selector = (options) => createSelector(options, undefined, s) as any
50
+ this.selectorFamily = (options) => createSelectorFamily(options, s) as any
51
+ this.transaction = (options) => transaction__INTERNAL(options, s)
52
+ this.timeline = (options) => timeline__INTERNAL(options, s)
53
+ this.getState = (token) => getState(token, s)
54
+ this.setState = (token, newValue) => setState(token, newValue, s)
55
+ this.subscribe = (token, handler, key) => subscribe(token, handler, key, s)
56
+ this.undo = (token) => undo__INTERNAL(token, s)
57
+ this.redo = (token) => redo__INTERNAL(token, s)
49
58
  }
50
59
  }
package/src/subscribe.ts CHANGED
@@ -19,12 +19,12 @@ export type KeyedStateUpdate<T> = StateUpdate<T> & {
19
19
  }
20
20
  export type UpdateHandler<T> = (update: StateUpdate<T>) => void
21
21
 
22
- export const subscribe = <T>(
22
+ export function subscribe<T>(
23
23
  token: ReadonlySelectorToken<T> | StateToken<T>,
24
24
  handleUpdate: UpdateHandler<T>,
25
25
  key: string = Math.random().toString(36).slice(2),
26
26
  store: Store = IMPLICIT.STORE,
27
- ): (() => void) => {
27
+ ): () => void {
28
28
  const state = withdraw<T>(token, store)
29
29
  if (state === null) {
30
30
  throw new Error(
@@ -32,7 +32,7 @@ export const subscribe = <T>(
32
32
  )
33
33
  }
34
34
  const unsubFunction = state.subject.subscribe(key, handleUpdate)
35
- store.config.logger?.info(`👀 subscribe to "${state.key}"`)
35
+ store.config.logger?.info(`👀 adding subscription "${key}" to "${state.key}"`)
36
36
  const dependencyUnsubFunctions =
37
37
  state.type !== `atom` ? subscribeToRootAtoms(state, store) : null
38
38
 
package/src/timeline.ts CHANGED
@@ -3,12 +3,12 @@ import type {
3
3
  TimelineSelectorUpdate,
4
4
  TimelineTransactionUpdate,
5
5
  } from "atom.io/internal"
6
- import { IMPLICIT } from "atom.io/internal"
7
6
  import {
7
+ IMPLICIT,
8
8
  redo__INTERNAL,
9
9
  timeline__INTERNAL,
10
10
  undo__INTERNAL,
11
- } from "atom.io/internal/"
11
+ } from "atom.io/internal"
12
12
 
13
13
  import type { AtomFamily, AtomToken } from "."
14
14
 
@@ -0,0 +1,39 @@
1
+ import { Transceiver, TransceiverMode, Subject } from 'atom.io/internal';
2
+ import { primitive, Json } from 'atom.io/json';
3
+
4
+ type SetUpdate = `add:${string}` | `clear:${string}` | `del:${string}` | `tx:${string}`;
5
+ type NumberedSetUpdate = `${number}=${SetUpdate}`;
6
+ interface SetRTXJson<P extends primitive> extends Json.Object {
7
+ members: P[];
8
+ cache: (NumberedSetUpdate | null)[];
9
+ cacheLimit: number;
10
+ cacheIdx: number;
11
+ cacheUpdateNumber: number;
12
+ }
13
+ declare class SetRTX<P extends primitive> extends Set<P> implements Transceiver<NumberedSetUpdate> {
14
+ mode: TransceiverMode;
15
+ readonly subject: Subject<SetUpdate>;
16
+ cacheLimit: number;
17
+ cache: (NumberedSetUpdate | null)[];
18
+ cacheIdx: number;
19
+ cacheUpdateNumber: number;
20
+ constructor(values?: Set<P> | readonly P[] | null, cacheLimit?: number);
21
+ toJSON(): SetRTXJson<P>;
22
+ static fromJSON<P extends primitive>(json: SetRTXJson<P>): SetRTX<P>;
23
+ add(value: P): this;
24
+ clear(): void;
25
+ delete(value: P): boolean;
26
+ readonly parent: SetRTX<P> | null;
27
+ child: SetRTX<P> | null;
28
+ transactionUpdates: SetUpdate[] | null;
29
+ transaction(run: (child: SetRTX<P>) => boolean): void;
30
+ protected _subscribe(key: string, fn: (update: SetUpdate) => void): () => void;
31
+ subscribe(key: string, fn: (update: NumberedSetUpdate) => void): () => void;
32
+ emit(update: SetUpdate): void;
33
+ private doStep;
34
+ do(update: NumberedSetUpdate): number | `OUT_OF_RANGE` | null;
35
+ undoStep(update: SetUpdate): void;
36
+ undo(update: NumberedSetUpdate): number | null;
37
+ }
38
+
39
+ export { NumberedSetUpdate, SetRTX, SetRTXJson, SetUpdate };
@@ -0,0 +1,39 @@
1
+ import { Transceiver, TransceiverMode, Subject } from 'atom.io/internal';
2
+ import { primitive, Json } from 'atom.io/json';
3
+
4
+ type SetUpdate = `add:${string}` | `clear:${string}` | `del:${string}` | `tx:${string}`;
5
+ type NumberedSetUpdate = `${number}=${SetUpdate}`;
6
+ interface SetRTXJson<P extends primitive> extends Json.Object {
7
+ members: P[];
8
+ cache: (NumberedSetUpdate | null)[];
9
+ cacheLimit: number;
10
+ cacheIdx: number;
11
+ cacheUpdateNumber: number;
12
+ }
13
+ declare class SetRTX<P extends primitive> extends Set<P> implements Transceiver<NumberedSetUpdate> {
14
+ mode: TransceiverMode;
15
+ readonly subject: Subject<SetUpdate>;
16
+ cacheLimit: number;
17
+ cache: (NumberedSetUpdate | null)[];
18
+ cacheIdx: number;
19
+ cacheUpdateNumber: number;
20
+ constructor(values?: Set<P> | readonly P[] | null, cacheLimit?: number);
21
+ toJSON(): SetRTXJson<P>;
22
+ static fromJSON<P extends primitive>(json: SetRTXJson<P>): SetRTX<P>;
23
+ add(value: P): this;
24
+ clear(): void;
25
+ delete(value: P): boolean;
26
+ readonly parent: SetRTX<P> | null;
27
+ child: SetRTX<P> | null;
28
+ transactionUpdates: SetUpdate[] | null;
29
+ transaction(run: (child: SetRTX<P>) => boolean): void;
30
+ protected _subscribe(key: string, fn: (update: SetUpdate) => void): () => void;
31
+ subscribe(key: string, fn: (update: NumberedSetUpdate) => void): () => void;
32
+ emit(update: SetUpdate): void;
33
+ private doStep;
34
+ do(update: NumberedSetUpdate): number | `OUT_OF_RANGE` | null;
35
+ undoStep(update: SetUpdate): void;
36
+ undo(update: NumberedSetUpdate): number | null;
37
+ }
38
+
39
+ export { NumberedSetUpdate, SetRTX, SetRTXJson, SetUpdate };