@firsthandjs/core 0.1.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/LICENSE +21 -0
- package/README.md +27 -0
- package/dist/computed.d.ts +13 -0
- package/dist/computed.d.ts.map +1 -0
- package/dist/context.d.ts +39 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/core.d.ts +103 -0
- package/dist/core.d.ts.map +1 -0
- package/dist/dev.d.ts +13 -0
- package/dist/dev.d.ts.map +1 -0
- package/dist/dev.prod.d.ts +4 -0
- package/dist/dev.prod.d.ts.map +1 -0
- package/dist/effect.d.ts +38 -0
- package/dist/effect.d.ts.map +1 -0
- package/dist/errors.d.ts +13 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/flags.d.ts +23 -0
- package/dist/flags.d.ts.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -0
- package/dist/lifecycle.d.ts +33 -0
- package/dist/lifecycle.d.ts.map +1 -0
- package/dist/signal.d.ts +17 -0
- package/dist/signal.d.ts.map +1 -0
- package/dist/types.d.ts +38 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +47 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Firsthand contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# @firsthandjs/core
|
|
2
|
+
|
|
3
|
+
The reactive graph, the owner tree and context. No DOM: this package must load
|
|
4
|
+
unchanged in a worker or on a server.
|
|
5
|
+
|
|
6
|
+
**Documentation:** [guide](https://github.com/firsthandjs/firsthand/blob/main/docs/guide/02-reactivity.md) · [API reference](https://github.com/firsthandjs/firsthand/blob/main/docs/reference/core.md) · [all docs](https://github.com/firsthandjs/firsthand/blob/main/docs/README.md)
|
|
7
|
+
|
|
8
|
+
```ts
|
|
9
|
+
import { signal, computed, effect, batch, untrack } from '@firsthandjs/core';
|
|
10
|
+
|
|
11
|
+
const count = signal(0);
|
|
12
|
+
const doubled = computed(() => count.value * 2);
|
|
13
|
+
|
|
14
|
+
effect(() => console.log(doubled.value)); // logs 0
|
|
15
|
+
count.value = 21; // logs 42, synchronously
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
- Writes push _invalidation_; values are pulled lazily on read, which makes
|
|
19
|
+
diamond dependencies glitch-free without a topological sort.
|
|
20
|
+
- Dependency edges are reusable doubly-linked objects, so a re-run with stable
|
|
21
|
+
dependencies allocates nothing.
|
|
22
|
+
- Everything is owned: disposing a scope unlinks every edge in both directions.
|
|
23
|
+
|
|
24
|
+
Full documentation: the [repository README](../../README.md),
|
|
25
|
+
[ARCHITECTURE.md](../../ARCHITECTURE.md) and [docs/adr](../../docs/adr).
|
|
26
|
+
|
|
27
|
+
MIT licensed.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { CellOptions, ReadonlyCell } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a lazy, memoised derived value.
|
|
4
|
+
*
|
|
5
|
+
* The body runs on the first read, and again only when a dependency it actually
|
|
6
|
+
* read has changed. If the recomputed value is equal to the previous one,
|
|
7
|
+
* nothing downstream is notified.
|
|
8
|
+
*
|
|
9
|
+
* The computed is registered with the enclosing scope, so disposing that scope
|
|
10
|
+
* unsubscribes it from every source — including sources that outlive it.
|
|
11
|
+
*/
|
|
12
|
+
export declare function computed<T>(fn: () => T, options?: CellOptions<T>): ReadonlyCell<T>;
|
|
13
|
+
//# sourceMappingURL=computed.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"computed.d.ts","sourceRoot":"","sources":["../src/computed.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE5D;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAIlF"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { ReadonlyCell } from './types.js';
|
|
2
|
+
declare const BRAND: unique symbol;
|
|
3
|
+
/**
|
|
4
|
+
* A typed context token. Its identity is the token object itself.
|
|
5
|
+
*
|
|
6
|
+
* The phantom member makes the token invariant in `T`, so a `Context<Theme>`
|
|
7
|
+
* cannot be passed where a `Context<User>` is expected.
|
|
8
|
+
*/
|
|
9
|
+
export interface Context<T> {
|
|
10
|
+
readonly id: symbol;
|
|
11
|
+
readonly description: string;
|
|
12
|
+
readonly [BRAND]?: (value: T) => T;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Creates a context token.
|
|
16
|
+
*
|
|
17
|
+
* Without a default value, reading the context with no provider above is a
|
|
18
|
+
* typed error rather than a silent `undefined`.
|
|
19
|
+
*/
|
|
20
|
+
export declare function createContext<T>(): Context<T>;
|
|
21
|
+
export declare function createContext<T>(defaultValue: T, description?: string): Context<T>;
|
|
22
|
+
/**
|
|
23
|
+
* Provides a value for `context` to the current scope and everything created
|
|
24
|
+
* under it afterwards.
|
|
25
|
+
*
|
|
26
|
+
* Pass a cell to keep the provider reactive; pass a plain value to provide a
|
|
27
|
+
* constant. Call `provide` before creating the children that should see it.
|
|
28
|
+
*/
|
|
29
|
+
export declare function provide<T>(context: Context<T>, value: NoInfer<T> | ReadonlyCell<NoInfer<T>>): void;
|
|
30
|
+
/**
|
|
31
|
+
* Resolves `context` once, at setup time, and returns the provider's cell.
|
|
32
|
+
*
|
|
33
|
+
* Steady-state cost of `theme.value` afterwards is exactly one signal read: no
|
|
34
|
+
* tree walk, no DOM traversal, no map lookup (ADR-0008). Because resolution
|
|
35
|
+
* follows the owner tree, it is unaffected by portals and shadow roots.
|
|
36
|
+
*/
|
|
37
|
+
export declare function useContext<T>(context: Context<T>): ReadonlyCell<T>;
|
|
38
|
+
export {};
|
|
39
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAc/C,OAAO,CAAC,MAAM,KAAK,EAAE,OAAO,MAAM,CAAC;AAEnC;;;;;GAKG;AACH,MAAM,WAAW,OAAO,CAAC,CAAC;IACxB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC;CACpC;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;AAI/C,wBAAgB,aAAa,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;AAgBpF;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,CAAC,EACvB,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAInB,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAC3C,IAAI,CASN;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAiBlE"}
|
package/dist/core.d.ts
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The reactive graph, the owner tree and the scheduler.
|
|
3
|
+
*
|
|
4
|
+
* These three live in one module because they are one algorithm: an effect run
|
|
5
|
+
* needs to reset its owner scope, and owner disposal needs to unlink graph
|
|
6
|
+
* edges. Splitting them would buy a module boundary and cost an indirection in
|
|
7
|
+
* the hottest path in the framework. The public API is layered on top in
|
|
8
|
+
* `signal.ts`, `computed.ts`, `effect.ts`, `lifecycle.ts` and `context.ts`.
|
|
9
|
+
*
|
|
10
|
+
* Model (ADR-0001): a write pushes *invalidation* through the subscriber graph
|
|
11
|
+
* and queues effects; values are pulled lazily on read. Nothing recomputes
|
|
12
|
+
* during a write.
|
|
13
|
+
*
|
|
14
|
+
* Edges (ADR-0002): every dependency is one reusable doubly-linked `Link`
|
|
15
|
+
* object participating in two intrusive lists. Re-running a node walks its
|
|
16
|
+
* existing dependency list in order and reuses links in place, so the common
|
|
17
|
+
* case allocates nothing.
|
|
18
|
+
*/
|
|
19
|
+
/** One dependency edge, shared by the source's subscriber list and the
|
|
20
|
+
* subscriber's dependency list. */
|
|
21
|
+
export declare class Link {
|
|
22
|
+
dep: Cell;
|
|
23
|
+
sub: Cell;
|
|
24
|
+
prevDep: Link | undefined;
|
|
25
|
+
nextDep: Link | undefined;
|
|
26
|
+
prevSub: Link | undefined;
|
|
27
|
+
nextSub: Link | undefined;
|
|
28
|
+
constructor(dep: Cell, sub: Cell, prevDep: Link | undefined, nextDep: Link | undefined, prevSub: Link | undefined);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* One node of the reactive graph. Signals, computeds and effects all use this
|
|
32
|
+
* class so that every property access in the graph code sees one hidden class.
|
|
33
|
+
*/
|
|
34
|
+
export declare class Cell {
|
|
35
|
+
flags: number;
|
|
36
|
+
/** Signal value, or the memoised result of `fn`. */
|
|
37
|
+
v: unknown;
|
|
38
|
+
/** Computed body, or effect body. `undefined` for signals. */
|
|
39
|
+
fn: (() => unknown) | undefined;
|
|
40
|
+
equals: (a: unknown, b: unknown) => boolean;
|
|
41
|
+
deps: Link | undefined;
|
|
42
|
+
depsTail: Link | undefined;
|
|
43
|
+
subs: Link | undefined;
|
|
44
|
+
subsTail: Link | undefined;
|
|
45
|
+
/** An effect's own scope: re-created on every run. `undefined` otherwise. */
|
|
46
|
+
scope: Owner | undefined;
|
|
47
|
+
constructor(flags: number, value: unknown, fn: (() => unknown) | undefined, equals: (a: unknown, b: unknown) => boolean);
|
|
48
|
+
get value(): unknown;
|
|
49
|
+
set value(next: unknown);
|
|
50
|
+
/** Reads without subscribing, and without refreshing lazily... except that a
|
|
51
|
+
* stale computed must still produce a correct value, so it does refresh. */
|
|
52
|
+
peek(): unknown;
|
|
53
|
+
/** Functional update. The updater runs untracked. */
|
|
54
|
+
set(updater: (previous: never) => unknown): void;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* A logical scope (ADR-0008). Owners form a tree that is independent of the
|
|
58
|
+
* DOM, which is what lets portals keep their context and disposal behaviour.
|
|
59
|
+
*/
|
|
60
|
+
export interface Owner {
|
|
61
|
+
parent: Owner | null;
|
|
62
|
+
/** Whether the owner is still in its parent's child list. */
|
|
63
|
+
attached: boolean;
|
|
64
|
+
prev: Owner | null;
|
|
65
|
+
next: Owner | null;
|
|
66
|
+
head: Owner | null;
|
|
67
|
+
tail: Owner | null;
|
|
68
|
+
/** Cleanup callbacks, in registration order. Allocated on first use. */
|
|
69
|
+
disposals: (() => void)[] | null;
|
|
70
|
+
/** Computeds and effects to unlink on disposal. Allocated on first use. */
|
|
71
|
+
cells: Cell[] | null;
|
|
72
|
+
/** Prototype-chained context record, shared by reference until `provide`. */
|
|
73
|
+
ctx: ContextRecord | null;
|
|
74
|
+
/** Error boundary installed by `catchError`. */
|
|
75
|
+
handler: ((error: unknown) => void) | null;
|
|
76
|
+
}
|
|
77
|
+
export type ContextRecord = Record<symbol, unknown>;
|
|
78
|
+
export declare function createOwner(parent: Owner | null): Owner;
|
|
79
|
+
/** Disposes an owner and detaches it from its parent. */
|
|
80
|
+
export declare function disposeOwner(owner: Owner): void;
|
|
81
|
+
/** Unsubscribes a cell in both directions and marks it permanently dead. */
|
|
82
|
+
export declare function disposeCell(cell: Cell): void;
|
|
83
|
+
/** Walks the owner tree for an error boundary; otherwise reports globally. */
|
|
84
|
+
export declare function handleError(error: unknown, owner: Owner | null): void;
|
|
85
|
+
export declare function getOwner(): Owner | null;
|
|
86
|
+
export declare function setOwner(owner: Owner | null): Owner | null;
|
|
87
|
+
/** Registers a cell with the current scope so disposal unlinks it. */
|
|
88
|
+
export declare function own(cell: Cell): void;
|
|
89
|
+
export declare function createEffectScope(cell: Cell): Owner;
|
|
90
|
+
export declare function runEffectNow(cell: Cell): void;
|
|
91
|
+
/**
|
|
92
|
+
* Retires an effect that took no dependencies on its first run.
|
|
93
|
+
*
|
|
94
|
+
* Such an effect can never be invalidated, so keeping it would cost one live
|
|
95
|
+
* object per static expression in every template. Its scope is left attached to
|
|
96
|
+
* the parent owner, because whatever the run created there must still be
|
|
97
|
+
* cleaned up with the parent.
|
|
98
|
+
*/
|
|
99
|
+
export declare function releaseEffect(cell: Cell): void;
|
|
100
|
+
export declare function defaultEquals<T>(equals: ((a: T, b: T) => boolean) | false | undefined): (a: unknown, b: unknown) => boolean;
|
|
101
|
+
export declare function batch<T>(fn: () => T): T;
|
|
102
|
+
export declare function untrack<T>(fn: () => T): T;
|
|
103
|
+
//# sourceMappingURL=core.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"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"}
|
package/dist/dev.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Development-only diagnostics.
|
|
3
|
+
*
|
|
4
|
+
* There is deliberately no `if (DEV)` guard at any call site: the production
|
|
5
|
+
* build aliases this module to `dev.prod.ts`, whose functions have empty bodies
|
|
6
|
+
* and are removed by the minifier. That keeps the production bundle free of
|
|
7
|
+
* diagnostics without creating branches that can never be taken in tests — see
|
|
8
|
+
* ARCHITECTURE section 8, item 6.
|
|
9
|
+
*/
|
|
10
|
+
export declare function devWarn(message: string): void;
|
|
11
|
+
/** Reports an error that no boundary handled, so it reaches `window.onerror`. */
|
|
12
|
+
export declare function reportUncaught(error: unknown): void;
|
|
13
|
+
//# sourceMappingURL=dev.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../src/dev.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE7C;AAED,iFAAiF;AACjF,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAInD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev.prod.d.ts","sourceRoot":"","sources":["../src/dev.prod.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAE3E,wBAAgB,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAE9C;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAInD"}
|
package/dist/effect.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Cell } from './core.js';
|
|
2
|
+
import type { Dispose } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* An effect body. Returning a function registers it as the cleanup; returning
|
|
5
|
+
* nothing is the common case, which is what the `void` in this union is for.
|
|
6
|
+
*/
|
|
7
|
+
type EffectBody = () => void | (() => void);
|
|
8
|
+
/**
|
|
9
|
+
* Creates and runs an effect, returning its node.
|
|
10
|
+
*
|
|
11
|
+
* Internal entry point used by the DOM layer, which disposes through the owner
|
|
12
|
+
* tree and therefore never needs the closure that `effect()` allocates. At
|
|
13
|
+
* 100 000 rows that closure would be 100 000 objects for nothing.
|
|
14
|
+
*/
|
|
15
|
+
export declare function createEffect(fn: EffectBody): Cell;
|
|
16
|
+
/**
|
|
17
|
+
* Runs `fn` once inside a tracking scope and keeps an effect only if the run
|
|
18
|
+
* actually read something reactive.
|
|
19
|
+
*
|
|
20
|
+
* This is how the DOM layer decides, by observation rather than by static
|
|
21
|
+
* analysis, whether a template expression is dynamic (ADR-0009). A `class` or
|
|
22
|
+
* text computed from constants costs nothing after the first evaluation.
|
|
23
|
+
*/
|
|
24
|
+
export declare function bind(fn: () => void): void;
|
|
25
|
+
/**
|
|
26
|
+
* Runs `fn` immediately, then again whenever something it read changes.
|
|
27
|
+
*
|
|
28
|
+
* Dependencies are re-observed on every run, so a conditional read genuinely
|
|
29
|
+
* drops the dependency it did not take. There is no dependency array, and
|
|
30
|
+
* nothing depends on call order.
|
|
31
|
+
*
|
|
32
|
+
* `fn` may return a cleanup function; it runs before each re-run and on
|
|
33
|
+
* disposal. Effects created inside another effect or a component are owned by
|
|
34
|
+
* it and disposed with it.
|
|
35
|
+
*/
|
|
36
|
+
export declare function effect(fn: EffectBody): Dispose;
|
|
37
|
+
export {};
|
|
38
|
+
//# sourceMappingURL=effect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"effect.d.ts","sourceRoot":"","sources":["../src/effect.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,IAAI,EAOL,MAAM,WAAW,CAAC;AAInB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C;;;GAGG;AAEH,KAAK,UAAU,GAAG,MAAM,IAAI,GAAG,CAAC,MAAM,IAAI,CAAC,CAAC;AAE5C;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,EAAE,EAAE,UAAU,GAAG,IAAI,CAMjD;AAED;;;;;;;GAOG;AACH,wBAAgB,IAAI,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI,CAKzC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,MAAM,CAAC,EAAE,EAAE,UAAU,GAAG,OAAO,CAW9C"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Thrown when reactive updates fail to settle, instead of hanging the tab. */
|
|
2
|
+
export declare class FirsthandCycleError extends Error {
|
|
3
|
+
constructor();
|
|
4
|
+
}
|
|
5
|
+
/** Thrown when a context without a default value is read with no provider above. */
|
|
6
|
+
export declare class FirsthandContextError extends Error {
|
|
7
|
+
constructor(description: string);
|
|
8
|
+
}
|
|
9
|
+
/** Thrown when a read-only reactive cell is written to. */
|
|
10
|
+
export declare class FirsthandReadonlyError extends Error {
|
|
11
|
+
constructor();
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,qBAAa,mBAAoB,SAAQ,KAAK;;CAQ7C;AAED,oFAAoF;AACpF,qBAAa,qBAAsB,SAAQ,KAAK;gBAClC,WAAW,EAAE,MAAM;CAIhC;AAED,2DAA2D;AAC3D,qBAAa,sBAAuB,SAAQ,KAAK;;CAKhD"}
|
package/dist/flags.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bit flags shared by every reactive node.
|
|
3
|
+
*
|
|
4
|
+
* All three node kinds (signal, computed, effect) use one object shape so that
|
|
5
|
+
* every access site in the graph code stays monomorphic (ADR-0002).
|
|
6
|
+
*/
|
|
7
|
+
/** The node memoises a value produced by `fn`: it is a computed. */
|
|
8
|
+
export declare const MUTABLE: number;
|
|
9
|
+
/** The node is scheduled when invalidated: it is an effect. */
|
|
10
|
+
export declare const WATCHING: number;
|
|
11
|
+
/** A direct dependency definitely changed. The node must re-evaluate. */
|
|
12
|
+
export declare const DIRTY: number;
|
|
13
|
+
/** A transitive dependency may have changed. Resolve by checking deps. */
|
|
14
|
+
export declare const PENDING: number;
|
|
15
|
+
/** The node is currently in the flush queue. */
|
|
16
|
+
export declare const QUEUED: number;
|
|
17
|
+
/** The node has been disposed; it must never run again. */
|
|
18
|
+
export declare const DISPOSED: number;
|
|
19
|
+
/** A computed has produced a value at least once. */
|
|
20
|
+
export declare const HAS_VALUE: number;
|
|
21
|
+
/** `DIRTY | PENDING`: the node is not known to be up to date. */
|
|
22
|
+
export declare const STALE: number;
|
|
23
|
+
//# sourceMappingURL=flags.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flags.d.ts","sourceRoot":"","sources":["../src/flags.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,oEAAoE;AACpE,eAAO,MAAM,OAAO,QAAS,CAAC;AAC9B,+DAA+D;AAC/D,eAAO,MAAM,QAAQ,QAAS,CAAC;AAC/B,yEAAyE;AACzE,eAAO,MAAM,KAAK,QAAS,CAAC;AAC5B,0EAA0E;AAC1E,eAAO,MAAM,OAAO,QAAS,CAAC;AAC9B,gDAAgD;AAChD,eAAO,MAAM,MAAM,QAAS,CAAC;AAC7B,2DAA2D;AAC3D,eAAO,MAAM,QAAQ,QAAS,CAAC;AAC/B,qDAAqD;AACrD,eAAO,MAAM,SAAS,QAAS,CAAC;AAEhC,iEAAiE;AACjE,eAAO,MAAM,KAAK,QAAkB,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@firsthandjs/core` — the reactive graph, the owner tree and context.
|
|
3
|
+
*
|
|
4
|
+
* This package knows nothing about the DOM: it must load unchanged in a worker
|
|
5
|
+
* or on a server.
|
|
6
|
+
*/
|
|
7
|
+
export { signal } from './signal.js';
|
|
8
|
+
export { computed } from './computed.js';
|
|
9
|
+
export { effect } from './effect.js';
|
|
10
|
+
export { batch, untrack } from './core.js';
|
|
11
|
+
export { onCleanup, createRoot, catchError, runWithOwner, getOwner } from './lifecycle.js';
|
|
12
|
+
export { createContext, provide, useContext } from './context.js';
|
|
13
|
+
export type { Context } from './context.js';
|
|
14
|
+
export { FirsthandCycleError, FirsthandContextError, FirsthandReadonlyError } from './errors.js';
|
|
15
|
+
export type { CellOptions, DeepReadonly, Dispose, ReadonlyCell, ReadonlyProps, Signal, } from './types.js';
|
|
16
|
+
export type { Owner } from './core.js';
|
|
17
|
+
/** Internal surface used by `@firsthandjs/dom`; not part of the public contract. */
|
|
18
|
+
export { createEffect, bind } from './effect.js';
|
|
19
|
+
export { Cell, createOwner, disposeOwner, disposeCell, handleError, own, setOwner, } from './core.js';
|
|
20
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC3F,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAClE,YAAY,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AACjG,YAAY,EACV,WAAW,EACX,YAAY,EACZ,OAAO,EACP,YAAY,EACZ,aAAa,EACb,MAAM,GACP,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAEvC,oFAAoF;AACpF,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EACL,IAAI,EACJ,WAAW,EACX,YAAY,EACZ,WAAW,EACX,WAAW,EACX,GAAG,EACH,QAAQ,GACT,MAAM,WAAW,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var c=class extends Error{constructor(){super("Reactive update did not settle: an effect keeps invalidating its own dependencies. Use untrack() or batch() to break the cycle."),this.name="FirsthandCycleError"}},h=class extends Error{constructor(n){super(`No provider for ${n}, and the context was created without a default value.`),this.name="FirsthandContextError"}},p=class extends Error{constructor(){super("Cannot assign to a computed value. Write to the signals it derives from instead."),this.name="FirsthandReadonlyError"}};function N(e){queueMicrotask(()=>{throw e})}var S=class{constructor(n,t,o,r,l){this.dep=n,this.sub=t,this.prevDep=o,this.nextDep=r,this.prevSub=l,this.nextSub=void 0}},K=Object.is,Z=()=>!1,i=class{constructor(n,t,o,r){this.flags=n,this.v=t,this.fn=o,this.equals=r,this.deps=void 0,this.depsTail=void 0,this.subs=void 0,this.subsTail=void 0,this.scope=void 0}get value(){let n=this.flags;return(n&1)!==0&&(n&12)!==0&&q(this),u!==void 0&&ne(this,u),this.v}set value(n){if((this.flags&1)!==0)throw new p;A(this,n)}peek(){let n=this.flags;return(n&1)!==0&&(n&12)!==0&&q(this),this.v}set(n){if((this.flags&1)!==0)throw new p;A(this,n(this.v))}},u,s=null,R=0,D=!1,m=0,v=[],ee=1e6;function ne(e,n){let t=n.depsTail;if(t!==void 0&&t.dep===e)return;let o=t!==void 0?t.nextDep:n.deps;if(o!==void 0&&o.dep===e){n.depsTail=o;return}let r=e.subsTail;if(r!==void 0&&r.sub===n&&te(r,n))return;let l=new S(e,n,t,o,r);n.depsTail=l,e.subsTail=l,o!==void 0&&(o.prevDep=l),t!==void 0?t.nextDep=l:n.deps=l,r!==void 0?r.nextSub=l:e.subs=l}function te(e,n){let t=n.depsTail;for(;t!==void 0;){if(t===e)return!0;t=t.prevDep}return!1}function L(e,n){let t=e.dep,o=e.prevDep,r=e.nextDep,l=e.prevSub,g=e.nextSub;return r!==void 0?r.prevDep=o:n.depsTail=o,o!==void 0?o.nextDep=r:n.deps=r,g!==void 0?g.prevSub=l:t.subsTail=l,l!==void 0?l.nextSub=g:t.subs=g,r}function B(e){e.depsTail=void 0,e.flags&=-13}function _(e){let n=e.depsTail,t=n!==void 0?n.nextDep:e.deps;for(;t!==void 0;)t=L(t,e)}function oe(e){let n=e;do{let t=n.sub,o=t.flags;(o&4)===0&&(t.flags=o|4,(o&8)===0&&((o&2)!==0?H(t):t.subs!==void 0&&M(t.subs))),n=n.nextSub}while(n!==void 0)}function M(e){let n=e;do{let t=n.sub,o=t.flags;(o&12)===0&&(t.flags=o|8,(o&2)!==0?H(t):t.subs!==void 0&&M(t.subs)),n=n.nextSub}while(n!==void 0)}function re(e){let n=e;do{let t=n.sub,o=t.flags;(o&12)===8&&(t.flags=o|4),n=n.nextSub}while(n!==void 0)}function A(e,n){if(e.equals(e.v,n))return;e.v=n;let t=e.subs;t!==void 0&&(oe(t),R===0&&V())}function q(e){if((e.flags&4)!==0||P(e)){if(!le(e))return!1;let t=e.subs;return t!==void 0&&t.nextSub!==void 0&&re(t),!0}return e.flags=e.flags&-9,!1}function P(e){let n=e.deps;for(;n!==void 0;){let t=n.dep;if((t.flags&1)!==0&&(t.flags&12)!==0&&q(t))return!0;n=n.nextDep}return!1}function le(e){let n=u,t=s;u=e,s=null,B(e);try{let o=e.fn();return(e.flags&64)!==0&&e.equals(e.v,o)?!1:(e.flags|=64,e.v=o,!0)}finally{_(e),u=n,s=t}}function H(e){(e.flags&16)===0&&(e.flags|=16,v.push(e))}function V(){if(D)return;D=!0;let e=0;try{for(;m<v.length;){let n=v[m];if(v[m++]=void 0,n.flags&=-17,(n.flags&32)===0){if(++e>ee)throw new c;(n.flags&4)!==0||P(n)?G(n):n.flags&=-9}}}finally{v.length=0,m=0,D=!1}}function G(e){let n=e.scope;j(n);let t=u,o=s;u=e,s=n,B(e);try{let r=e.fn();typeof r=="function"&&(n.disposals??=[]).push(r)}catch(r){x(r,n)}finally{_(e),u=t,s=o}}function T(e){let n={parent:e,attached:e!==null,prev:null,next:null,head:null,tail:null,disposals:null,cells:null,ctx:e!==null?e.ctx:null,handler:null};if(e!==null){let t=e.tail;n.prev=t,t!==null?t.next=n:e.head=n,e.tail=n}return n}function j(e){let n=e.head;for(;n!==null;){let r=n.next;n.attached=!1,n.prev=null,n.next=null,j(n),n=r}e.head=null,e.tail=null,e.ctx=e.parent!==null?e.parent.ctx:null;let t=e.cells;if(t!==null){for(let r=t.length-1;r>=0;r--)E(t[r]);e.cells=null}let o=e.disposals;if(o!==null){e.disposals=null;for(let r=o.length-1;r>=0;r--)try{o[r]()}catch(l){x(l,e)}}}function O(e){if(e.attached){let n=e.parent,t=e.prev,o=e.next;t!==null?t.next=o:n.head=o,o!==null?o.prev=t:n.tail=t,e.attached=!1,e.prev=null,e.next=null}j(e)}function E(e){e.flags|=32;let n=e.deps;for(;n!==void 0;)n=L(n,e);let t=e.subs;for(;t!==void 0;){let r=t.nextSub;L(t,t.sub),t=r}let o=e.scope;o!==void 0&&(O(o),e.scope=void 0,e.fn=void 0)}function x(e,n){if(e instanceof c)throw e;let t=n;for(;t!==null;){let o=t.handler;if(o!==null){try{o(e)}catch(r){x(r,t.parent)}return}t=t.parent}N(e)}function d(){return s}function f(e){let n=s;return s=e,n}function y(e){let n=s;n!==null&&(n.cells??=[]).push(e)}function Y(e){let n=T(s);return e.scope=n,n}function $(e){G(e)}function Q(e){e.flags|=32,e.fn=void 0;let n=s;n!==null&&n.cells.pop()}function a(e){return e===void 0?K:e===!1?Z:e}function ie(e){R++;try{return e()}finally{--R===0&&V()}}function se(e){let n=u;u=void 0;try{return e()}finally{u=n}}function ue(e,n){return new i(0,e,void 0,a(n?.equals))}function de(e,n){let t=new i(5,void 0,e,a(n?.equals));return y(t),t}function I(e){let n=new i(2,void 0,e,a(void 0));return y(n),Y(n),$(n),n}function fe(e){let n=I(e);n.deps===void 0&&Q(n)}function ae(e){d()===null&&void 0;let n=I(e);return()=>{E(n)}}function ce(e){let n=d();if(n===null){return}(n.disposals??=[]).push(e)}function pe(e){let n=T(d()),t=f(n);try{return e(()=>{O(n)})}finally{f(t)}}function xe(e,n){let t=T(d());t.handler=n;let o=f(t);try{let r=e();return f(o),r}catch(r){f(o),x(r,t);return}}function he(e,n){let t=f(e);try{return n()}finally{f(t)}}var z=Symbol("firsthand.ctx");function we(e,n){let t=n??"context";return{id:Symbol(t),description:t,hasDefault:arguments.length>0,defaultValue:e,fallback:void 0}}function ve(e,n){let t=d();if(t===null){`${e.description}`;return}let o=n instanceof i?n:new i(0,n,void 0,a(void 0));be(t)[e.id]=o}function Ce(e){let n=d(),t=n!==null?n.ctx:null,o=t!==null?t[e.id]:void 0;if(o!==void 0)return o;let r=e;if(!r.hasDefault)throw new h(r.description);return r.fallback??=new i(0,r.defaultValue,void 0,a(void 0))}function be(e){let n=e.ctx;if(n!==null&&n[z]===e)return n;let t=n!==null?Object.create(n):Object.create(null);return t[z]=e,e.ctx=t,t}export{i as Cell,h as FirsthandContextError,c as FirsthandCycleError,p as FirsthandReadonlyError,ie as batch,fe as bind,xe as catchError,de as computed,we as createContext,I as createEffect,T as createOwner,pe as createRoot,E as disposeCell,O as disposeOwner,ae as effect,d as getOwner,x as handleError,ce as onCleanup,y as own,ve as provide,he as runWithOwner,f as setOwner,ue as signal,se as untrack,Ce as useContext};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { getOwner } from './core.js';
|
|
2
|
+
import type { Dispose } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Registers a callback to run when the enclosing scope is disposed, or — inside
|
|
5
|
+
* an effect — before the effect's next run.
|
|
6
|
+
*/
|
|
7
|
+
export declare function onCleanup(fn: () => void): void;
|
|
8
|
+
/**
|
|
9
|
+
* Creates a detached root scope and hands back its disposer.
|
|
10
|
+
*
|
|
11
|
+
* Everything reactive created inside `fn` belongs to this root, so `dispose()`
|
|
12
|
+
* unsubscribes all of it. This is the explicit escape hatch for reactive code
|
|
13
|
+
* that lives outside a component; not calling `dispose()` leaks.
|
|
14
|
+
*/
|
|
15
|
+
export declare function createRoot<T>(fn: (dispose: Dispose) => T): T;
|
|
16
|
+
/**
|
|
17
|
+
* Installs an error boundary on the current scope.
|
|
18
|
+
*
|
|
19
|
+
* Errors thrown by `fn`, by effects created under it, or by DOM parts it owns
|
|
20
|
+
* propagate up the *owner* tree — so a portal's errors reach the boundary that
|
|
21
|
+
* lexically encloses the `portal()` call, not one near its DOM parent.
|
|
22
|
+
*/
|
|
23
|
+
export declare function catchError<T>(fn: () => T, handler: (error: unknown) => void): T | undefined;
|
|
24
|
+
/**
|
|
25
|
+
* Runs `fn` with `owner` as the current scope.
|
|
26
|
+
*
|
|
27
|
+
* Needed by asynchronous continuations — an `await` resumes with no ambient
|
|
28
|
+
* scope, so code that creates effects after awaiting must re-establish one.
|
|
29
|
+
*/
|
|
30
|
+
export declare function runWithOwner<T>(owner: ReturnType<typeof getOwner>, fn: () => T): T;
|
|
31
|
+
export { getOwner };
|
|
32
|
+
export type { Owner } from './core.js';
|
|
33
|
+
//# sourceMappingURL=lifecycle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../src/lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,EAA6B,QAAQ,EAAyB,MAAM,WAAW,CAAC;AAEvF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C;;;GAGG;AACH,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI,CAO9C;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,CAAC,GAAG,CAAC,CAU5D;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GAAG,CAAC,GAAG,SAAS,CAgB3F;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,KAAK,EAAE,UAAU,CAAC,OAAO,QAAQ,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAOlF;AAED,OAAO,EAAE,QAAQ,EAAE,CAAC;AACpB,YAAY,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC"}
|
package/dist/signal.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { CellOptions, Signal } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a mutable reactive cell.
|
|
4
|
+
*
|
|
5
|
+
* ```ts
|
|
6
|
+
* const count = signal(0);
|
|
7
|
+
* count.value++; // dependents update synchronously (ADR-0006)
|
|
8
|
+
* count.peek(); // read without subscribing
|
|
9
|
+
* count.set(n => n + 1); // functional update
|
|
10
|
+
* ```
|
|
11
|
+
*
|
|
12
|
+
* A signal is an ordinary object with its own identity. It is not stored in a
|
|
13
|
+
* slot table, so creating one inside a conditional is legal and its lifetime is
|
|
14
|
+
* simply the lifetime of the reference to it.
|
|
15
|
+
*/
|
|
16
|
+
export declare function signal<T>(value: T, options?: CellOptions<T>): Signal<T>;
|
|
17
|
+
//# sourceMappingURL=signal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signal.d.ts","sourceRoot":"","sources":["../src/signal.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEtD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAEvE"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/** Public reactive types. */
|
|
2
|
+
/** A reactive value that can only be read. */
|
|
3
|
+
export interface ReadonlyCell<T> {
|
|
4
|
+
/** Current value. Reading inside an effect or computed subscribes to it. */
|
|
5
|
+
readonly value: T;
|
|
6
|
+
/** Current value, without subscribing. */
|
|
7
|
+
peek(): T;
|
|
8
|
+
}
|
|
9
|
+
/** A reactive value that can be read and written. */
|
|
10
|
+
export interface Signal<T> extends ReadonlyCell<T> {
|
|
11
|
+
value: T;
|
|
12
|
+
/** Functional update; the updater runs untracked. */
|
|
13
|
+
set(updater: (previous: T) => T): void;
|
|
14
|
+
}
|
|
15
|
+
/** Releases whatever the call that returned it created. */
|
|
16
|
+
export type Dispose = () => void;
|
|
17
|
+
export interface CellOptions<T> {
|
|
18
|
+
/**
|
|
19
|
+
* Custom equality. `false` makes every write propagate, which is what you
|
|
20
|
+
* want for values the application mutates in place and diffs itself.
|
|
21
|
+
*/
|
|
22
|
+
equals?: ((a: T, b: T) => boolean) | false;
|
|
23
|
+
}
|
|
24
|
+
/** Deeply readonly view of a value. Used by `ReadonlyProps`. */
|
|
25
|
+
export type DeepReadonly<T> = T extends (...args: never[]) => unknown ? T : T extends ReadonlyArray<infer U> ? ReadonlyArray<DeepReadonly<U>> : T extends ReadonlyMap<infer K, infer V> ? ReadonlyMap<DeepReadonly<K>, DeepReadonly<V>> : T extends ReadonlySet<infer U> ? ReadonlySet<DeepReadonly<U>> : T extends Date | RegExp | Promise<unknown> ? T : T extends object ? {
|
|
26
|
+
readonly [K in keyof T]: DeepReadonly<T[K]>;
|
|
27
|
+
} : T;
|
|
28
|
+
/**
|
|
29
|
+
* The type a component sees for its props: every key readonly, deeply.
|
|
30
|
+
*
|
|
31
|
+
* Runtime enforcement is limited to the absence of setters on the props object
|
|
32
|
+
* itself — see ADR-0005 for why nothing stronger is possible without paying in
|
|
33
|
+
* object identity, copies or proxies.
|
|
34
|
+
*/
|
|
35
|
+
export type ReadonlyProps<T> = {
|
|
36
|
+
readonly [K in keyof T]: DeepReadonly<T[K]>;
|
|
37
|
+
};
|
|
38
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,6BAA6B;AAE7B,8CAA8C;AAC9C,MAAM,WAAW,YAAY,CAAC,CAAC;IAC7B,4EAA4E;IAC5E,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;IAClB,0CAA0C;IAC1C,IAAI,IAAI,CAAC,CAAC;CACX;AAED,qDAAqD;AACrD,MAAM,WAAW,MAAM,CAAC,CAAC,CAAE,SAAQ,YAAY,CAAC,CAAC,CAAC;IAChD,KAAK,EAAE,CAAC,CAAC;IACT,qDAAqD;IACrD,GAAG,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;CACxC;AAED,2DAA2D;AAC3D,MAAM,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC;AAEjC,MAAM,WAAW,WAAW,CAAC,CAAC;IAC5B;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,OAAO,CAAC,GAAG,KAAK,CAAC;CAC5C;AAED,gEAAgE;AAChE,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,OAAO,GACjE,CAAC,GACD,CAAC,SAAS,aAAa,CAAC,MAAM,CAAC,CAAC,GAC9B,aAAa,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,GAC9B,CAAC,SAAS,WAAW,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,GACrC,WAAW,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,GAC7C,CAAC,SAAS,WAAW,CAAC,MAAM,CAAC,CAAC,GAC5B,WAAW,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,GAC5B,CAAC,SAAS,IAAI,GAAG,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,GACxC,CAAC,GACD,CAAC,SAAS,MAAM,GACd;IAAE,QAAQ,EAAE,CAAC,IAAI,MAAM,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAAE,GAC/C,CAAC,CAAC;AAEhB;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI;IAAE,QAAQ,EAAE,CAAC,IAAI,MAAM,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAAE,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@firsthandjs/core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Fine-grained reactive core: signals, computeds, effects, owners and context. No DOM.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"main": "./dist/index.js",
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md",
|
|
19
|
+
"LICENSE"
|
|
20
|
+
],
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=20.11.0"
|
|
23
|
+
},
|
|
24
|
+
"publishConfig": {
|
|
25
|
+
"access": "public",
|
|
26
|
+
"provenance": true
|
|
27
|
+
},
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "git+https://github.com/firsthandjs/firsthand.git",
|
|
31
|
+
"directory": "packages/core"
|
|
32
|
+
},
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/firsthandjs/firsthand/issues"
|
|
35
|
+
},
|
|
36
|
+
"homepage": "https://github.com/firsthandjs/firsthand#readme",
|
|
37
|
+
"keywords": [
|
|
38
|
+
"reactive",
|
|
39
|
+
"signals",
|
|
40
|
+
"fine-grained",
|
|
41
|
+
"web-components",
|
|
42
|
+
"ui",
|
|
43
|
+
"framework",
|
|
44
|
+
"jsx",
|
|
45
|
+
"no-virtual-dom"
|
|
46
|
+
]
|
|
47
|
+
}
|