@usenagi/core 0.2.0 → 0.4.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/README.ja.md CHANGED
@@ -32,9 +32,9 @@ GSAP、Lenis、IntersectionObserver などを `setup()` で初期化し、`useUn
32
32
  // counter.ts
33
33
  import { create, signal, useWatch, useDomRef } from "@usenagi/core";
34
34
 
35
- const { component } = create();
35
+ const app = create();
36
36
 
37
- component({
37
+ app.component({
38
38
  name: "counter",
39
39
  setup() {
40
40
  const { refs } = useDomRef<{
@@ -71,13 +71,14 @@ npm i @usenagi/core
71
71
  ### First component
72
72
 
73
73
  ```ts
74
- import { create, defineComponent, signal, useWatch, useDomRef } from "@usenagi/core";
74
+ import { create, defineComponent, propTypes, signal, useWatch, useDomRef } from "@usenagi/core";
75
75
 
76
76
  const Greeting = defineComponent({
77
77
  name: "greeting",
78
+ props: propTypes<{ name: string }>(),
78
79
  setup(el, props) {
79
80
  const { refs } = useDomRef<{ message: HTMLParagraphElement }>();
80
- const text = signal((props.name as string) ?? "world");
81
+ const text = signal(props.name ?? "world");
81
82
 
82
83
  useWatch(text, (v) => {
83
84
  refs.message.textContent = `Hello, ${v}!`;
@@ -95,10 +96,10 @@ create().component(Greeting)(document.querySelector("#app")!);
95
96
 
96
97
  ```ts
97
98
  import { create } from "@usenagi/core";
98
- import { createScheduler } from "@usenagi/core/addons/scheduler";
99
+ import { schedulerAddon } from "@usenagi/core/addons/scheduler";
99
100
  import { visible, idle } from "@usenagi/core/addons/cue";
100
101
 
101
- const app = create({ scheduler: createScheduler() });
102
+ const app = create().install(schedulerAddon());
102
103
 
103
104
  // mount when the element enters the viewport
104
105
  app.component(HeavyWidget, { when: visible() })(el);
@@ -107,7 +108,7 @@ app.component(HeavyWidget, { when: visible() })(el);
107
108
  app.component(Analytics, { when: idle() })(el);
108
109
  ```
109
110
 
110
- `when` は `setup()` の前に待機する条件、`priority` は `setup()` を含む mount task の実行タイミングを決める。
111
+ `schedulerAddon()` を使うと、`when` は `setup()` の前に待機する条件、`priority` は `setup()` を含む mount task の実行タイミングを決める。
111
112
 
112
113
  ### BYO mounter recipe
113
114
 
@@ -118,14 +119,21 @@ app.component(Analytics, { when: idle() })(el);
118
119
 
119
120
  ## API
120
121
 
122
+ ### Component Definition
123
+
124
+ | API | 説明 |
125
+ | ---------------------- | ---------------------------------------------------------------- |
126
+ | `defineComponent(opts)` | 型安全な `ComponentSetup` 定義ヘルパー |
127
+ | `propTypes<T>()` | コンポーネント props の型マーカー(ランタイムコストゼロ) |
128
+
121
129
  ### Reactivity
122
130
 
123
- | API | 説明 |
124
- | ---------------------- | -------------------------------------------------------------------- |
125
- | `signal(value)` | `.value` を持つリアクティブな値コンテナを作成する |
126
- | `readonly(signal)` | 書き込み可能な `signal` の読み取り専用ラッパー |
127
- | `useComputed(fn)` | `signal` の依存を自動追跡する派生値 |
128
- | `useWatch(target, cb)` | 値変更時に `cb` を呼ぶ。unmount 時に自動で購読解除する |
131
+ | API | 説明 |
132
+ | ---------------------- | ------------------------------------------------------ |
133
+ | `signal(value)` | `.value` を持つリアクティブな値コンテナを作成する |
134
+ | `readonly(signal)` | 書き込み可能な `signal` の読み取り専用ラッパー |
135
+ | `useComputed(fn)` | `signal` の依存を自動追跡する派生値 |
136
+ | `useWatch(target, cb)` | 値変更時に `cb` を呼ぶ。unmount 時に自動で購読解除する |
129
137
 
130
138
  ```ts
131
139
  const width = signal(10);
@@ -139,10 +147,10 @@ useWatch(area, (v) => {
139
147
 
140
148
  ### Lifecycle
141
149
 
142
- | API | 説明 |
143
- | ---------------- | ------------------------------------------------------ |
144
- | `useMount(fn)` | コンポーネントのマウント完了後に1回実行する |
145
- | `useUnmount(fn)` | unmount 時に実行する。クリーンアップに使う |
150
+ | API | 説明 |
151
+ | ---------------- | ------------------------------------------- |
152
+ | `useMount(fn)` | コンポーネントのマウント完了後に1回実行する |
153
+ | `useUnmount(fn)` | unmount 時に実行する。クリーンアップに使う |
146
154
 
147
155
  ```ts
148
156
  import gsap from 'gsap';
@@ -157,11 +165,11 @@ setup(el) {
157
165
 
158
166
  ルート要素には **`setup(el)`** を、**`[data-ref]`** の子要素には **`useDomRef()`** を使う。
159
167
 
160
- | API | 説明 |
161
- | ------------------------------ | ---------------------------------------------------------- |
162
- | `useDomRef<T>()` | `[data-ref]` 要素への型付きアクセス |
163
- | `useEvent(el, event, handler)` | イベントリスナーを追加する。unmount 時に自動で除去する |
164
- | `useSlot()` | 子コンポーネントをマウントする。親の unmount に連動する |
168
+ | API | 説明 |
169
+ | ------------------------------ | ------------------------------------------------------- |
170
+ | `useDomRef<T>()` | `[data-ref]` 要素への型付きアクセス |
171
+ | `useEvent(el, event, handler)` | イベントリスナーを追加する。unmount 時に自動で除去する |
172
+ | `useSlot()` | 子コンポーネントをマウントする。親の unmount に連動する |
165
173
 
166
174
  ### Parent / child
167
175
 
@@ -171,39 +179,59 @@ setup(el) {
171
179
 
172
180
  ### Observers
173
181
 
174
- | API | 説明 |
175
- | --------------------------------- | ----------------------------------------------------------------- |
176
- | `useIntersectionWatch(cb, opts?)` | IntersectionObserver のラッパー。unmount 時に自動で切断する |
177
- | `useMediaQuery(query)` | `matchMedia` の結果を `ReadonlySignal<boolean>` で返す |
182
+ | API | 説明 |
183
+ | --------------------------------- | ----------------------------------------------------------- |
184
+ | `useIntersectionWatch(cb, opts?)` | IntersectionObserver のラッパー。unmount 時に自動で切断する |
185
+ | `useMediaQuery(query, cb)` | query 一致時に callback を実行し、`matchesQuery` `ReadonlySignal<boolean>` で返す |
178
186
 
179
187
  ### Addons
180
188
 
181
189
  ```ts
182
- import { createScheduler } from "@usenagi/core/addons/scheduler";
190
+ import { create, defineAddon } from "@usenagi/core";
191
+ import { schedulerAddon } from "@usenagi/core/addons/scheduler";
192
+
193
+ const app = create().install(schedulerAddon(), myAddon());
194
+ ```
195
+
196
+ | API | 説明 |
197
+ | --- | --- |
198
+ | `defineAddon({ name, install(ctx) })` | addon を定義する(`ctx` は `AddonContext`) |
199
+ | `app.install(...addons)` | app に addon を登録する(複数可) |
200
+ | `ctx.addMountMiddleware` / `addUnmountMiddleware` / `addComponentMiddleware` | mount / unmount / ComponentSetup の middleware を追加する |
201
+ | `ctx.installedAddons` | この app に install 済みの addon 名 |
202
+
203
+ `addMountMiddleware` / `addUnmountMiddleware` / `addComponentMiddleware` は **後から install した addon ほど外側**に適用される(`install(a, b)` なら実行順は `b → a → コア`)。
204
+
205
+ 遅延 mount には `schedulerAddon()` が必要。`when` や `priority` を使う場合も同様で、これらの mount option は scheduler addon が解釈する。addon の状態(scheduler / pending)は **各 app の `install` ごと**に作られる。
206
+
207
+ #### Scheduler + cue
208
+
209
+ ```ts
210
+ import { schedulerAddon } from "@usenagi/core/addons/scheduler";
183
211
  import { visible, idle, interaction, media } from "@usenagi/core/addons/cue";
184
212
  ```
185
213
 
186
- | API | 説明 |
187
- | ------------------------ | ------------------------------------------------------------------------------------- |
188
- | `createScheduler(opts?)` | `schedule(task, { priority, signal })` を実装した Scheduler を返す |
189
- | `visible(opts?)` | 要素が viewport に入ったときに解決する Cue |
190
- | `idle(timeout?)` | `requestIdleCallback` で解決する Cue |
191
- | `interaction(events?)` | 最初のユーザー操作で解決する Cue |
192
- | `media(query)` | media query が一致したときに解決する Cue |
214
+ | API | 説明 |
215
+ | --- | --- |
216
+ | `schedulerAddon(opts?)` | 遅延 mount addon |
217
+ | `visible(opts?)` | 要素が viewport に入ったときに解決する Cue |
218
+ | `idle(timeout?)` | `requestIdleCallback` で解決する Cue |
219
+ | `interaction(events?)` | 最初のユーザー操作で解決する Cue |
220
+ | `media(query)` | media query が一致したときに解決する Cue |
193
221
 
194
222
  ---
195
223
 
196
224
  ## Comparison
197
225
 
198
- | | **nagi** | Alpine.js | Stimulus | petite-vue |
199
- | ----------------------- | ----------- | --------- | -------- | ---------- |
200
- | Inline JS in HTML | ✗ | ◯ | ✗ | ◯ |
201
- | Composition-style setup | ◯ | △ | ✗ | ◯ |
202
- | BYO mounter | ◯ | △ | △ | △ |
203
- | Async mount cue | ◯ | ✗ | ✗ | ✗ |
204
- | Lifecycle cleanup | ◯ | △ | ◯ | △ |
205
- | `useComputed` (derived signals) | ◯ | ◯ | ✗ | ◯ |
206
- | Core gzip | ~2.5 kB | ~16 kB | ~8 kB | ~6 kB |
226
+ | | **nagi** | Alpine.js | Stimulus | petite-vue |
227
+ | -------------------------- | -------- | --------- | -------- | ---------- |
228
+ | Inline JS in HTML | ✗ | ◯ | ✗ | ◯ |
229
+ | Composition-style setup | ◯ | △ | ✗ | ◯ |
230
+ | BYO mounter | ◯ | △ | △ | △ |
231
+ | Async mount cue | ◯ | ✗ | ✗ | ✗ |
232
+ | Lifecycle cleanup | ◯ | △ | ◯ | △ |
233
+ | computed (derived signals) | ◯ | ◯ | ✗ | ◯ |
234
+ | Core gzip | ~2.5 kB | ~16 kB | ~8 kB | ~6 kB |
207
235
 
208
236
  (◯ = 組み込み、△ = 利用側の実装・規約で対応可能、✗ = 主な機能ではない)
209
237
 
@@ -233,13 +261,13 @@ import { visible, idle, interaction, media } from "@usenagi/core/addons/cue";
233
261
 
234
262
  ## Examples
235
263
 
236
- | Example | 説明 |
237
- | ----------------------------------------------------- | ----------------------------------------------------------------- |
238
- | [basic-counter](./examples/basic-counter/) | 最小の `signal` + `useWatch` 例 |
239
- | [computed](./examples/computed/) | `useComputed` による派生値(width × height = area) |
240
- | [parent-child](./examples/parent-child/) | `createContext` + `withContext` + `useSlot` |
241
- | [lenis-scroll-scene](./examples/lenis-scroll-scene/) | Lenis + `useComputed` によるスクロール進捗連動 |
242
- | [byo-mounter recipe](./examples/recipes/byo-mounter/) | `[data-component]` スキャン + manifest + cue |
264
+ | Example | 説明 |
265
+ | ----------------------------------------------------- | --------------------------------------------------- |
266
+ | [basic-counter](./examples/basic-counter/) | 最小の `signal` + `useWatch` 例 |
267
+ | [computed](./examples/computed/) | `useComputed` による派生値(width × height = area) |
268
+ | [parent-child](./examples/parent-child/) | `createContext` + `withContext` + `useSlot` |
269
+ | [lenis-scroll-scene](./examples/lenis-scroll-scene/) | Lenis + `useComputed` によるスクロール進捗連動 |
270
+ | [byo-mounter recipe](./examples/recipes/byo-mounter/) | `[data-component]` スキャン + manifest + cue |
243
271
 
244
272
  ---
245
273
 
package/README.md CHANGED
@@ -32,9 +32,9 @@ You are free to implement `[data-component]` scanning, manifests, lazy imports,
32
32
  // counter.ts
33
33
  import { create, signal, useWatch, useDomRef } from "@usenagi/core";
34
34
 
35
- const { component } = create();
35
+ const app = create();
36
36
 
37
- component({
37
+ app.component({
38
38
  name: "counter",
39
39
  setup() {
40
40
  const { refs } = useDomRef<{
@@ -71,13 +71,14 @@ npm i @usenagi/core
71
71
  ### First component
72
72
 
73
73
  ```ts
74
- import { create, defineComponent, signal, useWatch, useDomRef } from "@usenagi/core";
74
+ import { create, defineComponent, propTypes, signal, useWatch, useDomRef } from "@usenagi/core";
75
75
 
76
76
  const Greeting = defineComponent({
77
77
  name: "greeting",
78
+ props: propTypes<{ name: string }>(),
78
79
  setup(el, props) {
79
80
  const { refs } = useDomRef<{ message: HTMLParagraphElement }>();
80
- const text = signal((props.name as string) ?? "world");
81
+ const text = signal(props.name ?? "world");
81
82
 
82
83
  useWatch(text, (v) => {
83
84
  refs.message.textContent = `Hello, ${v}!`;
@@ -95,10 +96,10 @@ If delayed mounting is required, add the scheduler / cue addons.
95
96
 
96
97
  ```ts
97
98
  import { create } from "@usenagi/core";
98
- import { createScheduler } from "@usenagi/core/addons/scheduler";
99
+ import { schedulerAddon } from "@usenagi/core/addons/scheduler";
99
100
  import { visible, idle } from "@usenagi/core/addons/cue";
100
101
 
101
- const app = create({ scheduler: createScheduler() });
102
+ const app = create().install(schedulerAddon());
102
103
 
103
104
  // mount when the element enters the viewport
104
105
  app.component(HeavyWidget, { when: visible() })(el);
@@ -107,7 +108,7 @@ app.component(HeavyWidget, { when: visible() })(el);
107
108
  app.component(Analytics, { when: idle() })(el);
108
109
  ```
109
110
 
110
- `when` is a condition to wait for before `setup()`, and `priority` determines the execution timing of the mount task that includes `setup()`.
111
+ With `schedulerAddon()`, `when` is a condition to wait for before `setup()`, and `priority` determines the execution timing of the mount task that includes `setup()`.
111
112
 
112
113
  ### BYO mounter recipe
113
114
 
@@ -118,12 +119,19 @@ An example of automatic mounting by combining `[data-component]` scanning, manif
118
119
 
119
120
  ## API
120
121
 
122
+ ### Component Definition
123
+
124
+ | API | Description |
125
+ | ---------------------- | --------------------------------------------------------------------------- |
126
+ | `defineComponent(opts)` | Type-safe helper to define a `ComponentSetup` object |
127
+ | `propTypes<T>()` | Type-only marker for declaring component props shape (zero runtime cost) |
128
+
121
129
  ### Reactivity
122
130
 
123
- | API | Description |
124
- | ---------------------- | -------------------------------------------------------------- |
125
- | `signal(value)` | Creates a reactive value container (`.value`) |
126
- | `readonly(signal)` | Read-only wrapper around a writable `signal` |
131
+ | API | Description |
132
+ | ---------------------- | ----------------------------------------------------------------- |
133
+ | `signal(value)` | Creates a reactive value container (`.value`) |
134
+ | `readonly(signal)` | Read-only wrapper around a writable `signal` |
127
135
  | `useComputed(fn)` | Derived value that auto-tracks `signal` dependencies |
128
136
  | `useWatch(target, cb)` | Calls `cb` on value change; automatically unsubscribes on unmount |
129
137
 
@@ -139,10 +147,10 @@ useWatch(area, (v) => {
139
147
 
140
148
  ### Lifecycle
141
149
 
142
- | API | Description |
143
- | ---------------- | ----------------------------------------- |
144
- | `useMount(fn)` | Runs once after the component mounts |
145
- | `useUnmount(fn)` | Runs on unmount; use for cleanup |
150
+ | API | Description |
151
+ | ---------------- | ------------------------------------ |
152
+ | `useMount(fn)` | Runs once after the component mounts |
153
+ | `useUnmount(fn)` | Runs on unmount; use for cleanup |
146
154
 
147
155
  ```ts
148
156
  import gsap from 'gsap';
@@ -157,11 +165,11 @@ setup(el) {
157
165
 
158
166
  Use **`setup(el)`** for the root element and **`useDomRef()`** for `[data-ref]` descendants.
159
167
 
160
- | API | Description |
161
- | ------------------------------ | ------------------------------------------------------------ |
162
- | `useDomRef<T>()` | Typed access to `[data-ref]` elements |
163
- | `useEvent(el, event, handler)` | Adds an event listener; automatically removed on unmount |
164
- | `useSlot()` | Mounts child components; tied to the parent's unmount |
168
+ | API | Description |
169
+ | ------------------------------ | -------------------------------------------------------- |
170
+ | `useDomRef<T>()` | Typed access to `[data-ref]` elements |
171
+ | `useEvent(el, event, handler)` | Adds an event listener; automatically removed on unmount |
172
+ | `useSlot()` | Mounts child components; tied to the parent's unmount |
165
173
 
166
174
  ### Parent / child
167
175
 
@@ -174,36 +182,56 @@ You can mount child components with `useSlot()`. You can pass values from parent
174
182
  | API | Description |
175
183
  | --------------------------------- | ------------------------------------------------------------------- |
176
184
  | `useIntersectionWatch(cb, opts?)` | IntersectionObserver wrapper; automatically disconnected on unmount |
177
- | `useMediaQuery(query)` | Returns `matchMedia` result as a `ReadonlySignal<boolean>` |
185
+ | `useMediaQuery(query, cb)` | Runs `callback` when the query matches; returns `matchesQuery` as `ReadonlySignal<boolean>` |
178
186
 
179
187
  ### Addons
180
188
 
181
189
  ```ts
182
- import { createScheduler } from "@usenagi/core/addons/scheduler";
190
+ import { create, defineAddon } from "@usenagi/core";
191
+ import { schedulerAddon } from "@usenagi/core/addons/scheduler";
192
+
193
+ const app = create().install(schedulerAddon(), myAddon());
194
+ ```
195
+
196
+ | API | Description |
197
+ | --- | --- |
198
+ | `defineAddon({ name, install(ctx) })` | Defines an addon (`ctx` is `AddonContext`) |
199
+ | `app.install(...addons)` | Registers one or more addons on the app |
200
+ | `ctx.addMountMiddleware` / `addUnmountMiddleware` / `addComponentMiddleware` | Add mount / unmount / ComponentSetup middleware |
201
+ | `ctx.installedAddons` | Addon names already installed on this app |
202
+
203
+ `addMountMiddleware`, `addUnmountMiddleware`, and `addComponentMiddleware` apply **outermost for addons installed later** (`install(a, b)` runs as `b → a → core`).
204
+
205
+ Deferred mounting requires `schedulerAddon()`. The same applies when using `when` or `priority`; these mount options are interpreted by the scheduler addon. Addon state (scheduler / pending) is created **per app `install`**, not per addon instance.
206
+
207
+ #### Scheduler + cue
208
+
209
+ ```ts
210
+ import { schedulerAddon } from "@usenagi/core/addons/scheduler";
183
211
  import { visible, idle, interaction, media } from "@usenagi/core/addons/cue";
184
212
  ```
185
213
 
186
- | API | Description |
187
- | ------------------------ | --------------------------------------------------------------------- |
188
- | `createScheduler(opts?)` | Returns a Scheduler implementing `schedule(task, { priority, signal })` |
189
- | `visible(opts?)` | A Cue that resolves when the element enters the viewport |
190
- | `idle(timeout?)` | A Cue that resolves via `requestIdleCallback` |
191
- | `interaction(events?)` | A Cue that resolves on the first user interaction |
192
- | `media(query)` | A Cue that resolves when the media query matches |
214
+ | API | Description |
215
+ | --- | --- |
216
+ | `schedulerAddon(opts?)` | Addon for deferred mount |
217
+ | `visible(opts?)` | A Cue that resolves when the element enters the viewport |
218
+ | `idle(timeout?)` | A Cue that resolves via `requestIdleCallback` |
219
+ | `interaction(events?)` | A Cue that resolves on the first user interaction |
220
+ | `media(query)` | A Cue that resolves when the media query matches |
193
221
 
194
222
  ---
195
223
 
196
224
  ## Comparison
197
225
 
198
- | | **nagi** | Alpine.js | Stimulus | petite-vue |
199
- | ----------------------- | ----------- | --------- | -------- | ---------- |
200
- | Inline JS in HTML | ✗ | ◯ | ✗ | ◯ |
201
- | Composition-style setup | ◯ | △ | ✗ | ◯ |
202
- | BYO mounter | ◯ | △ | △ | △ |
203
- | Async mount cue | ◯ | ✗ | ✗ | ✗ |
204
- | Lifecycle cleanup | ◯ | △ | ◯ | △ |
205
- | `useComputed` (derived signals) | ◯ | ◯ | ✗ | ◯ |
206
- | Core gzip | ~2.5 kB | ~16 kB | ~8 kB | ~6 kB |
226
+ | | **nagi** | Alpine.js | Stimulus | petite-vue |
227
+ | -------------------------- | -------- | --------- | -------- | ---------- |
228
+ | Inline JS in HTML | ✗ | ◯ | ✗ | ◯ |
229
+ | Composition-style setup | ◯ | △ | ✗ | ◯ |
230
+ | BYO mounter | ◯ | △ | △ | △ |
231
+ | Async mount cue | ◯ | ✗ | ✗ | ✗ |
232
+ | Lifecycle cleanup | ◯ | △ | ◯ | △ |
233
+ | computed (derived signals) | ◯ | ◯ | ✗ | ◯ |
234
+ | Core gzip | ~2.5 kB | ~16 kB | ~8 kB | ~6 kB |
207
235
 
208
236
  (◯ = built-in, △ = handled via userland/convention, ✗ = not a primary feature)
209
237
 
@@ -233,13 +261,13 @@ import { visible, idle, interaction, media } from "@usenagi/core/addons/cue";
233
261
 
234
262
  ## Examples
235
263
 
236
- | Example | Description |
237
- | ----------------------------------------------------- | ------------------------------------------------------ |
238
- | [basic-counter](./examples/basic-counter/) | Minimal `signal` + `useWatch` example |
239
- | [computed](./examples/computed/) | Derived value with `useComputed` (width × height = area) |
240
- | [parent-child](./examples/parent-child/) | `createContext` + `withContext` + `useSlot` |
241
- | [lenis-scroll-scene](./examples/lenis-scroll-scene/) | Scroll-progress animation with Lenis + `useComputed` |
242
- | [byo-mounter recipe](./examples/recipes/byo-mounter/) | `[data-component]` scanning + manifest + cue |
264
+ | Example | Description |
265
+ | ----------------------------------------------------- | -------------------------------------------------------- |
266
+ | [basic-counter](./examples/basic-counter/) | Minimal `signal` + `useWatch` example |
267
+ | [computed](./examples/computed/) | Derived value with `useComputed` (width × height = area) |
268
+ | [parent-child](./examples/parent-child/) | `createContext` + `withContext` + `useSlot` |
269
+ | [lenis-scroll-scene](./examples/lenis-scroll-scene/) | Scroll-progress animation with Lenis + `useComputed` |
270
+ | [byo-mounter recipe](./examples/recipes/byo-mounter/) | `[data-component]` scanning + manifest + cue |
243
271
 
244
272
  ---
245
273
 
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});function e(e){return(e instanceof DOMException||e instanceof Error)&&e.name===`AbortError`}function t(t,r,i){if(i?.aborted)return;let{scheduler:a}=globalThis;if(typeof a?.postTask==`function`){a.postTask(t,{priority:r,signal:i}).catch(t=>{e(t)||queueMicrotask(()=>{throw t})});return}n(t,r,i)}function n(e,t,n){function r(){n?.aborted||e()}function i(e,t){let r=e();n?.addEventListener(`abort`,()=>t(r),{once:!0})}switch(t){case`user-blocking`:queueMicrotask(r);break;case`user-visible`:i(()=>requestAnimationFrame(r),cancelAnimationFrame);break;case`background`:typeof requestIdleCallback==`function`?i(()=>requestIdleCallback(r),cancelIdleCallback):i(()=>setTimeout(r,0),clearTimeout);break}}function r(e={}){let n=e.priority??`user-visible`;return{schedule(e,r={}){t(e,r.priority??n,r.signal)}}}exports.createScheduler=r;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});function e(e){return e}function t(){let e=new Map;return{add(t){let n=e.get(t);n&&n.abort();let r=new AbortController;return e.set(t,r),{signal:r.signal,complete(){return e.get(t)!==r||r.signal.aborted?!1:(e.delete(t),!0)},abort(){e.get(t)===r&&(r.abort(),e.delete(t))}}},abort(t){let n=e.get(t);n&&(n.abort(),e.delete(t))}}}function n(e){return(e instanceof DOMException||e instanceof Error)&&e.name===`AbortError`}function r(e,t,r){if(r?.aborted)return;let{scheduler:a}=globalThis;if(typeof a?.postTask==`function`){a.postTask(e,{priority:t,signal:r}).catch(e=>{n(e)||queueMicrotask(()=>{throw e})});return}i(e,t,r)}function i(e,t,n){function r(){n?.aborted||e()}function i(e,t){let r=e();n?.addEventListener(`abort`,()=>t(r),{once:!0})}switch(t){case`user-blocking`:queueMicrotask(r);break;case`user-visible`:i(()=>requestAnimationFrame(r),cancelAnimationFrame);break;case`background`:typeof requestIdleCallback==`function`?i(()=>requestIdleCallback(r),cancelIdleCallback):i(()=>setTimeout(r,0),clearTimeout);break}}function a(e={}){let t=e.priority??`user-visible`;return{schedule(e,n={}){r(e,n.priority??t,n.signal)}}}function o(e){return(e instanceof DOMException||e instanceof Error)&&e.name===`AbortError`}function s(n){return e({name:`@usenagi/scheduler`,install(e){let r=a(n),i=t();e.addMountMiddleware((e,t,n)=>(t,a)=>{let s=i.add(t),c=()=>{r.schedule(()=>{s.complete()&&e(t,a)},{priority:n.priority,signal:s.signal})},{when:l}=n;l?l(t,s.signal).then(()=>{s.signal.aborted||c()},e=>{o(e)||(s.abort(),queueMicrotask(()=>{throw e}))}):c()}),e.addUnmountMiddleware(e=>t=>{t.forEach(i.abort),e(t)})}})}exports.schedulerAddon=s;
@@ -1,26 +1,54 @@
1
- //#region lib/utils/isAbortError.ts
1
+ //#region lib/core/addon.ts
2
2
  function e(e) {
3
- return (e instanceof DOMException || e instanceof Error) && e.name === "AbortError";
3
+ return e;
4
+ }
5
+ //#endregion
6
+ //#region lib/addons/scheduler/_internal/pending.ts
7
+ function t() {
8
+ let e = /* @__PURE__ */ new Map();
9
+ return {
10
+ add(t) {
11
+ let n = e.get(t);
12
+ n && n.abort();
13
+ let r = new AbortController();
14
+ return e.set(t, r), {
15
+ signal: r.signal,
16
+ complete() {
17
+ return e.get(t) !== r || r.signal.aborted ? !1 : (e.delete(t), !0);
18
+ },
19
+ abort() {
20
+ e.get(t) === r && (r.abort(), e.delete(t));
21
+ }
22
+ };
23
+ },
24
+ abort(t) {
25
+ let n = e.get(t);
26
+ n && (n.abort(), e.delete(t));
27
+ }
28
+ };
4
29
  }
5
30
  //#endregion
6
- //#region lib/addons/scheduler/task.ts
7
- function t(t, r, i) {
8
- if (i?.aborted) return;
31
+ //#region lib/addons/scheduler/_internal/schedule.ts
32
+ function n(e) {
33
+ return (e instanceof DOMException || e instanceof Error) && e.name === "AbortError";
34
+ }
35
+ function r(e, t, r) {
36
+ if (r?.aborted) return;
9
37
  let { scheduler: a } = globalThis;
10
38
  if (typeof a?.postTask == "function") {
11
- a.postTask(t, {
12
- priority: r,
13
- signal: i
14
- }).catch((t) => {
15
- e(t) || queueMicrotask(() => {
16
- throw t;
39
+ a.postTask(e, {
40
+ priority: t,
41
+ signal: r
42
+ }).catch((e) => {
43
+ n(e) || queueMicrotask(() => {
44
+ throw e;
17
45
  });
18
46
  });
19
47
  return;
20
48
  }
21
- n(t, r, i);
49
+ i(e, t, r);
22
50
  }
23
- function n(e, t, n) {
51
+ function i(e, t, n) {
24
52
  function r() {
25
53
  n?.aborted || e();
26
54
  }
@@ -40,13 +68,43 @@ function n(e, t, n) {
40
68
  break;
41
69
  }
42
70
  }
71
+ function a(e = {}) {
72
+ let t = e.priority ?? "user-visible";
73
+ return { schedule(e, n = {}) {
74
+ r(e, n.priority ?? t, n.signal);
75
+ } };
76
+ }
43
77
  //#endregion
44
78
  //#region lib/addons/scheduler/index.ts
45
- function r(e = {}) {
46
- let n = e.priority ?? "user-visible";
47
- return { schedule(e, r = {}) {
48
- t(e, r.priority ?? n, r.signal);
49
- } };
79
+ function o(e) {
80
+ return (e instanceof DOMException || e instanceof Error) && e.name === "AbortError";
81
+ }
82
+ function s(n) {
83
+ return e({
84
+ name: "@usenagi/scheduler",
85
+ install(e) {
86
+ let r = a(n), i = t();
87
+ e.addMountMiddleware((e, t, n) => (t, a) => {
88
+ let s = i.add(t), c = () => {
89
+ r.schedule(() => {
90
+ s.complete() && e(t, a);
91
+ }, {
92
+ priority: n.priority,
93
+ signal: s.signal
94
+ });
95
+ }, { when: l } = n;
96
+ l ? l(t, s.signal).then(() => {
97
+ s.signal.aborted || c();
98
+ }, (e) => {
99
+ o(e) || (s.abort(), queueMicrotask(() => {
100
+ throw e;
101
+ }));
102
+ }) : c();
103
+ }), e.addUnmountMiddleware((e) => (t) => {
104
+ t.forEach(i.abort), e(t);
105
+ });
106
+ }
107
+ });
50
108
  }
51
109
  //#endregion
52
- export { r as createScheduler };
110
+ export { s as schedulerAddon };