@akashjs/runtime 0.2.1 → 0.2.6
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/dist/{chunk-NVZLEJXB.cjs → chunk-2Q6SYE5O.cjs} +2 -2
- package/dist/{chunk-NVZLEJXB.cjs.map → chunk-2Q6SYE5O.cjs.map} +1 -1
- package/dist/chunk-2U643GJZ.cjs +2 -0
- package/dist/chunk-2U643GJZ.cjs.map +1 -0
- package/dist/chunk-35DJOBEO.cjs +2 -0
- package/dist/chunk-35DJOBEO.cjs.map +1 -0
- package/dist/{chunk-POLTPHUA.js → chunk-3GRR4VW2.js} +3 -3
- package/dist/{chunk-3AL2DVPZ.cjs.map → chunk-3GRR4VW2.js.map} +1 -1
- package/dist/{chunk-Z5LQV5ND.js → chunk-5A7KDBDU.js} +2 -2
- package/dist/{chunk-Z5LQV5ND.js.map → chunk-5A7KDBDU.js.map} +1 -1
- package/dist/{chunk-D6QQYZIC.js → chunk-5NHDEY2C.js} +2 -2
- package/dist/chunk-5NHDEY2C.js.map +1 -0
- package/dist/{chunk-NQVWTQ2I.cjs → chunk-6GGYM5SF.cjs} +3 -3
- package/dist/{chunk-NQVWTQ2I.cjs.map → chunk-6GGYM5SF.cjs.map} +1 -1
- package/dist/chunk-AVVJKYT3.cjs +2 -0
- package/dist/chunk-AVVJKYT3.cjs.map +1 -0
- package/dist/{chunk-3AL2DVPZ.cjs → chunk-EEILP4OL.cjs} +3 -3
- package/dist/chunk-EEILP4OL.cjs.map +1 -0
- package/dist/{chunk-BT6HNBE7.js → chunk-FTTNKDZQ.js} +3 -3
- package/dist/{chunk-BT6HNBE7.js.map → chunk-FTTNKDZQ.js.map} +1 -1
- package/dist/chunk-H2HNKYN2.js +2 -0
- package/dist/chunk-H2HNKYN2.js.map +1 -0
- package/dist/chunk-TKFJGLUO.js +2 -0
- package/dist/chunk-TKFJGLUO.js.map +1 -0
- package/dist/chunk-U53YRJNV.js +36 -0
- package/dist/chunk-U53YRJNV.js.map +1 -0
- package/dist/chunk-ZYVQQ5VR.cjs +36 -0
- package/dist/chunk-ZYVQQ5VR.cjs.map +1 -0
- package/dist/{context-2uQ6fuxu.d.ts → context-CB1mCq2h.d.cts} +1 -1
- package/dist/{context-2uQ6fuxu.d.cts → context-CB1mCq2h.d.ts} +1 -1
- package/dist/core.cjs +1 -1
- package/dist/core.d.cts +1 -1
- package/dist/core.d.ts +1 -1
- package/dist/core.js +1 -1
- package/dist/devtools-overlay-CJWKBTP4.js +57 -0
- package/dist/devtools-overlay-CJWKBTP4.js.map +1 -0
- package/dist/devtools-overlay-EQ3G755P.cjs +57 -0
- package/dist/devtools-overlay-EQ3G755P.cjs.map +1 -0
- package/dist/index.cjs +18 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -7
- package/dist/index.d.ts +11 -7
- package/dist/index.js +18 -18
- package/dist/index.js.map +1 -1
- package/dist/machine.cjs +1 -1
- package/dist/machine.js +1 -1
- package/dist/offline.cjs +1 -1
- package/dist/offline.d.cts +2 -0
- package/dist/offline.d.ts +2 -0
- package/dist/offline.js +1 -1
- package/dist/pwa.cjs +1 -1
- package/dist/pwa.d.cts +5 -1
- package/dist/pwa.d.ts +5 -1
- package/dist/pwa.js +1 -1
- package/dist/ssr.cjs +1 -1
- package/dist/ssr.js +1 -1
- package/dist/store.cjs +1 -1
- package/dist/store.d.cts +35 -36
- package/dist/store.d.ts +35 -36
- package/dist/store.js +1 -1
- package/dist/sync.cjs +1 -1
- package/dist/sync.d.cts +59 -4
- package/dist/sync.d.ts +59 -4
- package/dist/sync.js +1 -1
- package/dist/test.cjs +4 -4
- package/dist/test.cjs.map +1 -1
- package/dist/test.d.cts +71 -3
- package/dist/test.d.ts +71 -3
- package/dist/test.js +4 -4
- package/dist/test.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-6NX6JRSV.cjs +0 -32
- package/dist/chunk-6NX6JRSV.cjs.map +0 -1
- package/dist/chunk-772SU4MG.js +0 -2
- package/dist/chunk-772SU4MG.js.map +0 -1
- package/dist/chunk-7LQZF3XA.cjs +0 -2
- package/dist/chunk-7LQZF3XA.cjs.map +0 -1
- package/dist/chunk-D6QQYZIC.js.map +0 -1
- package/dist/chunk-H4SAK7A5.cjs +0 -2
- package/dist/chunk-H4SAK7A5.cjs.map +0 -1
- package/dist/chunk-IM2VW4TK.js +0 -32
- package/dist/chunk-IM2VW4TK.js.map +0 -1
- package/dist/chunk-ODDXU5DO.js +0 -2
- package/dist/chunk-ODDXU5DO.js.map +0 -1
- package/dist/chunk-POLTPHUA.js.map +0 -1
- package/dist/chunk-YIB4EKVI.cjs +0 -2
- package/dist/chunk-YIB4EKVI.cjs.map +0 -1
package/dist/test.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/test.ts"],"names":["mount","component","options","container","props","provides","node","defineComponent","key","value","provide","text","el","findByText","role","findImplicitRole","id","selector","fireEvent","tick","nativeInputValueSetter","resolve","root","best","walker","IMPLICIT_ROLES","tags","tag","roles","waitFor","assertion","timeout","interval","start","err","r","waitForElement","found","createTestSignal","initialValue","inner","signal","history","setCount","read","fn","newVal"],"mappings":"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","file":"test.cjs","sourcesContent":["/**\n * @akashjs/runtime/test — Test utilities.\n *\n * Provides mount(), fireEvent, and query helpers for testing\n * AkashJS components with Vitest (or any test runner).\n *\n * No TestBed, no module configuration, no compileComponents().\n * Just mount a component and query the resulting DOM.\n */\n\nimport { defineComponent } from './component.js';\nimport { provide } from './context.js';\nimport { signal } from './signals.js';\nimport { flushSync } from './scheduler.js';\nimport type { Signal } from './signals.js';\nimport type { Component } from './component.js';\nimport type { InjectionKey } from './context.js';\n\n// --- Mount result ---\n\nexport interface MountResult {\n /** The root container element */\n container: HTMLElement;\n /** Unmount the component and clean up */\n unmount(): void;\n /** Find the first element whose text content contains the given string */\n getByText(text: string): HTMLElement;\n /** Find the first element with the given ARIA role */\n getByRole(role: string): HTMLElement;\n /** Find the first element with the given data-testid */\n getByTestId(id: string): HTMLElement;\n /** Query all elements matching a CSS selector */\n queryAll(selector: string): HTMLElement[];\n /** Query the first element matching a CSS selector, or null */\n query(selector: string): HTMLElement | null;\n}\n\n// --- Mount options ---\n\nexport interface MountOptions<P extends Record<string, unknown>> {\n /** Props to pass to the component */\n props?: P;\n /** Context values to provide (Map of InjectionKey -> value) */\n provide?: Map<InjectionKey<any>, any>;\n}\n\n// --- mount() ---\n\n/**\n * Mount a component into a detached DOM element for testing.\n *\n * ```ts\n * const { getByText, getByRole } = mount(Counter, { props: { initial: 5 } });\n * expect(getByText('Count: 5')).toBeTruthy();\n * ```\n */\nexport function mount<P extends Record<string, unknown> = Record<string, unknown>>(\n component: Component<P>,\n options?: MountOptions<P>,\n): MountResult {\n const container = document.createElement('div');\n container.setAttribute('data-akash-test-root', '');\n\n const props = (options?.props ?? {}) as P & { children?: undefined };\n const provides = options?.provide;\n\n let node: Node;\n\n if (provides && provides.size > 0) {\n // Wrap in a provider component to inject context values\n const Wrapper = defineComponent(() => {\n for (const [key, value] of provides) {\n provide(key, value);\n }\n return () => component(props);\n });\n node = Wrapper({});\n } else {\n node = component(props);\n }\n\n container.appendChild(node);\n\n // Attach to document so queries work properly\n document.body.appendChild(container);\n\n return {\n container,\n\n unmount() {\n container.remove();\n },\n\n getByText(text: string): HTMLElement {\n const el = findByText(container, text);\n if (!el) {\n throw new Error(\n `[AkashJS Test] Could not find element with text: \"${text}\"\\n` +\n ` Container HTML: ${container.innerHTML.slice(0, 200)}`,\n );\n }\n return el;\n },\n\n getByRole(role: string): HTMLElement {\n const el = container.querySelector<HTMLElement>(`[role=\"${role}\"]`)\n ?? findImplicitRole(container, role);\n if (!el) {\n throw new Error(\n `[AkashJS Test] Could not find element with role: \"${role}\"\\n` +\n ` Container HTML: ${container.innerHTML.slice(0, 200)}`,\n );\n }\n return el;\n },\n\n getByTestId(id: string): HTMLElement {\n const el = container.querySelector<HTMLElement>(`[data-testid=\"${id}\"]`);\n if (!el) {\n throw new Error(\n `[AkashJS Test] Could not find element with data-testid: \"${id}\"\\n` +\n ` Container HTML: ${container.innerHTML.slice(0, 200)}`,\n );\n }\n return el;\n },\n\n queryAll(selector: string): HTMLElement[] {\n return Array.from(container.querySelectorAll<HTMLElement>(selector));\n },\n\n query(selector: string): HTMLElement | null {\n return container.querySelector<HTMLElement>(selector);\n },\n };\n}\n\n// --- fireEvent ---\n\n/**\n * Fire DOM events on elements. Returns a promise that resolves\n * after a microtask, giving effects time to flush.\n *\n * ```ts\n * await fireEvent.click(button);\n * await fireEvent.input(input, 'hello');\n * ```\n */\nexport const fireEvent = {\n async click(el: HTMLElement): Promise<void> {\n el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));\n await tick();\n },\n\n async input(el: HTMLInputElement | HTMLTextAreaElement, value: string): Promise<void> {\n // Set the value property directly (as a user typing would)\n const nativeInputValueSetter = Object.getOwnPropertyDescriptor(\n el instanceof HTMLTextAreaElement ? HTMLTextAreaElement.prototype : HTMLInputElement.prototype,\n 'value',\n )?.set;\n\n if (nativeInputValueSetter) {\n nativeInputValueSetter.call(el, value);\n } else {\n (el as any).value = value;\n }\n\n el.dispatchEvent(new Event('input', { bubbles: true }));\n el.dispatchEvent(new Event('change', { bubbles: true }));\n await tick();\n },\n\n async submit(el: HTMLFormElement): Promise<void> {\n el.dispatchEvent(new Event('submit', { bubbles: true, cancelable: true }));\n await tick();\n },\n\n async focus(el: HTMLElement): Promise<void> {\n el.focus();\n el.dispatchEvent(new FocusEvent('focus', { bubbles: true }));\n await tick();\n },\n\n async blur(el: HTMLElement): Promise<void> {\n el.blur();\n el.dispatchEvent(new FocusEvent('blur', { bubbles: true }));\n await tick();\n },\n\n async keyDown(el: HTMLElement, key: string, options?: KeyboardEventInit): Promise<void> {\n el.dispatchEvent(new KeyboardEvent('keydown', { key, bubbles: true, ...options }));\n await tick();\n },\n\n async keyUp(el: HTMLElement, key: string, options?: KeyboardEventInit): Promise<void> {\n el.dispatchEvent(new KeyboardEvent('keyup', { key, bubbles: true, ...options }));\n await tick();\n },\n};\n\n// --- Internal helpers ---\n\n/** Wait one microtask for effects to flush */\nfunction tick(): Promise<void> {\n return new Promise((resolve) => queueMicrotask(resolve));\n}\n\n/** Walk DOM tree to find the deepest element containing text */\nfunction findByText(root: HTMLElement, text: string): HTMLElement | null {\n // Walk all descendant elements; keep the deepest match\n let best: HTMLElement | null = null;\n\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);\n let node: Node | null = walker.nextNode(); // skip root itself on first call\n\n while (node) {\n if (node instanceof HTMLElement && node.textContent?.includes(text)) {\n best = node; // deeper elements overwrite shallower ones\n }\n node = walker.nextNode();\n }\n\n // If no descendant matched, check root itself\n if (!best && root.textContent?.includes(text)) {\n best = root;\n }\n\n return best;\n}\n\nfunction getDirectTextContent(el: HTMLElement): string {\n let text = '';\n for (const child of el.childNodes) {\n if (child.nodeType === Node.TEXT_NODE) {\n text += child.textContent ?? '';\n }\n }\n return text;\n}\n\n/** Map of HTML tag names to their implicit ARIA roles */\nconst IMPLICIT_ROLES: Record<string, string[]> = {\n button: ['button'],\n a: ['link'],\n input: ['textbox', 'checkbox', 'radio', 'spinbutton', 'slider'],\n select: ['combobox', 'listbox'],\n textarea: ['textbox'],\n img: ['img'],\n form: ['form'],\n nav: ['navigation'],\n main: ['main'],\n header: ['banner'],\n footer: ['contentinfo'],\n aside: ['complementary'],\n section: ['region'],\n article: ['article'],\n ul: ['list'],\n ol: ['list'],\n li: ['listitem'],\n table: ['table'],\n th: ['columnheader'],\n td: ['cell'],\n h1: ['heading'],\n h2: ['heading'],\n h3: ['heading'],\n h4: ['heading'],\n h5: ['heading'],\n h6: ['heading'],\n};\n\nfunction findImplicitRole(root: HTMLElement, role: string): HTMLElement | null {\n // Find tags that implicitly have this role\n const tags: string[] = [];\n for (const [tag, roles] of Object.entries(IMPLICIT_ROLES)) {\n if (roles.includes(role)) tags.push(tag);\n }\n\n if (tags.length === 0) return null;\n\n const selector = tags.join(', ');\n return root.querySelector<HTMLElement>(selector);\n}\n\n// =========================================================================\n// Async helpers\n// =========================================================================\n\nexport interface WaitForOptions {\n /** Timeout in ms (default: 1000) */\n timeout?: number;\n /** Poll interval in ms (default: 50) */\n interval?: number;\n}\n\n/**\n * Wait for an assertion to pass. Retries until it doesn't throw or times out.\n *\n * ```ts\n * await waitFor(() => expect(getByText('loaded')).toBeTruthy());\n * ```\n */\nexport async function waitFor(\n assertion: () => void | Promise<void>,\n options?: WaitForOptions,\n): Promise<void> {\n const { timeout = 1000, interval = 50 } = options ?? {};\n const start = Date.now();\n\n while (true) {\n try {\n await assertion();\n return;\n } catch (err) {\n if (Date.now() - start >= timeout) throw err;\n await new Promise(r => setTimeout(r, interval));\n }\n }\n}\n\n/**\n * Wait for an element matching a selector to appear in the container.\n *\n * ```ts\n * const el = await waitForElement(container, '.loaded');\n * ```\n */\nexport async function waitForElement(\n container: HTMLElement,\n selector: string,\n options?: WaitForOptions,\n): Promise<HTMLElement> {\n let found: HTMLElement | null = null;\n await waitFor(() => {\n found = container.querySelector<HTMLElement>(selector);\n if (!found) throw new Error(`Element \"${selector}\" not found`);\n }, options);\n return found!;\n}\n\n/**\n * Synchronously flush all pending effects. Use after signal writes\n * when you need the DOM to update before asserting.\n *\n * ```ts\n * count.set(5);\n * flush();\n * expect(getByText('5')).toBeTruthy();\n * ```\n */\nexport { flushSync as flush };\n\n// =========================================================================\n// Signal test helpers\n// =========================================================================\n\nexport interface TestSignal<T> extends Signal<T> {\n /** History of all values set on this signal (including initial) */\n history: T[];\n /** Number of times set() or update() was called */\n setCount: number;\n /** Reset history and set count */\n resetHistory(): void;\n}\n\n/**\n * Create a signal with test inspection capabilities.\n *\n * ```ts\n * const count = createTestSignal(0);\n * count.set(1);\n * count.set(2);\n * expect(count.history).toEqual([0, 1, 2]);\n * expect(count.setCount).toBe(2);\n * ```\n */\nexport function createTestSignal<T>(initialValue: T): TestSignal<T> {\n const inner = signal(initialValue);\n const history: T[] = [initialValue];\n let setCount = 0;\n\n const read = (() => inner()) as TestSignal<T>;\n\n read.set = (value: T) => {\n inner.set(value);\n history.push(value);\n setCount++;\n };\n\n read.update = (fn: (prev: T) => T) => {\n const newVal = fn(inner.peek());\n read.set(newVal);\n };\n\n read.peek = () => inner.peek();\n\n Object.defineProperty(read, 'history', { get: () => [...history] });\n Object.defineProperty(read, 'setCount', { get: () => setCount });\n\n read.resetHistory = () => {\n history.length = 0;\n history.push(inner.peek());\n setCount = 0;\n };\n\n return read;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/test.ts"],"names":["mount","component","options","container","props","provides","node","defineComponent","key","value","provide","mountedContainers","text","el","findByText","role","findImplicitRole","id","selector","fireEvent","tick","nativeInputValueSetter","resolve","root","best","walker","IMPLICIT_ROLES","tags","tag","roles","waitFor","assertion","timeout","interval","start","err","r","waitForElement","found","createTestSignal","initialValue","inner","signal","history","setCount","read","fn","newVal","cleanup","clearStores","createTestStore","useStore","store","mockFetch","responses","defaultStatus","recorded","input","init","url","method","body","headers","v","k","path","responseData","responseStatus","responseBody","_status","rest","isNullBody","mockQueryClient","cache"],"mappings":"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","file":"test.cjs","sourcesContent":["/**\n * @akashjs/runtime/test — Test utilities.\n *\n * Provides mount(), fireEvent, and query helpers for testing\n * AkashJS components with Vitest (or any test runner).\n *\n * No TestBed, no module configuration, no compileComponents().\n * Just mount a component and query the resulting DOM.\n */\n\nimport { defineComponent } from './component.js';\nimport { provide } from './context.js';\nimport { signal } from './signals.js';\nimport { flushSync } from './scheduler.js';\nimport { clearStores } from './store.js';\nimport type { Signal } from './signals.js';\nimport type { Component } from './component.js';\nimport type { InjectionKey } from './context.js';\nimport type { Store } from './store.js';\n\n// --- Mount result ---\n\nexport interface MountResult {\n /** The root container element */\n container: HTMLElement;\n /** Unmount the component and clean up */\n unmount(): void;\n /** Find the first element whose text content contains the given string */\n getByText(text: string): HTMLElement;\n /** Find the first element with the given ARIA role */\n getByRole(role: string): HTMLElement;\n /** Find the first element with the given data-testid */\n getByTestId(id: string): HTMLElement;\n /** Query all elements matching a CSS selector */\n queryAll(selector: string): HTMLElement[];\n /** Query the first element matching a CSS selector, or null */\n query(selector: string): HTMLElement | null;\n}\n\n// --- Mount options ---\n\nexport interface MountOptions<P extends Record<string, unknown>> {\n /** Props to pass to the component */\n props?: P;\n /** Context values to provide (Map of InjectionKey -> value) */\n provide?: Map<InjectionKey<any>, any>;\n}\n\n// --- mount() ---\n\n/**\n * Mount a component into a detached DOM element for testing.\n *\n * ```ts\n * const { getByText, getByRole } = mount(Counter, { props: { initial: 5 } });\n * expect(getByText('Count: 5')).toBeTruthy();\n * ```\n */\nexport function mount<P extends Record<string, unknown> = Record<string, unknown>>(\n component: Component<P>,\n options?: MountOptions<P>,\n): MountResult {\n const container = document.createElement('div');\n container.setAttribute('data-akash-test-root', '');\n\n const props = (options?.props ?? {}) as P & { children?: undefined };\n const provides = options?.provide;\n\n let node: Node;\n\n if (provides && provides.size > 0) {\n // Wrap in a provider component to inject context values\n const Wrapper = defineComponent(() => {\n for (const [key, value] of provides) {\n provide(key, value);\n }\n return () => component(props);\n });\n node = Wrapper({});\n } else {\n node = component(props);\n }\n\n container.appendChild(node);\n\n // Attach to document so queries work properly\n document.body.appendChild(container);\n mountedContainers.push(container);\n\n return {\n container,\n\n unmount() {\n container.remove();\n },\n\n getByText(text: string): HTMLElement {\n const el = findByText(container, text);\n if (!el) {\n throw new Error(\n `[AkashJS Test] Could not find element with text: \"${text}\"\\n` +\n ` Container HTML: ${container.innerHTML.slice(0, 200)}`,\n );\n }\n return el;\n },\n\n getByRole(role: string): HTMLElement {\n const el = container.querySelector<HTMLElement>(`[role=\"${role}\"]`)\n ?? findImplicitRole(container, role);\n if (!el) {\n throw new Error(\n `[AkashJS Test] Could not find element with role: \"${role}\"\\n` +\n ` Container HTML: ${container.innerHTML.slice(0, 200)}`,\n );\n }\n return el;\n },\n\n getByTestId(id: string): HTMLElement {\n const el = container.querySelector<HTMLElement>(`[data-testid=\"${id}\"]`);\n if (!el) {\n throw new Error(\n `[AkashJS Test] Could not find element with data-testid: \"${id}\"\\n` +\n ` Container HTML: ${container.innerHTML.slice(0, 200)}`,\n );\n }\n return el;\n },\n\n queryAll(selector: string): HTMLElement[] {\n return Array.from(container.querySelectorAll<HTMLElement>(selector));\n },\n\n query(selector: string): HTMLElement | null {\n return container.querySelector<HTMLElement>(selector);\n },\n };\n}\n\n// --- fireEvent ---\n\n/**\n * Fire DOM events on elements. Returns a promise that resolves\n * after a microtask, giving effects time to flush.\n *\n * ```ts\n * await fireEvent.click(button);\n * await fireEvent.input(input, 'hello');\n * ```\n */\nexport const fireEvent = {\n async click(el: HTMLElement): Promise<void> {\n el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));\n await tick();\n },\n\n async input(el: HTMLInputElement | HTMLTextAreaElement, value: string): Promise<void> {\n // Set the value property directly (as a user typing would)\n const nativeInputValueSetter = Object.getOwnPropertyDescriptor(\n el instanceof HTMLTextAreaElement ? HTMLTextAreaElement.prototype : HTMLInputElement.prototype,\n 'value',\n )?.set;\n\n if (nativeInputValueSetter) {\n nativeInputValueSetter.call(el, value);\n } else {\n (el as any).value = value;\n }\n\n el.dispatchEvent(new Event('input', { bubbles: true }));\n el.dispatchEvent(new Event('change', { bubbles: true }));\n await tick();\n },\n\n async submit(el: HTMLFormElement): Promise<void> {\n el.dispatchEvent(new Event('submit', { bubbles: true, cancelable: true }));\n await tick();\n },\n\n async focus(el: HTMLElement): Promise<void> {\n el.focus();\n el.dispatchEvent(new FocusEvent('focus', { bubbles: true }));\n await tick();\n },\n\n async blur(el: HTMLElement): Promise<void> {\n el.blur();\n el.dispatchEvent(new FocusEvent('blur', { bubbles: true }));\n await tick();\n },\n\n async keyDown(el: HTMLElement, key: string, options?: KeyboardEventInit): Promise<void> {\n el.dispatchEvent(new KeyboardEvent('keydown', { key, bubbles: true, ...options }));\n await tick();\n },\n\n async keyUp(el: HTMLElement, key: string, options?: KeyboardEventInit): Promise<void> {\n el.dispatchEvent(new KeyboardEvent('keyup', { key, bubbles: true, ...options }));\n await tick();\n },\n};\n\n// --- Internal helpers ---\n\n/** Wait one microtask for effects to flush */\nfunction tick(): Promise<void> {\n return new Promise((resolve) => queueMicrotask(resolve));\n}\n\n/** Walk DOM tree to find the deepest element containing text */\nfunction findByText(root: HTMLElement, text: string): HTMLElement | null {\n // Walk all descendant elements; keep the deepest match\n let best: HTMLElement | null = null;\n\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);\n let node: Node | null = walker.nextNode(); // skip root itself on first call\n\n while (node) {\n if (node instanceof HTMLElement && node.textContent?.includes(text)) {\n best = node; // deeper elements overwrite shallower ones\n }\n node = walker.nextNode();\n }\n\n // If no descendant matched, check root itself\n if (!best && root.textContent?.includes(text)) {\n best = root;\n }\n\n return best;\n}\n\nfunction getDirectTextContent(el: HTMLElement): string {\n let text = '';\n for (const child of el.childNodes) {\n if (child.nodeType === Node.TEXT_NODE) {\n text += child.textContent ?? '';\n }\n }\n return text;\n}\n\n/** Map of HTML tag names to their implicit ARIA roles */\nconst IMPLICIT_ROLES: Record<string, string[]> = {\n button: ['button'],\n a: ['link'],\n input: ['textbox', 'checkbox', 'radio', 'spinbutton', 'slider'],\n select: ['combobox', 'listbox'],\n textarea: ['textbox'],\n img: ['img'],\n form: ['form'],\n nav: ['navigation'],\n main: ['main'],\n header: ['banner'],\n footer: ['contentinfo'],\n aside: ['complementary'],\n section: ['region'],\n article: ['article'],\n ul: ['list'],\n ol: ['list'],\n li: ['listitem'],\n table: ['table'],\n th: ['columnheader'],\n td: ['cell'],\n h1: ['heading'],\n h2: ['heading'],\n h3: ['heading'],\n h4: ['heading'],\n h5: ['heading'],\n h6: ['heading'],\n};\n\nfunction findImplicitRole(root: HTMLElement, role: string): HTMLElement | null {\n // Find tags that implicitly have this role\n const tags: string[] = [];\n for (const [tag, roles] of Object.entries(IMPLICIT_ROLES)) {\n if (roles.includes(role)) tags.push(tag);\n }\n\n if (tags.length === 0) return null;\n\n const selector = tags.join(', ');\n return root.querySelector<HTMLElement>(selector);\n}\n\n// =========================================================================\n// Async helpers\n// =========================================================================\n\nexport interface WaitForOptions {\n /** Timeout in ms (default: 1000) */\n timeout?: number;\n /** Poll interval in ms (default: 50) */\n interval?: number;\n}\n\n/**\n * Wait for an assertion to pass. Retries until it doesn't throw or times out.\n *\n * ```ts\n * await waitFor(() => expect(getByText('loaded')).toBeTruthy());\n * ```\n */\nexport async function waitFor(\n assertion: () => void | Promise<void>,\n options?: WaitForOptions,\n): Promise<void> {\n const { timeout = 1000, interval = 50 } = options ?? {};\n const start = Date.now();\n\n while (true) {\n try {\n await assertion();\n return;\n } catch (err) {\n if (Date.now() - start >= timeout) throw err;\n await new Promise(r => setTimeout(r, interval));\n }\n }\n}\n\n/**\n * Wait for an element matching a selector to appear in the container.\n *\n * ```ts\n * const el = await waitForElement(container, '.loaded');\n * ```\n */\nexport async function waitForElement(\n container: HTMLElement,\n selector: string,\n options?: WaitForOptions,\n): Promise<HTMLElement> {\n let found: HTMLElement | null = null;\n await waitFor(() => {\n found = container.querySelector<HTMLElement>(selector);\n if (!found) throw new Error(`Element \"${selector}\" not found`);\n }, options);\n return found!;\n}\n\n/**\n * Synchronously flush all pending effects. Use after signal writes\n * when you need the DOM to update before asserting.\n *\n * ```ts\n * count.set(5);\n * flush();\n * expect(getByText('5')).toBeTruthy();\n * ```\n */\nexport { flushSync as flush };\n\n// =========================================================================\n// Signal test helpers\n// =========================================================================\n\nexport interface TestSignal<T> extends Signal<T> {\n /** History of all values set on this signal (including initial) */\n history: T[];\n /** Number of times set() or update() was called */\n setCount: number;\n /** Reset history and set count */\n resetHistory(): void;\n}\n\n/**\n * Create a signal with test inspection capabilities.\n *\n * ```ts\n * const count = createTestSignal(0);\n * count.set(1);\n * count.set(2);\n * expect(count.history).toEqual([0, 1, 2]);\n * expect(count.setCount).toBe(2);\n * ```\n */\nexport function createTestSignal<T>(initialValue: T): TestSignal<T> {\n const inner = signal(initialValue);\n const history: T[] = [initialValue];\n let setCount = 0;\n\n const read = (() => inner()) as TestSignal<T>;\n\n read.set = (value: T) => {\n inner.set(value);\n history.push(value);\n setCount++;\n };\n\n read.update = (fn: (prev: T) => T) => {\n const newVal = fn(inner.peek());\n read.set(newVal);\n };\n\n read.peek = () => inner.peek();\n\n Object.defineProperty(read, 'history', { get: () => [...history] });\n Object.defineProperty(read, 'setCount', { get: () => setCount });\n\n read.resetHistory = () => {\n history.length = 0;\n history.push(inner.peek());\n setCount = 0;\n };\n\n return read;\n}\n\n// =========================================================================\n// Store test helpers\n// =========================================================================\n\n/** Track mounted containers for cleanup */\nconst mountedContainers: HTMLElement[] = [];\n\n/**\n * Unmount all rendered components and clear store singletons.\n * Call in afterEach() for clean test isolation.\n */\nexport function cleanup(): void {\n for (const container of mountedContainers) {\n container.remove();\n }\n mountedContainers.length = 0;\n clearStores();\n}\n\n/**\n * Create a fresh store instance for testing, bypassing the singleton cache.\n * Automatically calls clearStores() first so the factory returns a new instance.\n *\n * ```ts\n * const store = createTestStore(useCounter);\n * store.increment();\n * expect(store.count()).toBe(1);\n * ```\n */\nexport function createTestStore<S, G, A>(\n useStore: () => Store<S, G, A>,\n): Store<S, G, A> {\n clearStores();\n const store = useStore();\n // Remove from singleton cache so subsequent useStore() calls create\n // independent instances, not the same test store\n clearStores();\n return store;\n}\n\n// =========================================================================\n// Mock fetch\n// =========================================================================\n\nexport interface MockFetchCall {\n url: string;\n method: string;\n body?: unknown;\n headers: Record<string, string>;\n}\n\nexport interface MockFetchInstance {\n /** The mock fetch function — pass to createHttpClient({ fetch }) */\n (input: RequestInfo | URL, init?: RequestInit): Promise<Response>;\n /** All recorded calls */\n calls: () => MockFetchCall[];\n /** Number of calls made */\n callCount: () => number;\n /** Reset call history */\n reset: () => void;\n}\n\n/**\n * Create a mock fetch that returns predefined responses.\n *\n * ```ts\n * const fetch = mockFetch({\n * '/api/users': [{ id: 1, name: 'Alice' }],\n * '/api/users/1': { id: 1, name: 'Alice' },\n * });\n * const res = await fetch('/api/users');\n * const data = await res.json(); // [{ id: 1, name: 'Alice' }]\n * expect(fetch.callCount()).toBe(1);\n * ```\n */\nexport function mockFetch(\n responses: Record<string, unknown> = {},\n defaultStatus = 200,\n): MockFetchInstance {\n const recorded: MockFetchCall[] = [];\n\n const fn = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {\n const url = typeof input === 'string' ? input\n : input instanceof URL ? input.toString()\n : input.url;\n const method = init?.method ?? 'GET';\n let body: unknown;\n if (init?.body) {\n try { body = JSON.parse(String(init.body)); } catch { body = init.body; }\n }\n const headers: Record<string, string> = {};\n if (init?.headers) {\n const h = new Headers(init.headers);\n h.forEach((v, k) => { headers[k] = v; });\n }\n\n recorded.push({ url, method, body, headers });\n\n // Match response by URL path\n const path = url.replace(/^https?:\\/\\/[^/]+/, '').split('?')[0];\n const responseData = responses[path] ?? responses[url];\n\n if (responseData === undefined) {\n return new Response('Not Found', { status: 404 });\n }\n\n // Support _status convention: { _status: 500, error: 'msg' }\n let responseStatus = defaultStatus;\n let responseBody: unknown = responseData;\n if (responseBody && typeof responseBody === 'object' && !Array.isArray(responseBody) && '_status' in (responseBody as Record<string, unknown>)) {\n const { _status, ...rest } = responseBody as Record<string, unknown>;\n responseStatus = Number(_status);\n responseBody = rest;\n }\n\n // Null-body statuses (204, 304) cannot have a response body\n const isNullBody = responseStatus === 204 || responseStatus === 304;\n return new Response(isNullBody ? null : JSON.stringify(responseBody), {\n status: responseStatus,\n headers: isNullBody ? {} : { 'Content-Type': 'application/json' },\n });\n };\n\n fn.calls = () => [...recorded];\n fn.callCount = () => recorded.length;\n fn.reset = () => { recorded.length = 0; };\n\n return fn as MockFetchInstance;\n}\n\n// =========================================================================\n// Mock query client\n// =========================================================================\n\n/**\n * Create a mock query client for testing. Behaves like createQueryClient\n * but with no real caching — each useCachedQuery gets a fresh context.\n *\n * ```ts\n * const qc = mockQueryClient();\n * const posts = useCachedQuery(qc, ['posts'], fetcher);\n * ```\n */\nexport function mockQueryClient(): {\n invalidate: (prefix: unknown) => void;\n setQueryData: (key: unknown, updater: unknown) => void;\n getQueryData: (key: unknown) => unknown;\n removeQuery: (key: unknown) => void;\n clear: () => void;\n _cache: Map<string, unknown>;\n _options: { defaultStaleTime?: number };\n} {\n const cache = new Map<string, unknown>();\n return {\n invalidate: () => {},\n setQueryData: () => {},\n getQueryData: () => undefined,\n removeQuery: (key) => { cache.delete(JSON.stringify(key)); },\n clear: () => { cache.clear(); },\n _cache: cache,\n _options: { defaultStaleTime: 0 },\n };\n}\n"]}
|
package/dist/test.d.cts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { I as InjectionKey } from './context-
|
|
2
|
-
export { f as flush } from './context-
|
|
1
|
+
import { I as InjectionKey } from './context-CB1mCq2h.cjs';
|
|
2
|
+
export { f as flush } from './context-CB1mCq2h.cjs';
|
|
3
3
|
import { S as Signal } from './signals-C7XfOHHR.cjs';
|
|
4
4
|
import { C as Component } from './component-C1WnFcRp.cjs';
|
|
5
|
+
import { Store } from './store.cjs';
|
|
6
|
+
import './sync.cjs';
|
|
5
7
|
|
|
6
8
|
/**
|
|
7
9
|
* @akashjs/runtime/test — Test utilities.
|
|
@@ -105,5 +107,71 @@ interface TestSignal<T> extends Signal<T> {
|
|
|
105
107
|
* ```
|
|
106
108
|
*/
|
|
107
109
|
declare function createTestSignal<T>(initialValue: T): TestSignal<T>;
|
|
110
|
+
/**
|
|
111
|
+
* Unmount all rendered components and clear store singletons.
|
|
112
|
+
* Call in afterEach() for clean test isolation.
|
|
113
|
+
*/
|
|
114
|
+
declare function cleanup(): void;
|
|
115
|
+
/**
|
|
116
|
+
* Create a fresh store instance for testing, bypassing the singleton cache.
|
|
117
|
+
* Automatically calls clearStores() first so the factory returns a new instance.
|
|
118
|
+
*
|
|
119
|
+
* ```ts
|
|
120
|
+
* const store = createTestStore(useCounter);
|
|
121
|
+
* store.increment();
|
|
122
|
+
* expect(store.count()).toBe(1);
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
125
|
+
declare function createTestStore<S, G, A>(useStore: () => Store<S, G, A>): Store<S, G, A>;
|
|
126
|
+
interface MockFetchCall {
|
|
127
|
+
url: string;
|
|
128
|
+
method: string;
|
|
129
|
+
body?: unknown;
|
|
130
|
+
headers: Record<string, string>;
|
|
131
|
+
}
|
|
132
|
+
interface MockFetchInstance {
|
|
133
|
+
/** The mock fetch function — pass to createHttpClient({ fetch }) */
|
|
134
|
+
(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
|
|
135
|
+
/** All recorded calls */
|
|
136
|
+
calls: () => MockFetchCall[];
|
|
137
|
+
/** Number of calls made */
|
|
138
|
+
callCount: () => number;
|
|
139
|
+
/** Reset call history */
|
|
140
|
+
reset: () => void;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Create a mock fetch that returns predefined responses.
|
|
144
|
+
*
|
|
145
|
+
* ```ts
|
|
146
|
+
* const fetch = mockFetch({
|
|
147
|
+
* '/api/users': [{ id: 1, name: 'Alice' }],
|
|
148
|
+
* '/api/users/1': { id: 1, name: 'Alice' },
|
|
149
|
+
* });
|
|
150
|
+
* const res = await fetch('/api/users');
|
|
151
|
+
* const data = await res.json(); // [{ id: 1, name: 'Alice' }]
|
|
152
|
+
* expect(fetch.callCount()).toBe(1);
|
|
153
|
+
* ```
|
|
154
|
+
*/
|
|
155
|
+
declare function mockFetch(responses?: Record<string, unknown>, defaultStatus?: number): MockFetchInstance;
|
|
156
|
+
/**
|
|
157
|
+
* Create a mock query client for testing. Behaves like createQueryClient
|
|
158
|
+
* but with no real caching — each useCachedQuery gets a fresh context.
|
|
159
|
+
*
|
|
160
|
+
* ```ts
|
|
161
|
+
* const qc = mockQueryClient();
|
|
162
|
+
* const posts = useCachedQuery(qc, ['posts'], fetcher);
|
|
163
|
+
* ```
|
|
164
|
+
*/
|
|
165
|
+
declare function mockQueryClient(): {
|
|
166
|
+
invalidate: (prefix: unknown) => void;
|
|
167
|
+
setQueryData: (key: unknown, updater: unknown) => void;
|
|
168
|
+
getQueryData: (key: unknown) => unknown;
|
|
169
|
+
removeQuery: (key: unknown) => void;
|
|
170
|
+
clear: () => void;
|
|
171
|
+
_cache: Map<string, unknown>;
|
|
172
|
+
_options: {
|
|
173
|
+
defaultStaleTime?: number;
|
|
174
|
+
};
|
|
175
|
+
};
|
|
108
176
|
|
|
109
|
-
export { type MountOptions, type MountResult, type TestSignal, type WaitForOptions, createTestSignal, fireEvent, mount, waitFor, waitForElement };
|
|
177
|
+
export { type MockFetchCall, type MockFetchInstance, type MountOptions, type MountResult, type TestSignal, type WaitForOptions, cleanup, createTestSignal, createTestStore, fireEvent, mockFetch, mockQueryClient, mount, waitFor, waitForElement };
|
package/dist/test.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { I as InjectionKey } from './context-
|
|
2
|
-
export { f as flush } from './context-
|
|
1
|
+
import { I as InjectionKey } from './context-CB1mCq2h.js';
|
|
2
|
+
export { f as flush } from './context-CB1mCq2h.js';
|
|
3
3
|
import { S as Signal } from './signals-C7XfOHHR.js';
|
|
4
4
|
import { C as Component } from './component-C1WnFcRp.js';
|
|
5
|
+
import { Store } from './store.js';
|
|
6
|
+
import './sync.js';
|
|
5
7
|
|
|
6
8
|
/**
|
|
7
9
|
* @akashjs/runtime/test — Test utilities.
|
|
@@ -105,5 +107,71 @@ interface TestSignal<T> extends Signal<T> {
|
|
|
105
107
|
* ```
|
|
106
108
|
*/
|
|
107
109
|
declare function createTestSignal<T>(initialValue: T): TestSignal<T>;
|
|
110
|
+
/**
|
|
111
|
+
* Unmount all rendered components and clear store singletons.
|
|
112
|
+
* Call in afterEach() for clean test isolation.
|
|
113
|
+
*/
|
|
114
|
+
declare function cleanup(): void;
|
|
115
|
+
/**
|
|
116
|
+
* Create a fresh store instance for testing, bypassing the singleton cache.
|
|
117
|
+
* Automatically calls clearStores() first so the factory returns a new instance.
|
|
118
|
+
*
|
|
119
|
+
* ```ts
|
|
120
|
+
* const store = createTestStore(useCounter);
|
|
121
|
+
* store.increment();
|
|
122
|
+
* expect(store.count()).toBe(1);
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
125
|
+
declare function createTestStore<S, G, A>(useStore: () => Store<S, G, A>): Store<S, G, A>;
|
|
126
|
+
interface MockFetchCall {
|
|
127
|
+
url: string;
|
|
128
|
+
method: string;
|
|
129
|
+
body?: unknown;
|
|
130
|
+
headers: Record<string, string>;
|
|
131
|
+
}
|
|
132
|
+
interface MockFetchInstance {
|
|
133
|
+
/** The mock fetch function — pass to createHttpClient({ fetch }) */
|
|
134
|
+
(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
|
|
135
|
+
/** All recorded calls */
|
|
136
|
+
calls: () => MockFetchCall[];
|
|
137
|
+
/** Number of calls made */
|
|
138
|
+
callCount: () => number;
|
|
139
|
+
/** Reset call history */
|
|
140
|
+
reset: () => void;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Create a mock fetch that returns predefined responses.
|
|
144
|
+
*
|
|
145
|
+
* ```ts
|
|
146
|
+
* const fetch = mockFetch({
|
|
147
|
+
* '/api/users': [{ id: 1, name: 'Alice' }],
|
|
148
|
+
* '/api/users/1': { id: 1, name: 'Alice' },
|
|
149
|
+
* });
|
|
150
|
+
* const res = await fetch('/api/users');
|
|
151
|
+
* const data = await res.json(); // [{ id: 1, name: 'Alice' }]
|
|
152
|
+
* expect(fetch.callCount()).toBe(1);
|
|
153
|
+
* ```
|
|
154
|
+
*/
|
|
155
|
+
declare function mockFetch(responses?: Record<string, unknown>, defaultStatus?: number): MockFetchInstance;
|
|
156
|
+
/**
|
|
157
|
+
* Create a mock query client for testing. Behaves like createQueryClient
|
|
158
|
+
* but with no real caching — each useCachedQuery gets a fresh context.
|
|
159
|
+
*
|
|
160
|
+
* ```ts
|
|
161
|
+
* const qc = mockQueryClient();
|
|
162
|
+
* const posts = useCachedQuery(qc, ['posts'], fetcher);
|
|
163
|
+
* ```
|
|
164
|
+
*/
|
|
165
|
+
declare function mockQueryClient(): {
|
|
166
|
+
invalidate: (prefix: unknown) => void;
|
|
167
|
+
setQueryData: (key: unknown, updater: unknown) => void;
|
|
168
|
+
getQueryData: (key: unknown) => unknown;
|
|
169
|
+
removeQuery: (key: unknown) => void;
|
|
170
|
+
clear: () => void;
|
|
171
|
+
_cache: Map<string, unknown>;
|
|
172
|
+
_options: {
|
|
173
|
+
defaultStaleTime?: number;
|
|
174
|
+
};
|
|
175
|
+
};
|
|
108
176
|
|
|
109
|
-
export { type MountOptions, type MountResult, type TestSignal, type WaitForOptions, createTestSignal, fireEvent, mount, waitFor, waitForElement };
|
|
177
|
+
export { type MockFetchCall, type MockFetchInstance, type MountOptions, type MountResult, type TestSignal, type WaitForOptions, cleanup, createTestSignal, createTestStore, fireEvent, mockFetch, mockQueryClient, mount, waitFor, waitForElement };
|
package/dist/test.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {B,h}from'./chunk-
|
|
2
|
-
Container HTML: ${
|
|
3
|
-
Container HTML: ${
|
|
4
|
-
Container HTML: ${
|
|
1
|
+
import {B as B$1,h}from'./chunk-FTTNKDZQ.js';import {c as c$1}from'./chunk-TKFJGLUO.js';import'./chunk-H2HNKYN2.js';import {l}from'./chunk-3GRR4VW2.js';export{b as flush}from'./chunk-3GRR4VW2.js';function A(e,n){let t=document.createElement("div");t.setAttribute("data-akash-test-root","");let r=n?.props??{},o=n?.provide,i;return o&&o.size>0?i=B$1(()=>{for(let[a,u]of o)h(a,u);return ()=>e(r)})({}):i=e(r),t.appendChild(i),document.body.appendChild(t),f.push(t),{container:t,unmount(){t.remove();},getByText(s){let a=k(t,s);if(!a)throw new Error(`[AkashJS Test] Could not find element with text: "${s}"
|
|
2
|
+
Container HTML: ${t.innerHTML.slice(0,200)}`);return a},getByRole(s){let a=t.querySelector(`[role="${s}"]`)??x(t,s);if(!a)throw new Error(`[AkashJS Test] Could not find element with role: "${s}"
|
|
3
|
+
Container HTML: ${t.innerHTML.slice(0,200)}`);return a},getByTestId(s){let a=t.querySelector(`[data-testid="${s}"]`);if(!a)throw new Error(`[AkashJS Test] Could not find element with data-testid: "${s}"
|
|
4
|
+
Container HTML: ${t.innerHTML.slice(0,200)}`);return a},queryAll(s){return Array.from(t.querySelectorAll(s))},query(s){return t.querySelector(s)}}}var O={async click(e){e.dispatchEvent(new MouseEvent("click",{bubbles:true,cancelable:true})),await c();},async input(e,n){let t=Object.getOwnPropertyDescriptor(e instanceof HTMLTextAreaElement?HTMLTextAreaElement.prototype:HTMLInputElement.prototype,"value")?.set;t?t.call(e,n):e.value=n,e.dispatchEvent(new Event("input",{bubbles:true})),e.dispatchEvent(new Event("change",{bubbles:true})),await c();},async submit(e){e.dispatchEvent(new Event("submit",{bubbles:true,cancelable:true})),await c();},async focus(e){e.focus(),e.dispatchEvent(new FocusEvent("focus",{bubbles:true})),await c();},async blur(e){e.blur(),e.dispatchEvent(new FocusEvent("blur",{bubbles:true})),await c();},async keyDown(e,n,t){e.dispatchEvent(new KeyboardEvent("keydown",{key:n,bubbles:true,...t})),await c();},async keyUp(e,n,t){e.dispatchEvent(new KeyboardEvent("keyup",{key:n,bubbles:true,...t})),await c();}};function c(){return new Promise(e=>queueMicrotask(e))}function k(e,n){let t=null,r=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT),o=r.nextNode();for(;o;)o instanceof HTMLElement&&o.textContent?.includes(n)&&(t=o),o=r.nextNode();return !t&&e.textContent?.includes(n)&&(t=e),t}var H={button:["button"],a:["link"],input:["textbox","checkbox","radio","spinbutton","slider"],select:["combobox","listbox"],textarea:["textbox"],img:["img"],form:["form"],nav:["navigation"],main:["main"],header:["banner"],footer:["contentinfo"],aside:["complementary"],section:["region"],article:["article"],ul:["list"],ol:["list"],li:["listitem"],table:["table"],th:["columnheader"],td:["cell"],h1:["heading"],h2:["heading"],h3:["heading"],h4:["heading"],h5:["heading"],h6:["heading"]};function x(e,n){let t=[];for(let[o,i]of Object.entries(H))i.includes(n)&&t.push(o);if(t.length===0)return null;let r=t.join(", ");return e.querySelector(r)}async function S(e,n){let{timeout:t=1e3,interval:r=50}=n??{},o=Date.now();for(;;)try{await e();return}catch(i){if(Date.now()-o>=t)throw i;await new Promise(s=>setTimeout(s,r));}}async function q(e,n,t){let r=null;return await S(()=>{if(r=e.querySelector(n),!r)throw new Error(`Element "${n}" not found`)},t),r}function N(e){let n=l(e),t=[e],r=0,o=(()=>n());return o.set=i=>{n.set(i),t.push(i),r++;},o.update=i=>{let s=i(n.peek());o.set(s);},o.peek=()=>n.peek(),Object.defineProperty(o,"history",{get:()=>[...t]}),Object.defineProperty(o,"setCount",{get:()=>r}),o.resetHistory=()=>{t.length=0,t.push(n.peek()),r=0;},o}var f=[];function j(){for(let e of f)e.remove();f.length=0,c$1();}function D(e){c$1();let n=e();return c$1(),n}function B(e={},n=200){let t=[],r=async(o,i)=>{let s=typeof o=="string"?o:o instanceof URL?o.toString():o.url,a=i?.method??"GET",u;if(i?.body)try{u=JSON.parse(String(i.body));}catch{u=i.body;}let y={};i?.headers&&new Headers(i.headers).forEach((p,v)=>{y[v]=p;}),t.push({url:s,method:a,body:u,headers:y});let M=s.replace(/^https?:\/\/[^/]+/,"").split("?")[0],h=e[M]??e[s];if(h===void 0)return new Response("Not Found",{status:404});let d=n,l=h;if(l&&typeof l=="object"&&!Array.isArray(l)&&"_status"in l){let{_status:g,...p}=l;d=Number(g),l=p;}let T=d===204||d===304;return new Response(T?null:JSON.stringify(l),{status:d,headers:T?{}:{"Content-Type":"application/json"}})};return r.calls=()=>[...t],r.callCount=()=>t.length,r.reset=()=>{t.length=0;},r}function _(){let e=new Map;return {invalidate:()=>{},setQueryData:()=>{},getQueryData:()=>{},removeQuery:n=>{e.delete(JSON.stringify(n));},clear:()=>{e.clear();},_cache:e,_options:{defaultStaleTime:0}}}export{j as cleanup,N as createTestSignal,D as createTestStore,O as fireEvent,B as mockFetch,_ as mockQueryClient,A as mount,S as waitFor,q as waitForElement};//# sourceMappingURL=test.js.map
|
|
5
5
|
//# sourceMappingURL=test.js.map
|
package/dist/test.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/test.ts"],"names":["mount","component","options","container","props","provides","node","defineComponent","key","value","provide","text","el","findByText","role","findImplicitRole","id","selector","fireEvent","tick","nativeInputValueSetter","resolve","root","best","walker","IMPLICIT_ROLES","tags","tag","roles","waitFor","assertion","timeout","interval","start","err","r","waitForElement","found","createTestSignal","initialValue","inner","signal","history","setCount","read","fn","newVal"],"mappings":"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","file":"test.js","sourcesContent":["/**\n * @akashjs/runtime/test — Test utilities.\n *\n * Provides mount(), fireEvent, and query helpers for testing\n * AkashJS components with Vitest (or any test runner).\n *\n * No TestBed, no module configuration, no compileComponents().\n * Just mount a component and query the resulting DOM.\n */\n\nimport { defineComponent } from './component.js';\nimport { provide } from './context.js';\nimport { signal } from './signals.js';\nimport { flushSync } from './scheduler.js';\nimport type { Signal } from './signals.js';\nimport type { Component } from './component.js';\nimport type { InjectionKey } from './context.js';\n\n// --- Mount result ---\n\nexport interface MountResult {\n /** The root container element */\n container: HTMLElement;\n /** Unmount the component and clean up */\n unmount(): void;\n /** Find the first element whose text content contains the given string */\n getByText(text: string): HTMLElement;\n /** Find the first element with the given ARIA role */\n getByRole(role: string): HTMLElement;\n /** Find the first element with the given data-testid */\n getByTestId(id: string): HTMLElement;\n /** Query all elements matching a CSS selector */\n queryAll(selector: string): HTMLElement[];\n /** Query the first element matching a CSS selector, or null */\n query(selector: string): HTMLElement | null;\n}\n\n// --- Mount options ---\n\nexport interface MountOptions<P extends Record<string, unknown>> {\n /** Props to pass to the component */\n props?: P;\n /** Context values to provide (Map of InjectionKey -> value) */\n provide?: Map<InjectionKey<any>, any>;\n}\n\n// --- mount() ---\n\n/**\n * Mount a component into a detached DOM element for testing.\n *\n * ```ts\n * const { getByText, getByRole } = mount(Counter, { props: { initial: 5 } });\n * expect(getByText('Count: 5')).toBeTruthy();\n * ```\n */\nexport function mount<P extends Record<string, unknown> = Record<string, unknown>>(\n component: Component<P>,\n options?: MountOptions<P>,\n): MountResult {\n const container = document.createElement('div');\n container.setAttribute('data-akash-test-root', '');\n\n const props = (options?.props ?? {}) as P & { children?: undefined };\n const provides = options?.provide;\n\n let node: Node;\n\n if (provides && provides.size > 0) {\n // Wrap in a provider component to inject context values\n const Wrapper = defineComponent(() => {\n for (const [key, value] of provides) {\n provide(key, value);\n }\n return () => component(props);\n });\n node = Wrapper({});\n } else {\n node = component(props);\n }\n\n container.appendChild(node);\n\n // Attach to document so queries work properly\n document.body.appendChild(container);\n\n return {\n container,\n\n unmount() {\n container.remove();\n },\n\n getByText(text: string): HTMLElement {\n const el = findByText(container, text);\n if (!el) {\n throw new Error(\n `[AkashJS Test] Could not find element with text: \"${text}\"\\n` +\n ` Container HTML: ${container.innerHTML.slice(0, 200)}`,\n );\n }\n return el;\n },\n\n getByRole(role: string): HTMLElement {\n const el = container.querySelector<HTMLElement>(`[role=\"${role}\"]`)\n ?? findImplicitRole(container, role);\n if (!el) {\n throw new Error(\n `[AkashJS Test] Could not find element with role: \"${role}\"\\n` +\n ` Container HTML: ${container.innerHTML.slice(0, 200)}`,\n );\n }\n return el;\n },\n\n getByTestId(id: string): HTMLElement {\n const el = container.querySelector<HTMLElement>(`[data-testid=\"${id}\"]`);\n if (!el) {\n throw new Error(\n `[AkashJS Test] Could not find element with data-testid: \"${id}\"\\n` +\n ` Container HTML: ${container.innerHTML.slice(0, 200)}`,\n );\n }\n return el;\n },\n\n queryAll(selector: string): HTMLElement[] {\n return Array.from(container.querySelectorAll<HTMLElement>(selector));\n },\n\n query(selector: string): HTMLElement | null {\n return container.querySelector<HTMLElement>(selector);\n },\n };\n}\n\n// --- fireEvent ---\n\n/**\n * Fire DOM events on elements. Returns a promise that resolves\n * after a microtask, giving effects time to flush.\n *\n * ```ts\n * await fireEvent.click(button);\n * await fireEvent.input(input, 'hello');\n * ```\n */\nexport const fireEvent = {\n async click(el: HTMLElement): Promise<void> {\n el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));\n await tick();\n },\n\n async input(el: HTMLInputElement | HTMLTextAreaElement, value: string): Promise<void> {\n // Set the value property directly (as a user typing would)\n const nativeInputValueSetter = Object.getOwnPropertyDescriptor(\n el instanceof HTMLTextAreaElement ? HTMLTextAreaElement.prototype : HTMLInputElement.prototype,\n 'value',\n )?.set;\n\n if (nativeInputValueSetter) {\n nativeInputValueSetter.call(el, value);\n } else {\n (el as any).value = value;\n }\n\n el.dispatchEvent(new Event('input', { bubbles: true }));\n el.dispatchEvent(new Event('change', { bubbles: true }));\n await tick();\n },\n\n async submit(el: HTMLFormElement): Promise<void> {\n el.dispatchEvent(new Event('submit', { bubbles: true, cancelable: true }));\n await tick();\n },\n\n async focus(el: HTMLElement): Promise<void> {\n el.focus();\n el.dispatchEvent(new FocusEvent('focus', { bubbles: true }));\n await tick();\n },\n\n async blur(el: HTMLElement): Promise<void> {\n el.blur();\n el.dispatchEvent(new FocusEvent('blur', { bubbles: true }));\n await tick();\n },\n\n async keyDown(el: HTMLElement, key: string, options?: KeyboardEventInit): Promise<void> {\n el.dispatchEvent(new KeyboardEvent('keydown', { key, bubbles: true, ...options }));\n await tick();\n },\n\n async keyUp(el: HTMLElement, key: string, options?: KeyboardEventInit): Promise<void> {\n el.dispatchEvent(new KeyboardEvent('keyup', { key, bubbles: true, ...options }));\n await tick();\n },\n};\n\n// --- Internal helpers ---\n\n/** Wait one microtask for effects to flush */\nfunction tick(): Promise<void> {\n return new Promise((resolve) => queueMicrotask(resolve));\n}\n\n/** Walk DOM tree to find the deepest element containing text */\nfunction findByText(root: HTMLElement, text: string): HTMLElement | null {\n // Walk all descendant elements; keep the deepest match\n let best: HTMLElement | null = null;\n\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);\n let node: Node | null = walker.nextNode(); // skip root itself on first call\n\n while (node) {\n if (node instanceof HTMLElement && node.textContent?.includes(text)) {\n best = node; // deeper elements overwrite shallower ones\n }\n node = walker.nextNode();\n }\n\n // If no descendant matched, check root itself\n if (!best && root.textContent?.includes(text)) {\n best = root;\n }\n\n return best;\n}\n\nfunction getDirectTextContent(el: HTMLElement): string {\n let text = '';\n for (const child of el.childNodes) {\n if (child.nodeType === Node.TEXT_NODE) {\n text += child.textContent ?? '';\n }\n }\n return text;\n}\n\n/** Map of HTML tag names to their implicit ARIA roles */\nconst IMPLICIT_ROLES: Record<string, string[]> = {\n button: ['button'],\n a: ['link'],\n input: ['textbox', 'checkbox', 'radio', 'spinbutton', 'slider'],\n select: ['combobox', 'listbox'],\n textarea: ['textbox'],\n img: ['img'],\n form: ['form'],\n nav: ['navigation'],\n main: ['main'],\n header: ['banner'],\n footer: ['contentinfo'],\n aside: ['complementary'],\n section: ['region'],\n article: ['article'],\n ul: ['list'],\n ol: ['list'],\n li: ['listitem'],\n table: ['table'],\n th: ['columnheader'],\n td: ['cell'],\n h1: ['heading'],\n h2: ['heading'],\n h3: ['heading'],\n h4: ['heading'],\n h5: ['heading'],\n h6: ['heading'],\n};\n\nfunction findImplicitRole(root: HTMLElement, role: string): HTMLElement | null {\n // Find tags that implicitly have this role\n const tags: string[] = [];\n for (const [tag, roles] of Object.entries(IMPLICIT_ROLES)) {\n if (roles.includes(role)) tags.push(tag);\n }\n\n if (tags.length === 0) return null;\n\n const selector = tags.join(', ');\n return root.querySelector<HTMLElement>(selector);\n}\n\n// =========================================================================\n// Async helpers\n// =========================================================================\n\nexport interface WaitForOptions {\n /** Timeout in ms (default: 1000) */\n timeout?: number;\n /** Poll interval in ms (default: 50) */\n interval?: number;\n}\n\n/**\n * Wait for an assertion to pass. Retries until it doesn't throw or times out.\n *\n * ```ts\n * await waitFor(() => expect(getByText('loaded')).toBeTruthy());\n * ```\n */\nexport async function waitFor(\n assertion: () => void | Promise<void>,\n options?: WaitForOptions,\n): Promise<void> {\n const { timeout = 1000, interval = 50 } = options ?? {};\n const start = Date.now();\n\n while (true) {\n try {\n await assertion();\n return;\n } catch (err) {\n if (Date.now() - start >= timeout) throw err;\n await new Promise(r => setTimeout(r, interval));\n }\n }\n}\n\n/**\n * Wait for an element matching a selector to appear in the container.\n *\n * ```ts\n * const el = await waitForElement(container, '.loaded');\n * ```\n */\nexport async function waitForElement(\n container: HTMLElement,\n selector: string,\n options?: WaitForOptions,\n): Promise<HTMLElement> {\n let found: HTMLElement | null = null;\n await waitFor(() => {\n found = container.querySelector<HTMLElement>(selector);\n if (!found) throw new Error(`Element \"${selector}\" not found`);\n }, options);\n return found!;\n}\n\n/**\n * Synchronously flush all pending effects. Use after signal writes\n * when you need the DOM to update before asserting.\n *\n * ```ts\n * count.set(5);\n * flush();\n * expect(getByText('5')).toBeTruthy();\n * ```\n */\nexport { flushSync as flush };\n\n// =========================================================================\n// Signal test helpers\n// =========================================================================\n\nexport interface TestSignal<T> extends Signal<T> {\n /** History of all values set on this signal (including initial) */\n history: T[];\n /** Number of times set() or update() was called */\n setCount: number;\n /** Reset history and set count */\n resetHistory(): void;\n}\n\n/**\n * Create a signal with test inspection capabilities.\n *\n * ```ts\n * const count = createTestSignal(0);\n * count.set(1);\n * count.set(2);\n * expect(count.history).toEqual([0, 1, 2]);\n * expect(count.setCount).toBe(2);\n * ```\n */\nexport function createTestSignal<T>(initialValue: T): TestSignal<T> {\n const inner = signal(initialValue);\n const history: T[] = [initialValue];\n let setCount = 0;\n\n const read = (() => inner()) as TestSignal<T>;\n\n read.set = (value: T) => {\n inner.set(value);\n history.push(value);\n setCount++;\n };\n\n read.update = (fn: (prev: T) => T) => {\n const newVal = fn(inner.peek());\n read.set(newVal);\n };\n\n read.peek = () => inner.peek();\n\n Object.defineProperty(read, 'history', { get: () => [...history] });\n Object.defineProperty(read, 'setCount', { get: () => setCount });\n\n read.resetHistory = () => {\n history.length = 0;\n history.push(inner.peek());\n setCount = 0;\n };\n\n return read;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/test.ts"],"names":["mount","component","options","container","props","provides","node","defineComponent","key","value","provide","mountedContainers","text","el","findByText","role","findImplicitRole","id","selector","fireEvent","tick","nativeInputValueSetter","resolve","root","best","walker","IMPLICIT_ROLES","tags","tag","roles","waitFor","assertion","timeout","interval","start","err","r","waitForElement","found","createTestSignal","initialValue","inner","signal","history","setCount","read","fn","newVal","cleanup","clearStores","createTestStore","useStore","store","mockFetch","responses","defaultStatus","recorded","input","init","url","method","body","headers","v","k","path","responseData","responseStatus","responseBody","_status","rest","isNullBody","mockQueryClient","cache"],"mappings":"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","file":"test.js","sourcesContent":["/**\n * @akashjs/runtime/test — Test utilities.\n *\n * Provides mount(), fireEvent, and query helpers for testing\n * AkashJS components with Vitest (or any test runner).\n *\n * No TestBed, no module configuration, no compileComponents().\n * Just mount a component and query the resulting DOM.\n */\n\nimport { defineComponent } from './component.js';\nimport { provide } from './context.js';\nimport { signal } from './signals.js';\nimport { flushSync } from './scheduler.js';\nimport { clearStores } from './store.js';\nimport type { Signal } from './signals.js';\nimport type { Component } from './component.js';\nimport type { InjectionKey } from './context.js';\nimport type { Store } from './store.js';\n\n// --- Mount result ---\n\nexport interface MountResult {\n /** The root container element */\n container: HTMLElement;\n /** Unmount the component and clean up */\n unmount(): void;\n /** Find the first element whose text content contains the given string */\n getByText(text: string): HTMLElement;\n /** Find the first element with the given ARIA role */\n getByRole(role: string): HTMLElement;\n /** Find the first element with the given data-testid */\n getByTestId(id: string): HTMLElement;\n /** Query all elements matching a CSS selector */\n queryAll(selector: string): HTMLElement[];\n /** Query the first element matching a CSS selector, or null */\n query(selector: string): HTMLElement | null;\n}\n\n// --- Mount options ---\n\nexport interface MountOptions<P extends Record<string, unknown>> {\n /** Props to pass to the component */\n props?: P;\n /** Context values to provide (Map of InjectionKey -> value) */\n provide?: Map<InjectionKey<any>, any>;\n}\n\n// --- mount() ---\n\n/**\n * Mount a component into a detached DOM element for testing.\n *\n * ```ts\n * const { getByText, getByRole } = mount(Counter, { props: { initial: 5 } });\n * expect(getByText('Count: 5')).toBeTruthy();\n * ```\n */\nexport function mount<P extends Record<string, unknown> = Record<string, unknown>>(\n component: Component<P>,\n options?: MountOptions<P>,\n): MountResult {\n const container = document.createElement('div');\n container.setAttribute('data-akash-test-root', '');\n\n const props = (options?.props ?? {}) as P & { children?: undefined };\n const provides = options?.provide;\n\n let node: Node;\n\n if (provides && provides.size > 0) {\n // Wrap in a provider component to inject context values\n const Wrapper = defineComponent(() => {\n for (const [key, value] of provides) {\n provide(key, value);\n }\n return () => component(props);\n });\n node = Wrapper({});\n } else {\n node = component(props);\n }\n\n container.appendChild(node);\n\n // Attach to document so queries work properly\n document.body.appendChild(container);\n mountedContainers.push(container);\n\n return {\n container,\n\n unmount() {\n container.remove();\n },\n\n getByText(text: string): HTMLElement {\n const el = findByText(container, text);\n if (!el) {\n throw new Error(\n `[AkashJS Test] Could not find element with text: \"${text}\"\\n` +\n ` Container HTML: ${container.innerHTML.slice(0, 200)}`,\n );\n }\n return el;\n },\n\n getByRole(role: string): HTMLElement {\n const el = container.querySelector<HTMLElement>(`[role=\"${role}\"]`)\n ?? findImplicitRole(container, role);\n if (!el) {\n throw new Error(\n `[AkashJS Test] Could not find element with role: \"${role}\"\\n` +\n ` Container HTML: ${container.innerHTML.slice(0, 200)}`,\n );\n }\n return el;\n },\n\n getByTestId(id: string): HTMLElement {\n const el = container.querySelector<HTMLElement>(`[data-testid=\"${id}\"]`);\n if (!el) {\n throw new Error(\n `[AkashJS Test] Could not find element with data-testid: \"${id}\"\\n` +\n ` Container HTML: ${container.innerHTML.slice(0, 200)}`,\n );\n }\n return el;\n },\n\n queryAll(selector: string): HTMLElement[] {\n return Array.from(container.querySelectorAll<HTMLElement>(selector));\n },\n\n query(selector: string): HTMLElement | null {\n return container.querySelector<HTMLElement>(selector);\n },\n };\n}\n\n// --- fireEvent ---\n\n/**\n * Fire DOM events on elements. Returns a promise that resolves\n * after a microtask, giving effects time to flush.\n *\n * ```ts\n * await fireEvent.click(button);\n * await fireEvent.input(input, 'hello');\n * ```\n */\nexport const fireEvent = {\n async click(el: HTMLElement): Promise<void> {\n el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));\n await tick();\n },\n\n async input(el: HTMLInputElement | HTMLTextAreaElement, value: string): Promise<void> {\n // Set the value property directly (as a user typing would)\n const nativeInputValueSetter = Object.getOwnPropertyDescriptor(\n el instanceof HTMLTextAreaElement ? HTMLTextAreaElement.prototype : HTMLInputElement.prototype,\n 'value',\n )?.set;\n\n if (nativeInputValueSetter) {\n nativeInputValueSetter.call(el, value);\n } else {\n (el as any).value = value;\n }\n\n el.dispatchEvent(new Event('input', { bubbles: true }));\n el.dispatchEvent(new Event('change', { bubbles: true }));\n await tick();\n },\n\n async submit(el: HTMLFormElement): Promise<void> {\n el.dispatchEvent(new Event('submit', { bubbles: true, cancelable: true }));\n await tick();\n },\n\n async focus(el: HTMLElement): Promise<void> {\n el.focus();\n el.dispatchEvent(new FocusEvent('focus', { bubbles: true }));\n await tick();\n },\n\n async blur(el: HTMLElement): Promise<void> {\n el.blur();\n el.dispatchEvent(new FocusEvent('blur', { bubbles: true }));\n await tick();\n },\n\n async keyDown(el: HTMLElement, key: string, options?: KeyboardEventInit): Promise<void> {\n el.dispatchEvent(new KeyboardEvent('keydown', { key, bubbles: true, ...options }));\n await tick();\n },\n\n async keyUp(el: HTMLElement, key: string, options?: KeyboardEventInit): Promise<void> {\n el.dispatchEvent(new KeyboardEvent('keyup', { key, bubbles: true, ...options }));\n await tick();\n },\n};\n\n// --- Internal helpers ---\n\n/** Wait one microtask for effects to flush */\nfunction tick(): Promise<void> {\n return new Promise((resolve) => queueMicrotask(resolve));\n}\n\n/** Walk DOM tree to find the deepest element containing text */\nfunction findByText(root: HTMLElement, text: string): HTMLElement | null {\n // Walk all descendant elements; keep the deepest match\n let best: HTMLElement | null = null;\n\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);\n let node: Node | null = walker.nextNode(); // skip root itself on first call\n\n while (node) {\n if (node instanceof HTMLElement && node.textContent?.includes(text)) {\n best = node; // deeper elements overwrite shallower ones\n }\n node = walker.nextNode();\n }\n\n // If no descendant matched, check root itself\n if (!best && root.textContent?.includes(text)) {\n best = root;\n }\n\n return best;\n}\n\nfunction getDirectTextContent(el: HTMLElement): string {\n let text = '';\n for (const child of el.childNodes) {\n if (child.nodeType === Node.TEXT_NODE) {\n text += child.textContent ?? '';\n }\n }\n return text;\n}\n\n/** Map of HTML tag names to their implicit ARIA roles */\nconst IMPLICIT_ROLES: Record<string, string[]> = {\n button: ['button'],\n a: ['link'],\n input: ['textbox', 'checkbox', 'radio', 'spinbutton', 'slider'],\n select: ['combobox', 'listbox'],\n textarea: ['textbox'],\n img: ['img'],\n form: ['form'],\n nav: ['navigation'],\n main: ['main'],\n header: ['banner'],\n footer: ['contentinfo'],\n aside: ['complementary'],\n section: ['region'],\n article: ['article'],\n ul: ['list'],\n ol: ['list'],\n li: ['listitem'],\n table: ['table'],\n th: ['columnheader'],\n td: ['cell'],\n h1: ['heading'],\n h2: ['heading'],\n h3: ['heading'],\n h4: ['heading'],\n h5: ['heading'],\n h6: ['heading'],\n};\n\nfunction findImplicitRole(root: HTMLElement, role: string): HTMLElement | null {\n // Find tags that implicitly have this role\n const tags: string[] = [];\n for (const [tag, roles] of Object.entries(IMPLICIT_ROLES)) {\n if (roles.includes(role)) tags.push(tag);\n }\n\n if (tags.length === 0) return null;\n\n const selector = tags.join(', ');\n return root.querySelector<HTMLElement>(selector);\n}\n\n// =========================================================================\n// Async helpers\n// =========================================================================\n\nexport interface WaitForOptions {\n /** Timeout in ms (default: 1000) */\n timeout?: number;\n /** Poll interval in ms (default: 50) */\n interval?: number;\n}\n\n/**\n * Wait for an assertion to pass. Retries until it doesn't throw or times out.\n *\n * ```ts\n * await waitFor(() => expect(getByText('loaded')).toBeTruthy());\n * ```\n */\nexport async function waitFor(\n assertion: () => void | Promise<void>,\n options?: WaitForOptions,\n): Promise<void> {\n const { timeout = 1000, interval = 50 } = options ?? {};\n const start = Date.now();\n\n while (true) {\n try {\n await assertion();\n return;\n } catch (err) {\n if (Date.now() - start >= timeout) throw err;\n await new Promise(r => setTimeout(r, interval));\n }\n }\n}\n\n/**\n * Wait for an element matching a selector to appear in the container.\n *\n * ```ts\n * const el = await waitForElement(container, '.loaded');\n * ```\n */\nexport async function waitForElement(\n container: HTMLElement,\n selector: string,\n options?: WaitForOptions,\n): Promise<HTMLElement> {\n let found: HTMLElement | null = null;\n await waitFor(() => {\n found = container.querySelector<HTMLElement>(selector);\n if (!found) throw new Error(`Element \"${selector}\" not found`);\n }, options);\n return found!;\n}\n\n/**\n * Synchronously flush all pending effects. Use after signal writes\n * when you need the DOM to update before asserting.\n *\n * ```ts\n * count.set(5);\n * flush();\n * expect(getByText('5')).toBeTruthy();\n * ```\n */\nexport { flushSync as flush };\n\n// =========================================================================\n// Signal test helpers\n// =========================================================================\n\nexport interface TestSignal<T> extends Signal<T> {\n /** History of all values set on this signal (including initial) */\n history: T[];\n /** Number of times set() or update() was called */\n setCount: number;\n /** Reset history and set count */\n resetHistory(): void;\n}\n\n/**\n * Create a signal with test inspection capabilities.\n *\n * ```ts\n * const count = createTestSignal(0);\n * count.set(1);\n * count.set(2);\n * expect(count.history).toEqual([0, 1, 2]);\n * expect(count.setCount).toBe(2);\n * ```\n */\nexport function createTestSignal<T>(initialValue: T): TestSignal<T> {\n const inner = signal(initialValue);\n const history: T[] = [initialValue];\n let setCount = 0;\n\n const read = (() => inner()) as TestSignal<T>;\n\n read.set = (value: T) => {\n inner.set(value);\n history.push(value);\n setCount++;\n };\n\n read.update = (fn: (prev: T) => T) => {\n const newVal = fn(inner.peek());\n read.set(newVal);\n };\n\n read.peek = () => inner.peek();\n\n Object.defineProperty(read, 'history', { get: () => [...history] });\n Object.defineProperty(read, 'setCount', { get: () => setCount });\n\n read.resetHistory = () => {\n history.length = 0;\n history.push(inner.peek());\n setCount = 0;\n };\n\n return read;\n}\n\n// =========================================================================\n// Store test helpers\n// =========================================================================\n\n/** Track mounted containers for cleanup */\nconst mountedContainers: HTMLElement[] = [];\n\n/**\n * Unmount all rendered components and clear store singletons.\n * Call in afterEach() for clean test isolation.\n */\nexport function cleanup(): void {\n for (const container of mountedContainers) {\n container.remove();\n }\n mountedContainers.length = 0;\n clearStores();\n}\n\n/**\n * Create a fresh store instance for testing, bypassing the singleton cache.\n * Automatically calls clearStores() first so the factory returns a new instance.\n *\n * ```ts\n * const store = createTestStore(useCounter);\n * store.increment();\n * expect(store.count()).toBe(1);\n * ```\n */\nexport function createTestStore<S, G, A>(\n useStore: () => Store<S, G, A>,\n): Store<S, G, A> {\n clearStores();\n const store = useStore();\n // Remove from singleton cache so subsequent useStore() calls create\n // independent instances, not the same test store\n clearStores();\n return store;\n}\n\n// =========================================================================\n// Mock fetch\n// =========================================================================\n\nexport interface MockFetchCall {\n url: string;\n method: string;\n body?: unknown;\n headers: Record<string, string>;\n}\n\nexport interface MockFetchInstance {\n /** The mock fetch function — pass to createHttpClient({ fetch }) */\n (input: RequestInfo | URL, init?: RequestInit): Promise<Response>;\n /** All recorded calls */\n calls: () => MockFetchCall[];\n /** Number of calls made */\n callCount: () => number;\n /** Reset call history */\n reset: () => void;\n}\n\n/**\n * Create a mock fetch that returns predefined responses.\n *\n * ```ts\n * const fetch = mockFetch({\n * '/api/users': [{ id: 1, name: 'Alice' }],\n * '/api/users/1': { id: 1, name: 'Alice' },\n * });\n * const res = await fetch('/api/users');\n * const data = await res.json(); // [{ id: 1, name: 'Alice' }]\n * expect(fetch.callCount()).toBe(1);\n * ```\n */\nexport function mockFetch(\n responses: Record<string, unknown> = {},\n defaultStatus = 200,\n): MockFetchInstance {\n const recorded: MockFetchCall[] = [];\n\n const fn = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {\n const url = typeof input === 'string' ? input\n : input instanceof URL ? input.toString()\n : input.url;\n const method = init?.method ?? 'GET';\n let body: unknown;\n if (init?.body) {\n try { body = JSON.parse(String(init.body)); } catch { body = init.body; }\n }\n const headers: Record<string, string> = {};\n if (init?.headers) {\n const h = new Headers(init.headers);\n h.forEach((v, k) => { headers[k] = v; });\n }\n\n recorded.push({ url, method, body, headers });\n\n // Match response by URL path\n const path = url.replace(/^https?:\\/\\/[^/]+/, '').split('?')[0];\n const responseData = responses[path] ?? responses[url];\n\n if (responseData === undefined) {\n return new Response('Not Found', { status: 404 });\n }\n\n // Support _status convention: { _status: 500, error: 'msg' }\n let responseStatus = defaultStatus;\n let responseBody: unknown = responseData;\n if (responseBody && typeof responseBody === 'object' && !Array.isArray(responseBody) && '_status' in (responseBody as Record<string, unknown>)) {\n const { _status, ...rest } = responseBody as Record<string, unknown>;\n responseStatus = Number(_status);\n responseBody = rest;\n }\n\n // Null-body statuses (204, 304) cannot have a response body\n const isNullBody = responseStatus === 204 || responseStatus === 304;\n return new Response(isNullBody ? null : JSON.stringify(responseBody), {\n status: responseStatus,\n headers: isNullBody ? {} : { 'Content-Type': 'application/json' },\n });\n };\n\n fn.calls = () => [...recorded];\n fn.callCount = () => recorded.length;\n fn.reset = () => { recorded.length = 0; };\n\n return fn as MockFetchInstance;\n}\n\n// =========================================================================\n// Mock query client\n// =========================================================================\n\n/**\n * Create a mock query client for testing. Behaves like createQueryClient\n * but with no real caching — each useCachedQuery gets a fresh context.\n *\n * ```ts\n * const qc = mockQueryClient();\n * const posts = useCachedQuery(qc, ['posts'], fetcher);\n * ```\n */\nexport function mockQueryClient(): {\n invalidate: (prefix: unknown) => void;\n setQueryData: (key: unknown, updater: unknown) => void;\n getQueryData: (key: unknown) => unknown;\n removeQuery: (key: unknown) => void;\n clear: () => void;\n _cache: Map<string, unknown>;\n _options: { defaultStaleTime?: number };\n} {\n const cache = new Map<string, unknown>();\n return {\n invalidate: () => {},\n setQueryData: () => {},\n getQueryData: () => undefined,\n removeQuery: (key) => { cache.delete(JSON.stringify(key)); },\n clear: () => { cache.clear(); },\n _cache: cache,\n _options: { defaultStaleTime: 0 },\n };\n}\n"]}
|
package/package.json
CHANGED
package/dist/chunk-6NX6JRSV.cjs
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
'use strict';var chunk3AL2DVPZ_cjs=require('./chunk-3AL2DVPZ.cjs');function f(r,e={}){let t=chunk3AL2DVPZ_cjs.l(false),a=chunk3AL2DVPZ_cjs.l(false),s=chunk3AL2DVPZ_cjs.l(!navigator.onLine),i=null;window.addEventListener("online",()=>s.set(false)),window.addEventListener("offline",()=>s.set(true));let u=new Promise((h,l)=>{if(!("serviceWorker"in navigator)){l(new Error("Service workers not supported"));return}navigator.serviceWorker.register(r,{scope:e.scope}).then(n=>{i=n,t.set(true),e.onReady?.(n),h(n),n.addEventListener("updatefound",()=>{let o=n.installing;o&&o.addEventListener("statechange",()=>{o.state==="installed"&&navigator.serviceWorker.controller&&(a.set(true),e.onUpdate?.(n),e.autoReload&&(o.postMessage({type:"SKIP_WAITING"}),window.location.reload()));});});}).catch(n=>{e.onError?.(n),l(n);});});return {registered:()=>t(),updateAvailable:()=>a(),offline:()=>s(),ready:u,async update(){i&&await i.update();},skipWaiting(){i?.waiting&&(i.waiting.postMessage({type:"SKIP_WAITING"}),window.location.reload());},async unregister(){return i?i.unregister():false}}}function v(r){let e=`// Auto-generated service worker by AkashJS
|
|
2
|
-
const CACHE_VERSION = 'v1';
|
|
3
|
-
|
|
4
|
-
self.addEventListener('install', (event) => {
|
|
5
|
-
self.skipWaiting();
|
|
6
|
-
});
|
|
7
|
-
|
|
8
|
-
self.addEventListener('activate', (event) => {
|
|
9
|
-
event.waitUntil(clients.claim());
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
self.addEventListener('message', (event) => {
|
|
13
|
-
if (event.data?.type === 'SKIP_WAITING') {
|
|
14
|
-
self.skipWaiting();
|
|
15
|
-
}
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
self.addEventListener('fetch', (event) => {
|
|
19
|
-
const url = new URL(event.request.url);
|
|
20
|
-
`;for(let t of r){let a=t.match instanceof RegExp?t.match.toString():`new RegExp(${JSON.stringify(t.match.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"))})`,s=t.cacheName??"akash-cache";e+=`
|
|
21
|
-
if (${a}.test(url.pathname)) {
|
|
22
|
-
event.respondWith(${p(t.strategy,s)});
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
`;}return e+=`
|
|
26
|
-
// Default: network-first
|
|
27
|
-
event.respondWith(
|
|
28
|
-
fetch(event.request).catch(() => caches.match(event.request))
|
|
29
|
-
);
|
|
30
|
-
});
|
|
31
|
-
`,e}function p(r,e){switch(r){case "cache-first":return `caches.match(event.request).then(cached => cached || fetch(event.request).then(response => { const clone = response.clone(); caches.open('${e}').then(cache => cache.put(event.request, clone)); return response; }))`;case "network-first":return `fetch(event.request).then(response => { const clone = response.clone(); caches.open('${e}').then(cache => cache.put(event.request, clone)); return response; }).catch(() => caches.match(event.request))`;case "stale-while-revalidate":return `caches.match(event.request).then(cached => { const fetchPromise = fetch(event.request).then(response => { caches.open('${e}').then(cache => cache.put(event.request, response.clone())); return response; }); return cached || fetchPromise; })`;case "network-only":return "fetch(event.request)";case "cache-only":return "caches.match(event.request)"}}async function y(r,e){return await Notification.requestPermission()!=="granted"?null:await r.pushManager.subscribe({userVisibleOnly:true,applicationServerKey:d(e)})}function d(r){let e="=".repeat((4-r.length%4)%4),t=(r+e).replace(/-/g,"+").replace(/_/g,"/"),a=atob(t);return Uint8Array.from(a,s=>s.charCodeAt(0))}exports.a=f;exports.b=v;exports.c=y;//# sourceMappingURL=chunk-6NX6JRSV.cjs.map
|
|
32
|
-
//# sourceMappingURL=chunk-6NX6JRSV.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/pwa.ts"],"names":["registerServiceWorker","swUrl","options","registered","signal","updateAvailable","offline","registration","ready","resolve","reject","reg","newWorker","err","generateSWScript","routes","script","route","pattern","cacheName","generateStrategyCode","strategy","subscribePush","vapidPublicKey","urlBase64ToUint8Array","base64String","padding","base64","rawData","char"],"mappings":"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","file":"chunk-6NX6JRSV.cjs","sourcesContent":["/**\n * PWA / Service Worker support.\n *\n * Register service workers, configure caching strategies,\n * handle updates, and manage push notifications.\n *\n * ```ts\n * const sw = registerServiceWorker('/sw.js', {\n * onUpdate: () => toast.info('New version available!'),\n * });\n * sw.ready; // Promise<ServiceWorkerRegistration>\n * sw.update(); // check for updates\n * ```\n */\n\nimport { signal } from './signals.js';\nimport type { ReadonlySignal } from './signals.js';\n\n// =========================================================================\n// Types\n// =========================================================================\n\nexport interface SWOptions {\n /** Callback when a new version is available */\n onUpdate?: (registration: ServiceWorkerRegistration) => void;\n /** Callback when the SW is ready */\n onReady?: (registration: ServiceWorkerRegistration) => void;\n /** Callback on registration error */\n onError?: (error: Error) => void;\n /** Auto-reload on update (default: false) */\n autoReload?: boolean;\n /** Scope of the service worker */\n scope?: string;\n}\n\nexport interface SWRegistration {\n /** Whether the SW is registered */\n registered: ReadonlySignal<boolean>;\n /** Whether an update is available */\n updateAvailable: ReadonlySignal<boolean>;\n /** Whether the app is running offline */\n offline: ReadonlySignal<boolean>;\n /** The raw SW registration (available after ready) */\n ready: Promise<ServiceWorkerRegistration>;\n /** Check for updates */\n update(): Promise<void>;\n /** Skip waiting and activate new SW */\n skipWaiting(): void;\n /** Unregister the service worker */\n unregister(): Promise<boolean>;\n}\n\n// =========================================================================\n// Service Worker registration\n// =========================================================================\n\n/**\n * Register a service worker with update detection.\n */\nexport function registerServiceWorker(\n swUrl: string,\n options: SWOptions = {},\n): SWRegistration {\n const registered = signal(false);\n const updateAvailable = signal(false);\n const offline = signal(!navigator.onLine);\n\n let registration: ServiceWorkerRegistration | null = null;\n\n // Track online/offline\n window.addEventListener('online', () => offline.set(false));\n window.addEventListener('offline', () => offline.set(true));\n\n const ready = new Promise<ServiceWorkerRegistration>((resolve, reject) => {\n if (!('serviceWorker' in navigator)) {\n reject(new Error('Service workers not supported'));\n return;\n }\n\n navigator.serviceWorker\n .register(swUrl, { scope: options.scope })\n .then((reg) => {\n registration = reg;\n registered.set(true);\n options.onReady?.(reg);\n resolve(reg);\n\n // Listen for updates\n reg.addEventListener('updatefound', () => {\n const newWorker = reg.installing;\n if (!newWorker) return;\n\n newWorker.addEventListener('statechange', () => {\n if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {\n updateAvailable.set(true);\n options.onUpdate?.(reg);\n\n if (options.autoReload) {\n newWorker.postMessage({ type: 'SKIP_WAITING' });\n window.location.reload();\n }\n }\n });\n });\n })\n .catch((err) => {\n options.onError?.(err);\n reject(err);\n });\n });\n\n return {\n registered: () => registered(),\n updateAvailable: () => updateAvailable(),\n offline: () => offline(),\n ready,\n async update() {\n if (registration) {\n await registration.update();\n }\n },\n skipWaiting() {\n if (registration?.waiting) {\n registration.waiting.postMessage({ type: 'SKIP_WAITING' });\n window.location.reload();\n }\n },\n async unregister() {\n if (registration) {\n return registration.unregister();\n }\n return false;\n },\n };\n}\n\n// =========================================================================\n// Caching strategies (for SW scripts)\n// =========================================================================\n\nexport type CacheStrategy = 'cache-first' | 'network-first' | 'stale-while-revalidate' | 'network-only' | 'cache-only';\n\nexport interface CacheRoute {\n /** URL pattern to match (string or regex) */\n match: string | RegExp;\n /** Caching strategy */\n strategy: CacheStrategy;\n /** Cache name */\n cacheName?: string;\n /** Max entries in cache */\n maxEntries?: number;\n /** Max age in seconds */\n maxAgeSeconds?: number;\n}\n\n/**\n * Generate a service worker script from cache route configs.\n *\n * ```ts\n * const swScript = generateSWScript([\n * { match: /\\.(?:js|css)$/, strategy: 'cache-first', cacheName: 'assets' },\n * { match: '/api/', strategy: 'network-first', maxAgeSeconds: 300 },\n * { match: /\\.(?:png|jpg|svg)$/, strategy: 'cache-first', cacheName: 'images' },\n * ]);\n * ```\n */\nexport function generateSWScript(routes: CacheRoute[]): string {\n let script = `// Auto-generated service worker by AkashJS\nconst CACHE_VERSION = 'v1';\n\nself.addEventListener('install', (event) => {\n self.skipWaiting();\n});\n\nself.addEventListener('activate', (event) => {\n event.waitUntil(clients.claim());\n});\n\nself.addEventListener('message', (event) => {\n if (event.data?.type === 'SKIP_WAITING') {\n self.skipWaiting();\n }\n});\n\nself.addEventListener('fetch', (event) => {\n const url = new URL(event.request.url);\n`;\n\n for (const route of routes) {\n const pattern = route.match instanceof RegExp\n ? route.match.toString()\n : `new RegExp(${JSON.stringify(route.match.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'))})`;\n\n const cacheName = route.cacheName ?? 'akash-cache';\n\n script += `\n if (${pattern}.test(url.pathname)) {\n event.respondWith(${generateStrategyCode(route.strategy, cacheName)});\n return;\n }\n`;\n }\n\n script += `\n // Default: network-first\n event.respondWith(\n fetch(event.request).catch(() => caches.match(event.request))\n );\n});\n`;\n\n return script;\n}\n\nfunction generateStrategyCode(strategy: CacheStrategy, cacheName: string): string {\n switch (strategy) {\n case 'cache-first':\n return `caches.match(event.request).then(cached => cached || fetch(event.request).then(response => { const clone = response.clone(); caches.open('${cacheName}').then(cache => cache.put(event.request, clone)); return response; }))`;\n case 'network-first':\n return `fetch(event.request).then(response => { const clone = response.clone(); caches.open('${cacheName}').then(cache => cache.put(event.request, clone)); return response; }).catch(() => caches.match(event.request))`;\n case 'stale-while-revalidate':\n return `caches.match(event.request).then(cached => { const fetchPromise = fetch(event.request).then(response => { caches.open('${cacheName}').then(cache => cache.put(event.request, response.clone())); return response; }); return cached || fetchPromise; })`;\n case 'network-only':\n return `fetch(event.request)`;\n case 'cache-only':\n return `caches.match(event.request)`;\n }\n}\n\n// =========================================================================\n// Push notifications\n// =========================================================================\n\n/**\n * Request push notification permission and subscribe.\n */\nexport async function subscribePush(\n registration: ServiceWorkerRegistration,\n vapidPublicKey: string,\n): Promise<PushSubscription | null> {\n const permission = await Notification.requestPermission();\n if (permission !== 'granted') return null;\n\n const subscription = await registration.pushManager.subscribe({\n userVisibleOnly: true,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n applicationServerKey: urlBase64ToUint8Array(vapidPublicKey) as any,\n });\n\n return subscription;\n}\n\nfunction urlBase64ToUint8Array(base64String: string): Uint8Array {\n const padding = '='.repeat((4 - (base64String.length % 4)) % 4);\n const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/');\n const rawData = atob(base64);\n return Uint8Array.from(rawData, (char) => char.charCodeAt(0));\n}\n"]}
|
package/dist/chunk-772SU4MG.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import {l,m,n}from'./chunk-POLTPHUA.js';var k=[];function v(s){k.push(...s.plugins);}var g=new Map;function R(s,a){return ()=>{if(g.has(s))return g.get(s);let r=a.state(),o=A(s,r,a);return g.set(s,o),o}}function A(s,a,r){let o={},c=Object.keys(a);for(let t of c)o[t]=l(a[t]);let n$1={$id:s};for(let t of c)n$1[t]=o[t];if(r.getters)for(let[t,e]of Object.entries(r.getters))n$1[t]=m(()=>e.call(n$1,o));let y=[...k,...r.plugins??[]];if(r.actions)for(let[t,e]of Object.entries(r.actions))n$1[t]=(...i)=>{for(let f of y)f.onAction?.(n$1,t,i);return e.apply(n$1,i)};n$1.$reset=()=>{let t=r.state();for(let e of c)o[e].set(t[e]);},n$1.$patch=t=>{if(typeof t=="function")t(o);else for(let[e,i]of Object.entries(t))e in o&&o[e].set(i);},n$1.$snapshot=()=>{let t={};for(let e of c)t[e]=o[e]();return t};let l$1=new Set,S=null;n$1.$subscribe=t=>{if(l$1.add(t),!S){let e=true;S=n(()=>{for(let f of c)o[f]();if(e){e=false;return}let i=n$1.$snapshot();for(let f of l$1)f(i);});}return ()=>{l$1.delete(t),l$1.size===0&&S&&(S(),S=null);}};for(let t of y)t.init?.(n$1);return n$1}function x(){g.clear();}function b(){return Object.fromEntries(g)}export{v as a,R as b,x as c,b as d};//# sourceMappingURL=chunk-772SU4MG.js.map
|
|
2
|
-
//# sourceMappingURL=chunk-772SU4MG.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/store.ts"],"names":["globalPlugins","configureStores","options","storeInstances","defineStore","id","definition","initialState","store","createStoreInstance","stateSignals","stateKeys","key","signal","getterFn","computed","plugins","actionFn","args","plugin","fresh","partialOrFn","value","snapshot","subscribers","subscribeEffect","callback","isInitial","effect","cb","clearStores","__getStoreInstances"],"mappings":"wCA2FA,IAAMA,EAA+B,EAAC,CAG/B,SAASC,CAAAA,CAAgBC,EAA2C,CACzEF,CAAAA,CAAc,IAAA,CAAK,GAAGE,EAAQ,OAAO,EACvC,CAIA,IAAMC,CAAAA,CAAiB,IAAI,GAAA,CAQpB,SAASC,CAAAA,CAKdC,CAAAA,CACAC,EACsB,CACtB,OAAO,IAAM,CAEX,GAAIH,CAAAA,CAAe,GAAA,CAAIE,CAAE,CAAA,CACvB,OAAOF,CAAAA,CAAe,GAAA,CAAIE,CAAE,CAAA,CAG9B,IAAME,EAAeD,CAAAA,CAAW,KAAA,EAAM,CAChCE,CAAAA,CAAQC,EAAoBJ,CAAAA,CAAIE,CAAAA,CAAcD,CAAU,CAAA,CAC9D,OAAAH,CAAAA,CAAe,GAAA,CAAIE,CAAAA,CAAIG,CAAK,EACrBA,CACT,CACF,CAEA,SAASC,CAAAA,CAKPJ,EACAE,CAAAA,CACAD,CAAAA,CACgB,CAEhB,IAAMI,EAAgD,EAAC,CACjDC,CAAAA,CAAY,MAAA,CAAO,KAAKJ,CAAY,CAAA,CAE1C,IAAA,IAAWK,CAAAA,IAAOD,EAChBD,CAAAA,CAAaE,CAAG,EAAIC,CAAAA,CAAON,CAAAA,CAAaK,CAAG,CAAC,CAAA,CAI9C,IAAMJ,GAAAA,CAAa,CAAE,GAAA,CAAKH,CAAG,CAAA,CAG7B,IAAA,IAAWO,KAAOD,CAAAA,CAChBH,GAAAA,CAAMI,CAAG,CAAA,CAAIF,EAAaE,CAAG,CAAA,CAI/B,GAAIN,CAAAA,CAAW,OAAA,CACb,OAAW,CAACM,CAAAA,CAAKE,CAAQ,CAAA,GAAK,OAAO,OAAA,CAAQR,CAAAA,CAAW,OAAO,CAAA,CAC7DE,IAAMI,CAAG,CAAA,CAAIG,CAAAA,CAAS,IACnBD,EAAsB,IAAA,CAAKN,GAAAA,CAAOE,CAAY,CACjD,CAAA,CAKJ,IAAMM,CAAAA,CAAU,CAAC,GAAGhB,CAAAA,CAAe,GAAIM,CAAAA,CAAW,OAAA,EAAW,EAAG,EAGhE,GAAIA,CAAAA,CAAW,OAAA,CACb,IAAA,GAAW,CAACM,CAAAA,CAAKK,CAAQ,IAAK,MAAA,CAAO,OAAA,CAAQX,EAAW,OAAO,CAAA,CAC7DE,GAAAA,CAAMI,CAAG,EAAI,CAAA,GAAIM,CAAAA,GAAoB,CACnC,IAAA,IAAWC,KAAUH,CAAAA,CAASG,CAAAA,CAAO,QAAA,GAAWX,GAAAA,CAAOI,EAAKM,CAAI,CAAA,CAChE,OAAQD,CAAAA,CAAsB,KAAA,CAAMT,IAAOU,CAAI,CACjD,CAAA,CAKJV,GAAAA,CAAM,OAAS,IAAM,CACnB,IAAMY,CAAAA,CAAQd,EAAW,KAAA,EAAM,CAC/B,IAAA,IAAWM,CAAAA,IAAOD,EAChBD,CAAAA,CAAaE,CAAG,EAAE,GAAA,CAAIQ,CAAAA,CAAMR,CAAc,CAAC,EAE/C,CAAA,CAGAJ,GAAAA,CAAM,OAAUa,CAAAA,EAAqD,CACnE,GAAI,OAAOA,GAAgB,UAAA,CACzBA,CAAAA,CAAYX,CAAY,CAAA,CAAA,YAEb,CAACE,CAAAA,CAAKU,CAAK,CAAA,GAAK,MAAA,CAAO,QAAQD,CAAW,CAAA,CAC/CT,CAAAA,IAAOF,CAAAA,EACTA,EAAaE,CAAG,CAAA,CAAE,GAAA,CAAIU,CAAK,EAInC,CAAA,CAGAd,GAAAA,CAAM,SAAA,CAAY,IAAS,CACzB,IAAMe,CAAAA,CAAoC,EAAC,CAC3C,IAAA,IAAWX,KAAOD,CAAAA,CAChBY,CAAAA,CAASX,CAAG,CAAA,CAAIF,EAAaE,CAAG,CAAA,EAAE,CAEpC,OAAOW,CACT,CAAA,CAGA,IAAMC,GAAAA,CAAc,IAAI,IACpBC,CAAAA,CAAuC,IAAA,CAC3CjB,IAAM,UAAA,CAAckB,CAAAA,EAA+C,CAGjE,GAFAF,GAAAA,CAAY,GAAA,CAAIE,CAAQ,EAEpB,CAACD,CAAAA,CAAiB,CACpB,IAAIE,EAAY,IAAA,CAChBF,CAAAA,CAAkBG,CAAAA,CAAO,IAAM,CAE7B,IAAA,IAAWhB,CAAAA,IAAOD,EAChBD,CAAAA,CAAaE,CAAG,GAAE,CAGpB,GAAIe,CAAAA,CAAW,CACbA,EAAY,KAAA,CACZ,MACF,CACA,IAAMJ,EAAWf,GAAAA,CAAM,SAAA,EAAU,CACjC,IAAA,IAAWqB,KAAML,GAAAA,CACfK,CAAAA,CAAGN,CAAQ,EAEf,CAAC,EACH,CACA,OAAO,IAAM,CACXC,IAAY,MAAA,CAAOE,CAAQ,CAAA,CAEvBF,GAAAA,CAAY,OAAS,CAAA,EAAKC,CAAAA,GAC5BA,CAAAA,EAAgB,CAChBA,EAAkB,IAAA,EAEtB,CACF,EAGA,IAAA,IAAWN,CAAAA,IAAUH,EAASG,CAAAA,CAAO,IAAA,GAAOX,GAAK,CAAA,CAEjD,OAAOA,GACT,CAKO,SAASsB,CAAAA,EAAoB,CAClC3B,CAAAA,CAAe,KAAA,GACjB,CAGO,SAAS4B,CAAAA,EAA4D,CAC1E,OAAO,MAAA,CAAO,WAAA,CAAY5B,CAAc,CAC1C","file":"chunk-772SU4MG.js","sourcesContent":["/**\n * Global state management via defineStore().\n *\n * Stores are singleton signal containers that persist across\n * components. They provide shared state, computed getters,\n * and actions — no external library needed.\n *\n * ```ts\n * const useCounterStore = defineStore('counter', {\n * state: () => ({ count: 0, name: 'Counter' }),\n * getters: {\n * doubled: (state) => state.count() * 2,\n * },\n * actions: {\n * increment() { this.count.update(c => c + 1); },\n * reset() { this.count.set(0); },\n * },\n * });\n *\n * // In any component:\n * const store = useCounterStore();\n * store.count(); // 0\n * store.doubled(); // 0\n * store.increment();\n * store.count(); // 1\n * ```\n */\n\nimport { signal, computed, effect } from './signals.js';\nimport type { Signal, ReadonlySignal } from './signals.js';\n\n// --- Types ---\n\ntype StateFactory<S> = () => S;\n\ntype Getters<S, G> = {\n [K in keyof G]: (state: SignalifiedState<S>) => G[K];\n};\n\ntype Actions<A> = {\n [K in keyof A]: A[K] extends (...args: infer P) => infer R\n ? (...args: P) => R\n : never;\n};\n\n/** Maps plain state values to signals */\ntype SignalifiedState<S> = {\n [K in keyof S]: Signal<S[K]>;\n};\n\n/** The store instance returned to consumers */\nexport type Store<S, G, A> = SignalifiedState<S> & {\n [K in keyof G]: ReadonlySignal<G[K]>;\n} & {\n [K in keyof A]: A[K] extends (...args: infer P) => infer R\n ? (...args: P) => R\n : never;\n} & {\n /** Reset all state to initial values */\n $reset(): void;\n /** Merge partial state into the store, or apply changes via callback */\n $patch(partialOrFn: Partial<S> | ((state: SignalifiedState<S>) => void)): void;\n /** Subscribe to all state changes */\n $subscribe(callback: (state: S) => void): () => void;\n /** Get a plain snapshot of current state */\n $snapshot(): S;\n /** Store ID */\n $id: string;\n};\n\n/** Actions with `this` typed to include state signals, getters, and other actions */\ntype StoreActions<S, G, A> = {\n [K in keyof A]: A[K] extends (...args: infer P) => infer R\n ? (this: SignalifiedState<S> & { [GK in keyof G]: ReadonlySignal<G[GK]> } & A, ...args: P) => R\n : never;\n};\n\nexport interface StoreDefinition<S, G, A> {\n state: StateFactory<S>;\n getters?: Getters<S, G>;\n actions?: StoreActions<S, G, A>;\n plugins?: StorePlugin[];\n}\n\n// --- Plugin system ---\n\nexport interface StorePlugin {\n init?(store: Store<any, any, any>): void;\n onAction?(store: Store<any, any, any>, actionName: string, args: unknown[]): void;\n}\n\nconst globalPlugins: StorePlugin[] = [];\n\n/** Register global plugins that apply to all stores */\nexport function configureStores(options: { plugins: StorePlugin[] }): void {\n globalPlugins.push(...options.plugins);\n}\n\n// --- Store registry (singleton) ---\n\nconst storeInstances = new Map<string, Store<any, any, any>>();\n\n// --- defineStore ---\n\n/**\n * Define a global store. Returns a composable function that\n * always returns the same store instance (singleton).\n */\nexport function defineStore<\n S extends Record<string, unknown>,\n G extends Record<string, unknown> = {},\n A extends Record<string, (...args: any[]) => any> = {},\n>(\n id: string,\n definition: StoreDefinition<S, G, A>,\n): () => Store<S, G, A> {\n return () => {\n // Return existing instance if already created\n if (storeInstances.has(id)) {\n return storeInstances.get(id) as Store<S, G, A>;\n }\n\n const initialState = definition.state();\n const store = createStoreInstance(id, initialState, definition);\n storeInstances.set(id, store);\n return store;\n };\n}\n\nfunction createStoreInstance<\n S extends Record<string, unknown>,\n G extends Record<string, unknown>,\n A extends Record<string, (...args: any[]) => any>,\n>(\n id: string,\n initialState: S,\n definition: StoreDefinition<S, G, A>,\n): Store<S, G, A> {\n // Create signals for each state property\n const stateSignals: Record<string, Signal<unknown>> = {};\n const stateKeys = Object.keys(initialState);\n\n for (const key of stateKeys) {\n stateSignals[key] = signal(initialState[key]);\n }\n\n // Build the store object first so getters can reference other getters via `this`\n const store: any = { $id: id };\n\n // Add state signals\n for (const key of stateKeys) {\n store[key] = stateSignals[key];\n }\n\n // Create computed getters — bound to store so `this.otherGetter()` works\n if (definition.getters) {\n for (const [key, getterFn] of Object.entries(definition.getters)) {\n store[key] = computed(() =>\n (getterFn as Function).call(store, stateSignals),\n );\n }\n }\n\n // Collect all plugins (global + per-store)\n const plugins = [...globalPlugins, ...(definition.plugins ?? [])];\n\n // Bind actions with `this` pointing to the full store (state + getters + actions)\n if (definition.actions) {\n for (const [key, actionFn] of Object.entries(definition.actions)) {\n store[key] = (...args: unknown[]) => {\n for (const plugin of plugins) plugin.onAction?.(store, key, args);\n return (actionFn as Function).apply(store, args);\n };\n }\n }\n\n // $reset\n store.$reset = () => {\n const fresh = definition.state();\n for (const key of stateKeys) {\n stateSignals[key].set(fresh[key as keyof S]);\n }\n };\n\n // $patch — merge partial state or apply via callback\n store.$patch = (partialOrFn: Partial<S> | ((state: any) => void)) => {\n if (typeof partialOrFn === 'function') {\n partialOrFn(stateSignals);\n } else {\n for (const [key, value] of Object.entries(partialOrFn)) {\n if (key in stateSignals) {\n stateSignals[key].set(value);\n }\n }\n }\n };\n\n // $snapshot\n store.$snapshot = (): S => {\n const snapshot: Record<string, unknown> = {};\n for (const key of stateKeys) {\n snapshot[key] = stateSignals[key]();\n }\n return snapshot as S;\n };\n\n // $subscribe — watch all state signals and notify on change\n const subscribers = new Set<(state: S) => void>();\n let subscribeEffect: (() => void) | null = null;\n store.$subscribe = (callback: (state: S) => void): (() => void) => {\n subscribers.add(callback);\n // Start watching if this is the first subscriber\n if (!subscribeEffect) {\n let isInitial = true;\n subscribeEffect = effect(() => {\n // Read all state signals to track them\n for (const key of stateKeys) {\n stateSignals[key]();\n }\n // Skip initial run — only notify on actual changes\n if (isInitial) {\n isInitial = false;\n return;\n }\n const snapshot = store.$snapshot();\n for (const cb of subscribers) {\n cb(snapshot);\n }\n });\n }\n return () => {\n subscribers.delete(callback);\n // Dispose effect when no subscribers remain\n if (subscribers.size === 0 && subscribeEffect) {\n subscribeEffect();\n subscribeEffect = null;\n }\n };\n };\n\n // Initialize plugins\n for (const plugin of plugins) plugin.init?.(store);\n\n return store as Store<S, G, A>;\n}\n\n/**\n * Clear all store instances (useful for testing).\n */\nexport function clearStores(): void {\n storeInstances.clear();\n}\n\n/** @internal — exposes store registry for devtools */\nexport function __getStoreInstances(): Record<string, Store<any, any, any>> {\n return Object.fromEntries(storeInstances);\n}\n"]}
|
package/dist/chunk-7LQZF3XA.cjs
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
'use strict';var chunk3AL2DVPZ_cjs=require('./chunk-3AL2DVPZ.cjs');var k=[];function v(s){k.push(...s.plugins);}var g=new Map;function R(s,a){return ()=>{if(g.has(s))return g.get(s);let r=a.state(),o=A(s,r,a);return g.set(s,o),o}}function A(s,a,r){let o={},c=Object.keys(a);for(let t of c)o[t]=chunk3AL2DVPZ_cjs.l(a[t]);let n={$id:s};for(let t of c)n[t]=o[t];if(r.getters)for(let[t,e]of Object.entries(r.getters))n[t]=chunk3AL2DVPZ_cjs.m(()=>e.call(n,o));let y=[...k,...r.plugins??[]];if(r.actions)for(let[t,e]of Object.entries(r.actions))n[t]=(...i)=>{for(let f of y)f.onAction?.(n,t,i);return e.apply(n,i)};n.$reset=()=>{let t=r.state();for(let e of c)o[e].set(t[e]);},n.$patch=t=>{if(typeof t=="function")t(o);else for(let[e,i]of Object.entries(t))e in o&&o[e].set(i);},n.$snapshot=()=>{let t={};for(let e of c)t[e]=o[e]();return t};let l=new Set,S=null;n.$subscribe=t=>{if(l.add(t),!S){let e=true;S=chunk3AL2DVPZ_cjs.n(()=>{for(let f of c)o[f]();if(e){e=false;return}let i=n.$snapshot();for(let f of l)f(i);});}return ()=>{l.delete(t),l.size===0&&S&&(S(),S=null);}};for(let t of y)t.init?.(n);return n}function x(){g.clear();}function b(){return Object.fromEntries(g)}exports.a=v;exports.b=R;exports.c=x;exports.d=b;//# sourceMappingURL=chunk-7LQZF3XA.cjs.map
|
|
2
|
-
//# sourceMappingURL=chunk-7LQZF3XA.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/store.ts"],"names":["globalPlugins","configureStores","options","storeInstances","defineStore","id","definition","initialState","store","createStoreInstance","stateSignals","stateKeys","key","signal","getterFn","computed","plugins","actionFn","args","plugin","fresh","partialOrFn","value","snapshot","subscribers","subscribeEffect","callback","isInitial","effect","cb","clearStores","__getStoreInstances"],"mappings":"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","file":"chunk-7LQZF3XA.cjs","sourcesContent":["/**\n * Global state management via defineStore().\n *\n * Stores are singleton signal containers that persist across\n * components. They provide shared state, computed getters,\n * and actions — no external library needed.\n *\n * ```ts\n * const useCounterStore = defineStore('counter', {\n * state: () => ({ count: 0, name: 'Counter' }),\n * getters: {\n * doubled: (state) => state.count() * 2,\n * },\n * actions: {\n * increment() { this.count.update(c => c + 1); },\n * reset() { this.count.set(0); },\n * },\n * });\n *\n * // In any component:\n * const store = useCounterStore();\n * store.count(); // 0\n * store.doubled(); // 0\n * store.increment();\n * store.count(); // 1\n * ```\n */\n\nimport { signal, computed, effect } from './signals.js';\nimport type { Signal, ReadonlySignal } from './signals.js';\n\n// --- Types ---\n\ntype StateFactory<S> = () => S;\n\ntype Getters<S, G> = {\n [K in keyof G]: (state: SignalifiedState<S>) => G[K];\n};\n\ntype Actions<A> = {\n [K in keyof A]: A[K] extends (...args: infer P) => infer R\n ? (...args: P) => R\n : never;\n};\n\n/** Maps plain state values to signals */\ntype SignalifiedState<S> = {\n [K in keyof S]: Signal<S[K]>;\n};\n\n/** The store instance returned to consumers */\nexport type Store<S, G, A> = SignalifiedState<S> & {\n [K in keyof G]: ReadonlySignal<G[K]>;\n} & {\n [K in keyof A]: A[K] extends (...args: infer P) => infer R\n ? (...args: P) => R\n : never;\n} & {\n /** Reset all state to initial values */\n $reset(): void;\n /** Merge partial state into the store, or apply changes via callback */\n $patch(partialOrFn: Partial<S> | ((state: SignalifiedState<S>) => void)): void;\n /** Subscribe to all state changes */\n $subscribe(callback: (state: S) => void): () => void;\n /** Get a plain snapshot of current state */\n $snapshot(): S;\n /** Store ID */\n $id: string;\n};\n\n/** Actions with `this` typed to include state signals, getters, and other actions */\ntype StoreActions<S, G, A> = {\n [K in keyof A]: A[K] extends (...args: infer P) => infer R\n ? (this: SignalifiedState<S> & { [GK in keyof G]: ReadonlySignal<G[GK]> } & A, ...args: P) => R\n : never;\n};\n\nexport interface StoreDefinition<S, G, A> {\n state: StateFactory<S>;\n getters?: Getters<S, G>;\n actions?: StoreActions<S, G, A>;\n plugins?: StorePlugin[];\n}\n\n// --- Plugin system ---\n\nexport interface StorePlugin {\n init?(store: Store<any, any, any>): void;\n onAction?(store: Store<any, any, any>, actionName: string, args: unknown[]): void;\n}\n\nconst globalPlugins: StorePlugin[] = [];\n\n/** Register global plugins that apply to all stores */\nexport function configureStores(options: { plugins: StorePlugin[] }): void {\n globalPlugins.push(...options.plugins);\n}\n\n// --- Store registry (singleton) ---\n\nconst storeInstances = new Map<string, Store<any, any, any>>();\n\n// --- defineStore ---\n\n/**\n * Define a global store. Returns a composable function that\n * always returns the same store instance (singleton).\n */\nexport function defineStore<\n S extends Record<string, unknown>,\n G extends Record<string, unknown> = {},\n A extends Record<string, (...args: any[]) => any> = {},\n>(\n id: string,\n definition: StoreDefinition<S, G, A>,\n): () => Store<S, G, A> {\n return () => {\n // Return existing instance if already created\n if (storeInstances.has(id)) {\n return storeInstances.get(id) as Store<S, G, A>;\n }\n\n const initialState = definition.state();\n const store = createStoreInstance(id, initialState, definition);\n storeInstances.set(id, store);\n return store;\n };\n}\n\nfunction createStoreInstance<\n S extends Record<string, unknown>,\n G extends Record<string, unknown>,\n A extends Record<string, (...args: any[]) => any>,\n>(\n id: string,\n initialState: S,\n definition: StoreDefinition<S, G, A>,\n): Store<S, G, A> {\n // Create signals for each state property\n const stateSignals: Record<string, Signal<unknown>> = {};\n const stateKeys = Object.keys(initialState);\n\n for (const key of stateKeys) {\n stateSignals[key] = signal(initialState[key]);\n }\n\n // Build the store object first so getters can reference other getters via `this`\n const store: any = { $id: id };\n\n // Add state signals\n for (const key of stateKeys) {\n store[key] = stateSignals[key];\n }\n\n // Create computed getters — bound to store so `this.otherGetter()` works\n if (definition.getters) {\n for (const [key, getterFn] of Object.entries(definition.getters)) {\n store[key] = computed(() =>\n (getterFn as Function).call(store, stateSignals),\n );\n }\n }\n\n // Collect all plugins (global + per-store)\n const plugins = [...globalPlugins, ...(definition.plugins ?? [])];\n\n // Bind actions with `this` pointing to the full store (state + getters + actions)\n if (definition.actions) {\n for (const [key, actionFn] of Object.entries(definition.actions)) {\n store[key] = (...args: unknown[]) => {\n for (const plugin of plugins) plugin.onAction?.(store, key, args);\n return (actionFn as Function).apply(store, args);\n };\n }\n }\n\n // $reset\n store.$reset = () => {\n const fresh = definition.state();\n for (const key of stateKeys) {\n stateSignals[key].set(fresh[key as keyof S]);\n }\n };\n\n // $patch — merge partial state or apply via callback\n store.$patch = (partialOrFn: Partial<S> | ((state: any) => void)) => {\n if (typeof partialOrFn === 'function') {\n partialOrFn(stateSignals);\n } else {\n for (const [key, value] of Object.entries(partialOrFn)) {\n if (key in stateSignals) {\n stateSignals[key].set(value);\n }\n }\n }\n };\n\n // $snapshot\n store.$snapshot = (): S => {\n const snapshot: Record<string, unknown> = {};\n for (const key of stateKeys) {\n snapshot[key] = stateSignals[key]();\n }\n return snapshot as S;\n };\n\n // $subscribe — watch all state signals and notify on change\n const subscribers = new Set<(state: S) => void>();\n let subscribeEffect: (() => void) | null = null;\n store.$subscribe = (callback: (state: S) => void): (() => void) => {\n subscribers.add(callback);\n // Start watching if this is the first subscriber\n if (!subscribeEffect) {\n let isInitial = true;\n subscribeEffect = effect(() => {\n // Read all state signals to track them\n for (const key of stateKeys) {\n stateSignals[key]();\n }\n // Skip initial run — only notify on actual changes\n if (isInitial) {\n isInitial = false;\n return;\n }\n const snapshot = store.$snapshot();\n for (const cb of subscribers) {\n cb(snapshot);\n }\n });\n }\n return () => {\n subscribers.delete(callback);\n // Dispose effect when no subscribers remain\n if (subscribers.size === 0 && subscribeEffect) {\n subscribeEffect();\n subscribeEffect = null;\n }\n };\n };\n\n // Initialize plugins\n for (const plugin of plugins) plugin.init?.(store);\n\n return store as Store<S, G, A>;\n}\n\n/**\n * Clear all store instances (useful for testing).\n */\nexport function clearStores(): void {\n storeInstances.clear();\n}\n\n/** @internal — exposes store registry for devtools */\nexport function __getStoreInstances(): Record<string, Store<any, any, any>> {\n return Object.fromEntries(storeInstances);\n}\n"]}
|