@bitstillery/mithril 3.8.0 → 3.9.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/index.ts +23 -3
- package/package.json +4 -4
- package/render/vnode.ts +1 -1
- package/ssrContext.ts +3 -1
- package/state.ts +26 -3
- package/store.ts +15 -15
package/index.ts
CHANGED
|
@@ -12,7 +12,15 @@ import censor from './util/censor'
|
|
|
12
12
|
import nextTick from './util/next_tick'
|
|
13
13
|
import domFor from './render/domFor'
|
|
14
14
|
import {signal, computed, effect, Signal, ComputedSignal, setSignalRedrawCallback, getSignalComponents} from './signal'
|
|
15
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
state,
|
|
17
|
+
watch,
|
|
18
|
+
registerState,
|
|
19
|
+
getRegisteredStates,
|
|
20
|
+
clearStateRegistry,
|
|
21
|
+
copyGlobalStatesToContext,
|
|
22
|
+
allowComputed,
|
|
23
|
+
} from './state'
|
|
16
24
|
|
|
17
25
|
import type {Vnode, Children, ComponentType} from './render/vnode'
|
|
18
26
|
import type {Hyperscript} from './render/hyperscript'
|
|
@@ -101,8 +109,20 @@ setSignalRedrawCallback((sig: Signal<any>) => {
|
|
|
101
109
|
})
|
|
102
110
|
|
|
103
111
|
// Export signals API
|
|
104
|
-
export {
|
|
105
|
-
|
|
112
|
+
export {
|
|
113
|
+
signal,
|
|
114
|
+
computed,
|
|
115
|
+
effect,
|
|
116
|
+
Signal,
|
|
117
|
+
ComputedSignal,
|
|
118
|
+
state,
|
|
119
|
+
watch,
|
|
120
|
+
registerState,
|
|
121
|
+
getRegisteredStates,
|
|
122
|
+
clearStateRegistry,
|
|
123
|
+
allowComputed,
|
|
124
|
+
}
|
|
125
|
+
export type {DeepPartial, State, StateArray, StateOptions, StateSignals, Unwatch} from './state'
|
|
106
126
|
|
|
107
127
|
// Export Store class
|
|
108
128
|
export {Store} from './store'
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bitstillery/mithril",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.9.0",
|
|
4
4
|
"description": "Mithril + Signals, Store and SSR",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Bitstillery",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"lint:ts": "bun run lint:ts-format && bun run lint:ts-syntax && bun run lint:ts-types",
|
|
53
53
|
"lint:ts-format": "oxfmt",
|
|
54
54
|
"lint:ts-syntax": "oxlint -c .oxlintrc.json",
|
|
55
|
-
"lint:ts-types": "
|
|
55
|
+
"lint:ts-types": "tsc --noEmit",
|
|
56
56
|
"test": "bun test",
|
|
57
57
|
"bench": "NODE_ENV=production bun run bench/index.ts",
|
|
58
58
|
"bench:hyperscript": "NODE_ENV=production bun run bench/index.ts hyperscript",
|
|
@@ -78,7 +78,6 @@
|
|
|
78
78
|
"@codemirror/theme-one-dark": "^6.1.2",
|
|
79
79
|
"@codemirror/view": "^6.39.1",
|
|
80
80
|
"@types/bun": "^1.3.9",
|
|
81
|
-
"@typescript/native-preview": "^7.0.0-dev.20260120.1",
|
|
82
81
|
"codemirror": "^6.0.1",
|
|
83
82
|
"marked": "^15.0.4",
|
|
84
83
|
"marked-gfm-heading-id": "^4.0.0",
|
|
@@ -86,6 +85,7 @@
|
|
|
86
85
|
"oxfmt": "^0.32.0",
|
|
87
86
|
"oxlint": "^1.30.0",
|
|
88
87
|
"standard-version": "^9.5.0",
|
|
89
|
-
"tippy.js": "^6.3.7"
|
|
88
|
+
"tippy.js": "^6.3.7",
|
|
89
|
+
"typescript": "^7.0.2"
|
|
90
90
|
}
|
|
91
91
|
}
|
package/render/vnode.ts
CHANGED
|
@@ -55,7 +55,7 @@ export type ComponentType<Attrs = Record<string, any>, State = any> =
|
|
|
55
55
|
*/
|
|
56
56
|
export abstract class MithrilComponent<Attrs = Record<string, any>> {
|
|
57
57
|
/** Required for JSX attribute type-checking - do not use directly */
|
|
58
|
-
private readonly __tsx_attrs!: (unknown extends Attrs ? Record<string, any> : Attrs) & {key?: string | number}
|
|
58
|
+
private readonly __tsx_attrs!: (unknown extends Attrs ? Record<string, any> : Attrs) & {key?: string | number | null}
|
|
59
59
|
|
|
60
60
|
oninit?(vnode: ComponentVnode<Attrs>): void
|
|
61
61
|
oncreate?(vnode: ComponentVnode<Attrs>): void
|
package/ssrContext.ts
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
* request's context. No globals, safe under concurrent requests.
|
|
6
6
|
* In the browser, getSSRContext() returns undefined and runWithContext just runs fn.
|
|
7
7
|
*/
|
|
8
|
+
import type {Store} from './store'
|
|
9
|
+
|
|
8
10
|
type StorageLike = {
|
|
9
11
|
getStore(): SSRAccessContext | undefined
|
|
10
12
|
run<T>(context: SSRAccessContext, fn: () => T): T
|
|
@@ -28,7 +30,7 @@ try {
|
|
|
28
30
|
* that runs inside the same runWithContext() call.
|
|
29
31
|
*/
|
|
30
32
|
export interface SSRAccessContext {
|
|
31
|
-
store?:
|
|
33
|
+
store?: Store
|
|
32
34
|
/** Per-request state registry for serialization; fresh Map per request. */
|
|
33
35
|
stateRegistry: Map<string, {state: any; initial: any}>
|
|
34
36
|
sessionId?: string
|
package/state.ts
CHANGED
|
@@ -871,11 +871,13 @@ export function state<T extends Record<string, any>>(initial: T, name?: string,
|
|
|
871
871
|
* Mapped type that adds $prop for each key, returning the Signal for that property.
|
|
872
872
|
* - Primitives: $prop => Signal<T[K]>
|
|
873
873
|
* - Nested objects: $prop => Signal<State<T[K]>>
|
|
874
|
-
* - Functions: $prop => ComputedSignal
|
|
874
|
+
* - Functions: $prop => ComputedSignal of the getter's return type
|
|
875
875
|
*/
|
|
876
876
|
export type StateSignals<T extends Record<string, any>> = {
|
|
877
|
-
|
|
878
|
-
|
|
877
|
+
// Only for declared keys: a record's index signature would otherwise gain a `$${string}` twin, and every
|
|
878
|
+
// lookup by a `string` key would read as `Value | Signal<Value>`.
|
|
879
|
+
[K in keyof T as K extends string ? (string extends K ? never : `$${K}`) : never]: T[K] extends (...args: any[]) => infer R
|
|
880
|
+
? ComputedSignal<R>
|
|
879
881
|
: T[K] extends object
|
|
880
882
|
? Signal<State<T[K]>>
|
|
881
883
|
: Signal<T[K]>
|
|
@@ -895,6 +897,27 @@ export type State<T extends Record<string, any>> = T extends (infer Elem)[]
|
|
|
895
897
|
[K in keyof T]: T[K] extends (...args: any[]) => infer R ? R : T[K] extends Record<string, any> ? State<T[K]> : T[K]
|
|
896
898
|
} & StateSignals<T>
|
|
897
899
|
|
|
900
|
+
/**
|
|
901
|
+
* A partial of a state shape at every depth, for the persistence tiers of a Store: each tier fills in
|
|
902
|
+
* part of a nested object (`saved` some keys of `exact`, `temporary` the rest). Arrays and computed
|
|
903
|
+
* getters are replaced wholesale, never merged element-wise.
|
|
904
|
+
*/
|
|
905
|
+
export type DeepPartial<T> = T extends (...args: any[]) => any
|
|
906
|
+
? T
|
|
907
|
+
: T extends readonly unknown[]
|
|
908
|
+
? T
|
|
909
|
+
: T extends object
|
|
910
|
+
? {[K in keyof T]?: DeepPartial<T[K]>}
|
|
911
|
+
: T
|
|
912
|
+
|
|
913
|
+
/**
|
|
914
|
+
* Opens the deferred-computed gate of a state built with `deferComputed` (ADR-0013) and marks its
|
|
915
|
+
* computeds dirty. The gate is a proxy trap rather than a key of the state, so it isn't on `State<T>`.
|
|
916
|
+
*/
|
|
917
|
+
export function allowComputed(stateInstance: State<any>): void {
|
|
918
|
+
;(stateInstance as unknown as {allowComputed?: () => void}).allowComputed?.()
|
|
919
|
+
}
|
|
920
|
+
|
|
898
921
|
/** Function returned by watch() to remove the watcher */
|
|
899
922
|
export type Unwatch = () => void
|
|
900
923
|
|
package/store.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {state, State, updateStateRegistry} from './state'
|
|
1
|
+
import {allowComputed, state, State, updateStateRegistry, type DeepPartial} from './state'
|
|
2
2
|
import {serializeStore, deserializeStore} from './render/ssrState'
|
|
3
3
|
|
|
4
4
|
// Helper function to restore computed properties (same as in ssrState.ts)
|
|
@@ -143,11 +143,11 @@ let storeInstanceCounter = 0
|
|
|
143
143
|
export class Store<T extends Record<string, any> = Record<string, any>> {
|
|
144
144
|
private stateInstance: State<T>
|
|
145
145
|
private templates = {
|
|
146
|
-
saved: {} as
|
|
147
|
-
temporary: {} as
|
|
148
|
-
tab: {} as
|
|
149
|
-
session: {} as
|
|
150
|
-
cookie: {} as
|
|
146
|
+
saved: {} as DeepPartial<T>,
|
|
147
|
+
temporary: {} as DeepPartial<T>,
|
|
148
|
+
tab: {} as DeepPartial<T>,
|
|
149
|
+
session: {} as DeepPartial<T>,
|
|
150
|
+
cookie: {} as DeepPartial<T>,
|
|
151
151
|
}
|
|
152
152
|
private lookup_verify_interval: number | null = null
|
|
153
153
|
private lookup_ttl: number
|
|
@@ -193,15 +193,15 @@ export class Store<T extends Record<string, any> = Record<string, any>> {
|
|
|
193
193
|
* (e.g. after $s, context, or route are ready) so computeds that depend on them can run.
|
|
194
194
|
*/
|
|
195
195
|
ready(): void {
|
|
196
|
-
|
|
196
|
+
allowComputed(this.stateInstance)
|
|
197
197
|
}
|
|
198
198
|
|
|
199
199
|
/**
|
|
200
200
|
* Merge deep on object `state`, but only the key/values in `blueprint`.
|
|
201
201
|
*/
|
|
202
|
-
blueprint(state: T, blueprint:
|
|
202
|
+
blueprint(state: T, blueprint: DeepPartial<T>): DeepPartial<T> {
|
|
203
203
|
if (state == null || typeof state !== 'object') {
|
|
204
|
-
return {} as
|
|
204
|
+
return {} as DeepPartial<T>
|
|
205
205
|
}
|
|
206
206
|
const result: any = {}
|
|
207
207
|
for (const key of Object.keys(blueprint)) {
|
|
@@ -227,7 +227,7 @@ export class Store<T extends Record<string, any> = Record<string, any>> {
|
|
|
227
227
|
result[key] = stateValue
|
|
228
228
|
}
|
|
229
229
|
}
|
|
230
|
-
return result as
|
|
230
|
+
return result as DeepPartial<T>
|
|
231
231
|
}
|
|
232
232
|
|
|
233
233
|
clean_lookup() {
|
|
@@ -297,11 +297,11 @@ export class Store<T extends Record<string, any> = Record<string, any>> {
|
|
|
297
297
|
}
|
|
298
298
|
|
|
299
299
|
load(
|
|
300
|
-
saved:
|
|
301
|
-
temporary:
|
|
302
|
-
tab:
|
|
303
|
-
session:
|
|
304
|
-
cookie:
|
|
300
|
+
saved: DeepPartial<T>,
|
|
301
|
+
temporary: DeepPartial<T>,
|
|
302
|
+
tab: DeepPartial<T> = {} as DeepPartial<T>,
|
|
303
|
+
session: DeepPartial<T> = {} as DeepPartial<T>,
|
|
304
|
+
cookie: DeepPartial<T> = {} as DeepPartial<T>,
|
|
305
305
|
) {
|
|
306
306
|
const restored_state = {
|
|
307
307
|
tab: this.get_tab_storage(this.tabStorageKey),
|