@notis_ai/cli 0.2.0-beta.16.1 → 0.2.0-beta.160.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +433 -133
- package/config/notis_app_boundary_rules.json +50 -0
- package/config/notis_app_design_rules.json +135 -0
- package/dist/agent-hooks/notis-agent-hook.mjs +18672 -0
- package/dist/base-skills/notis-apps/SKILL.md +70 -0
- package/dist/base-skills/notis-apps/references/architecture.md +164 -0
- package/dist/base-skills/notis-apps/references/context.md +81 -0
- package/dist/base-skills/notis-apps/references/design.md +165 -0
- package/dist/base-skills/notis-apps/references/reading.md +89 -0
- package/dist/base-skills/notis-apps/references/release.md +99 -0
- package/dist/base-skills/notis-apps/references/sdk.md +62 -0
- package/dist/base-skills/notis-apps/references/troubleshooting.md +23 -0
- package/dist/base-skills/notis-cli/SKILL.md +140 -0
- package/dist/base-skills/notis-cli/references/app-delivery.md +18 -0
- package/dist/base-skills/notis-cli/references/native-databases.md +20 -0
- package/dist/base-skills/notis-cli/references/tool-examples.md +56 -0
- package/dist/base-skills/notis-cli/references/troubleshooting.md +39 -0
- package/dist/base-skills/notis-query/SKILL.md +67 -0
- package/dist/base-skills/notis-query/references/database-discovery.md +59 -0
- package/dist/base-skills/notis-query/references/documents.md +50 -0
- package/dist/base-skills/notis-query/references/query.md +543 -0
- package/dist/skill-sync/index.js +1626 -0
- package/dist/skill-sync/index.js.map +7 -0
- package/dist/skill-sync-worker.mjs +2990 -0
- package/package.json +16 -6
- package/skills/notis-apps/cli.md +313 -0
- package/skills/notis-cli/AGENT_INSTRUCTIONS.md +39 -0
- package/skills/notis-onboarding/BRIEF.md +129 -0
- package/skills/notis-query/cli.md +39 -0
- package/src/agent-hook-entry.js +5 -0
- package/src/cli.js +294 -25
- package/src/command-specs/agents.js +392 -0
- package/src/command-specs/apps.js +1470 -202
- package/src/command-specs/auth.js +114 -137
- package/src/command-specs/diagnostics.js +716 -0
- package/src/command-specs/handover.js +374 -0
- package/src/command-specs/helpers.js +84 -82
- package/src/command-specs/index.js +25 -6
- package/src/command-specs/meta.js +150 -18
- package/src/command-specs/onboarding.js +290 -0
- package/src/command-specs/profile.js +358 -0
- package/src/command-specs/reports.js +86 -0
- package/src/command-specs/skills.js +75 -0
- package/src/command-specs/smoke.js +386 -0
- package/src/command-specs/tools.js +455 -139
- package/src/runtime/agent-browser.js +632 -0
- package/src/runtime/agent-memory-state.js +126 -0
- package/src/runtime/agent-setup.js +383 -0
- package/src/runtime/app-boundary-validator.js +404 -0
- package/src/runtime/app-changelog.js +79 -0
- package/src/runtime/app-platform.js +2633 -210
- package/src/runtime/app-registry-scaffolds.js +367 -0
- package/src/runtime/app-test-server.js +292 -0
- package/src/runtime/assets/store-screenshot-dark.png +0 -0
- package/src/runtime/auth-recovery.js +110 -0
- package/src/runtime/base-skills.d.ts +20 -0
- package/src/runtime/base-skills.js +167 -0
- package/src/runtime/channel.js +133 -0
- package/src/runtime/delegated-context.js +68 -0
- package/src/runtime/errors.js +1 -0
- package/src/runtime/git.js +233 -0
- package/src/runtime/login-listener.js +15 -0
- package/src/runtime/oauth.js +2622 -0
- package/src/runtime/output.js +37 -5
- package/src/runtime/ports.js +31 -0
- package/src/runtime/profiles.js +906 -55
- package/src/runtime/skill-sync/cloud-client.ts +99 -0
- package/src/runtime/skill-sync/index.ts +697 -0
- package/src/runtime/skill-sync/local-scanner.ts +1046 -0
- package/src/runtime/skill-sync/symlink-manager.ts +433 -0
- package/src/runtime/skill-sync/sync-plan.ts +22 -0
- package/src/runtime/skill-sync/types.ts +110 -0
- package/src/runtime/skill-sync/write-cloud-skill.ts +50 -0
- package/src/runtime/skill-sync-service.js +109 -0
- package/src/runtime/store-screenshot.js +143 -0
- package/src/runtime/sync-skills.d.ts +37 -0
- package/src/runtime/sync-skills.js +231 -0
- package/src/runtime/telemetry.js +92 -0
- package/src/runtime/transport.js +324 -45
- package/src/skill-sync-worker-entry.js +2 -0
- package/src/skill-sync-worker.js +50 -0
- package/template/.harness/index.html.tmpl +430 -0
- package/template/CHANGELOG.md +5 -0
- package/template/app/layout.tsx +5 -2
- package/template/app/page.tsx +49 -42
- package/template/components/page-heading.tsx +23 -0
- package/template/components/ui/badge.tsx +7 -4
- package/template/components/ui/card.tsx +24 -11
- package/template/components/ui/native-select.tsx +24 -0
- package/template/notis.config.ts +24 -6
- package/template/package-lock.json +4137 -0
- package/template/package.json +5 -5
- package/template/packages/{notis-sdk → sdk}/package.json +13 -3
- package/template/packages/sdk/src/agentContext.ts +36 -0
- package/template/packages/sdk/src/components/DocumentEditor.tsx +103 -0
- package/template/packages/sdk/src/components/Markdown.tsx +60 -0
- package/template/packages/sdk/src/components/MarkdownEditor.tsx +121 -0
- package/template/packages/sdk/src/components/MultiSelectActionBar.tsx +285 -0
- package/template/packages/sdk/src/components/MultiSelectCheckbox.tsx +97 -0
- package/template/packages/sdk/src/components/MultiSelectDragOverlay.tsx +39 -0
- package/template/packages/sdk/src/components/NotisCommentBoundary.tsx +172 -0
- package/template/packages/sdk/src/components/NotisSelectionBoundary.tsx +59 -0
- package/template/packages/sdk/src/components/ShortcutHints.tsx +56 -0
- package/template/packages/sdk/src/components/Skeleton.tsx +24 -0
- package/template/packages/sdk/src/config.ts +257 -0
- package/template/packages/sdk/src/documents.ts +256 -0
- package/template/packages/sdk/src/hooks/useActiveResource.ts +19 -0
- package/template/packages/sdk/src/hooks/useAgentContext.ts +23 -0
- package/template/packages/sdk/src/hooks/useCloudComputer.ts +64 -0
- package/template/packages/sdk/src/hooks/useCollectionInteractions.ts +836 -0
- package/template/packages/sdk/src/hooks/useDatabaseSchema.ts +49 -0
- package/template/packages/sdk/src/hooks/useDatabaseSubscription.ts +76 -0
- package/template/packages/sdk/src/hooks/useDocument.ts +43 -0
- package/template/packages/sdk/src/hooks/useDocuments.ts +84 -0
- package/template/packages/sdk/src/hooks/useHandover.ts +78 -0
- package/template/packages/sdk/src/hooks/useLongPressSelection.ts +79 -0
- package/template/packages/sdk/src/hooks/useMultiSelect.ts +95 -0
- package/template/packages/{notis-sdk → sdk}/src/hooks/useNotis.ts +10 -4
- package/template/packages/{notis-sdk → sdk}/src/hooks/useNotisNavigation.ts +11 -8
- package/template/packages/sdk/src/hooks/useQuery.ts +71 -0
- package/template/packages/sdk/src/hooks/useTool.ts +65 -0
- package/template/packages/sdk/src/hooks/useToolQuery.ts +12 -0
- package/template/packages/sdk/src/hooks/useTopBarSearch.ts +81 -0
- package/template/packages/sdk/src/hooks/useUpsertDocument.ts +95 -0
- package/template/packages/sdk/src/index.ts +161 -0
- package/template/packages/sdk/src/interactions/actions.ts +59 -0
- package/template/packages/sdk/src/interactions/shortcuts.tsx +694 -0
- package/template/packages/sdk/src/interactions/visibility.ts +13 -0
- package/template/packages/sdk/src/interactions.ts +45 -0
- package/template/packages/sdk/src/provider.tsx +44 -0
- package/template/packages/sdk/src/queryCache.ts +170 -0
- package/template/packages/sdk/src/runtime.ts +451 -0
- package/template/packages/sdk/src/styles.css +213 -0
- package/template/packages/sdk/src/tailwind.ts +56 -0
- package/template/packages/{notis-sdk → sdk}/src/vite.ts +5 -1
- package/template/tailwind.config.ts +1 -0
- package/src/command-specs/db.js +0 -163
- package/src/runtime/app-preview-server.js +0 -312
- package/template/packages/notis-sdk/src/config.ts +0 -48
- package/template/packages/notis-sdk/src/helpers.ts +0 -131
- package/template/packages/notis-sdk/src/hooks/useAppState.ts +0 -50
- package/template/packages/notis-sdk/src/hooks/useCollectionItem.ts +0 -58
- package/template/packages/notis-sdk/src/hooks/useDatabase.ts +0 -87
- package/template/packages/notis-sdk/src/hooks/useDocument.ts +0 -61
- package/template/packages/notis-sdk/src/hooks/useTool.ts +0 -49
- package/template/packages/notis-sdk/src/hooks/useUpsertDocument.ts +0 -57
- package/template/packages/notis-sdk/src/index.ts +0 -47
- package/template/packages/notis-sdk/src/provider.tsx +0 -44
- package/template/packages/notis-sdk/src/runtime.ts +0 -159
- package/template/packages/notis-sdk/src/styles.css +0 -123
- /package/template/packages/{notis-sdk → sdk}/src/hooks/useBackend.ts +0 -0
- /package/template/packages/{notis-sdk → sdk}/src/hooks/useTools.ts +0 -0
- /package/template/packages/{notis-sdk → sdk}/src/ui.ts +0 -0
- /package/template/packages/{notis-sdk → sdk}/tsconfig.json +0 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export {
|
|
2
|
+
ShortcutProvider,
|
|
3
|
+
SHORTCUT_SCOPE_PRIORITY,
|
|
4
|
+
isEditableShortcutEvent,
|
|
5
|
+
shortcutDisplay,
|
|
6
|
+
useShortcuts,
|
|
7
|
+
} from './interactions/shortcuts';
|
|
8
|
+
export type {
|
|
9
|
+
ShortcutDefinition,
|
|
10
|
+
ShortcutHelpEntry,
|
|
11
|
+
ShortcutScope,
|
|
12
|
+
UseShortcutsOptions,
|
|
13
|
+
} from './interactions/shortcuts';
|
|
14
|
+
export { collectionActionDefaults } from './interactions/actions';
|
|
15
|
+
export type {
|
|
16
|
+
CollectionAction,
|
|
17
|
+
CollectionActionContext,
|
|
18
|
+
CollectionActionIntent,
|
|
19
|
+
ResolvedCollectionAction,
|
|
20
|
+
} from './interactions/actions';
|
|
21
|
+
export { useCollectionInteractions, COLLECTION_ITEM_ATTRIBUTE } from './hooks/useCollectionInteractions';
|
|
22
|
+
export type {
|
|
23
|
+
CollectionInteractionController,
|
|
24
|
+
CollectionInteractionReason,
|
|
25
|
+
CollectionKeyboardShortcuts,
|
|
26
|
+
CollectionNavigationContext,
|
|
27
|
+
CollectionNavigationDirection,
|
|
28
|
+
CollectionSelectionChange,
|
|
29
|
+
SelectionMarqueeRect,
|
|
30
|
+
UseCollectionInteractionsOptions,
|
|
31
|
+
} from './hooks/useCollectionInteractions';
|
|
32
|
+
export { MultiSelectActionBar, BULK_ACTION_LAYOUT_EVENT } from './components/MultiSelectActionBar';
|
|
33
|
+
export type { BulkActionLayoutDetail } from './components/MultiSelectActionBar';
|
|
34
|
+
export { MultiSelectCheckbox, MultiSelectCheckbox as SelectionCheckbox } from './components/MultiSelectCheckbox';
|
|
35
|
+
export { MultiSelectDragOverlay, MultiSelectDragOverlay as SelectionMarquee } from './components/MultiSelectDragOverlay';
|
|
36
|
+
export { ShortcutHints } from './components/ShortcutHints';
|
|
37
|
+
export type { MultiSelectAction, MultiSelectActionBarProps } from './components/MultiSelectActionBar';
|
|
38
|
+
export type { MultiSelectCheckboxProps } from './components/MultiSelectCheckbox';
|
|
39
|
+
export type { MultiSelectDragOverlayProps } from './components/MultiSelectDragOverlay';
|
|
40
|
+
export type { MultiSelectCheckboxProps as SelectionCheckboxProps } from './components/MultiSelectCheckbox';
|
|
41
|
+
export type { MultiSelectDragOverlayProps as SelectionMarqueeProps } from './components/MultiSelectDragOverlay';
|
|
42
|
+
export type { ShortcutHint, ShortcutHintsProps } from './components/ShortcutHints';
|
|
43
|
+
|
|
44
|
+
export { useLongPressSelection } from './hooks/useLongPressSelection';
|
|
45
|
+
export { isInteractionElementVisible } from './interactions/visibility';
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React, { createContext, useContext, type Context, type ReactNode } from 'react';
|
|
4
|
+
import type { NotisRuntime } from './runtime';
|
|
5
|
+
import { ShortcutProvider } from './interactions/shortcuts';
|
|
6
|
+
|
|
7
|
+
const NOTIS_CONTEXT_SYMBOL = Symbol.for('notis.sdk.runtime_context');
|
|
8
|
+
|
|
9
|
+
type NotisContextGlobal = typeof globalThis & {
|
|
10
|
+
[NOTIS_CONTEXT_SYMBOL]?: Context<NotisRuntime | null>;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
function getNotisContext(): Context<NotisRuntime | null> {
|
|
14
|
+
const scope = globalThis as NotisContextGlobal;
|
|
15
|
+
if (!scope[NOTIS_CONTEXT_SYMBOL]) {
|
|
16
|
+
scope[NOTIS_CONTEXT_SYMBOL] = createContext<NotisRuntime | null>(null);
|
|
17
|
+
}
|
|
18
|
+
return scope[NOTIS_CONTEXT_SYMBOL]!;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const NotisContext = getNotisContext();
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Provides the Notis runtime context to all child components. The portal owns
|
|
25
|
+
* the runtime and injects it into the rendered app tree.
|
|
26
|
+
*/
|
|
27
|
+
export function NotisProvider({ children, runtime }: { children: ReactNode; runtime: NotisRuntime | null }) {
|
|
28
|
+
if (runtime === undefined) {
|
|
29
|
+
throw new Error('NotisProvider requires an explicit runtime prop.');
|
|
30
|
+
}
|
|
31
|
+
return (
|
|
32
|
+
<NotisContext.Provider value={runtime}>
|
|
33
|
+
<ShortcutProvider>{children}</ShortcutProvider>
|
|
34
|
+
</NotisContext.Provider>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Returns the raw NotisRuntime or null if not yet available.
|
|
40
|
+
* Prefer the typed hooks (useTool, useNotis, etc.) over this.
|
|
41
|
+
*/
|
|
42
|
+
export function useNotisRuntime(): NotisRuntime | null {
|
|
43
|
+
return useContext(NotisContext);
|
|
44
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/** In-memory read snapshots. The host owns the lifetime and authorization scope. */
|
|
2
|
+
export interface QuerySnapshot<T = unknown> {
|
|
3
|
+
data: T | undefined;
|
|
4
|
+
hasData: boolean;
|
|
5
|
+
isFetching: boolean;
|
|
6
|
+
error: Error | null;
|
|
7
|
+
updatedAt: number;
|
|
8
|
+
invalidation: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface QueryFetchOptions {
|
|
12
|
+
staleTimeMs?: number;
|
|
13
|
+
force?: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface NotisQueryClient {
|
|
17
|
+
getSnapshot<T>(key: string): QuerySnapshot<T>;
|
|
18
|
+
subscribe(key: string, listener: () => void): () => void;
|
|
19
|
+
fetch<T>(key: string, read: () => Promise<T>, options?: QueryFetchOptions): Promise<T>;
|
|
20
|
+
prefetch<T>(key: string, read: () => Promise<T>, options?: QueryFetchOptions): Promise<T>;
|
|
21
|
+
invalidate(key?: string): void;
|
|
22
|
+
clear(): void;
|
|
23
|
+
/** Host-only: permanently retire a revoked security scope. */
|
|
24
|
+
dispose?(): void;
|
|
25
|
+
getRevision?(): number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const EMPTY: QuerySnapshot = Object.freeze({
|
|
29
|
+
data: undefined, hasData: false, isFetching: false, error: null, updatedAt: 0, invalidation: 0,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
/** Stable keys distinguish filters, pagination, and resource identities. */
|
|
33
|
+
export function queryKey(value: unknown): string {
|
|
34
|
+
if (value instanceof Date) return JSON.stringify(value.toISOString());
|
|
35
|
+
if (Array.isArray(value)) return `[${value.map(queryKey).join(',')}]`;
|
|
36
|
+
if (value && typeof value === 'object') {
|
|
37
|
+
const record = value as Record<string, unknown>;
|
|
38
|
+
return `{${Object.keys(record).filter((key) => record[key] !== undefined).sort()
|
|
39
|
+
.map((key) => `${JSON.stringify(key)}:${queryKey(record[key])}`).join(',')}}`;
|
|
40
|
+
}
|
|
41
|
+
return JSON.stringify(value) ?? 'null';
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** One shared queue can bound descriptor and data speculation together. */
|
|
45
|
+
export function createPrefetchQueue(concurrency = 2) {
|
|
46
|
+
let active = 0;
|
|
47
|
+
const waiting: Array<() => void> = [];
|
|
48
|
+
function drain() {
|
|
49
|
+
while (active < concurrency && waiting.length) waiting.shift()!();
|
|
50
|
+
}
|
|
51
|
+
return <T>(read: () => Promise<T>): Promise<T> => new Promise<T>((resolve, reject) => {
|
|
52
|
+
waiting.push(() => {
|
|
53
|
+
active += 1;
|
|
54
|
+
Promise.resolve().then(read).then(resolve, reject).finally(() => { active -= 1; drain(); });
|
|
55
|
+
});
|
|
56
|
+
drain();
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
type Entry = {
|
|
61
|
+
snapshot: QuerySnapshot;
|
|
62
|
+
listeners: Set<() => void>;
|
|
63
|
+
generation: number;
|
|
64
|
+
pending?: Promise<unknown>;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export function createQueryClient(options: {
|
|
68
|
+
maxEntries?: number;
|
|
69
|
+
now?: () => number;
|
|
70
|
+
schedule?: ReturnType<typeof createPrefetchQueue>;
|
|
71
|
+
/** Bounds idempotent reads only; never retries the underlying callback. */
|
|
72
|
+
readTimeoutMs?: number;
|
|
73
|
+
} = {}): NotisQueryClient {
|
|
74
|
+
const entries = new Map<string, Entry>();
|
|
75
|
+
const now = options.now ?? Date.now;
|
|
76
|
+
const maxEntries = options.maxEntries ?? 100;
|
|
77
|
+
const schedule = options.schedule ?? createPrefetchQueue(2);
|
|
78
|
+
let epoch = 0;
|
|
79
|
+
let disposed = false;
|
|
80
|
+
let revision = 0;
|
|
81
|
+
|
|
82
|
+
function prune(keep?: string) {
|
|
83
|
+
for (const [oldKey, old] of entries) {
|
|
84
|
+
if (entries.size <= maxEntries) break;
|
|
85
|
+
if (oldKey !== keep && !old.listeners.size && !old.pending) entries.delete(oldKey);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function entry(key: string): Entry {
|
|
89
|
+
let value = entries.get(key);
|
|
90
|
+
if (!value) {
|
|
91
|
+
value = { snapshot: EMPTY, listeners: new Set(), generation: 0 };
|
|
92
|
+
entries.set(key, value);
|
|
93
|
+
}
|
|
94
|
+
// Only operations, not getSnapshot during render, update the LRU.
|
|
95
|
+
entries.delete(key);
|
|
96
|
+
entries.set(key, value);
|
|
97
|
+
prune(key);
|
|
98
|
+
return value;
|
|
99
|
+
}
|
|
100
|
+
function publish(value: Entry, snapshot: QuerySnapshot) {
|
|
101
|
+
value.snapshot = snapshot;
|
|
102
|
+
for (const listener of value.listeners) listener();
|
|
103
|
+
}
|
|
104
|
+
const client: NotisQueryClient = {
|
|
105
|
+
getSnapshot<T>(key: string) { return (entries.get(key)?.snapshot ?? EMPTY) as QuerySnapshot<T>; },
|
|
106
|
+
subscribe(key, listener) {
|
|
107
|
+
const value = entry(key);
|
|
108
|
+
value.listeners.add(listener);
|
|
109
|
+
return () => { value.listeners.delete(listener); prune(); };
|
|
110
|
+
},
|
|
111
|
+
fetch<T>(key: string, read: () => Promise<T>, fetchOptions: QueryFetchOptions = {}) {
|
|
112
|
+
if (disposed) return Promise.reject(new Error('App query scope is no longer available.'));
|
|
113
|
+
const value = entry(key);
|
|
114
|
+
if (value.pending) return value.pending as Promise<T>;
|
|
115
|
+
if (!fetchOptions.force && value.snapshot.hasData && value.snapshot.updatedAt > 0
|
|
116
|
+
&& now() - value.snapshot.updatedAt < (fetchOptions.staleTimeMs ?? 30_000)) {
|
|
117
|
+
return Promise.resolve(value.snapshot.data as T);
|
|
118
|
+
}
|
|
119
|
+
const generation = ++value.generation;
|
|
120
|
+
const requestEpoch = epoch;
|
|
121
|
+
const canCommit = () => epoch === requestEpoch && value.generation === generation && entries.get(key) === value;
|
|
122
|
+
let timeout: ReturnType<typeof setTimeout>;
|
|
123
|
+
const deadline = new Promise<never>((_, reject) => {
|
|
124
|
+
timeout = setTimeout(() => reject(new Error('This read took too long. Please retry.')), options.readTimeoutMs ?? 30_000);
|
|
125
|
+
// Node-based verification must not stay alive for a retired browser read.
|
|
126
|
+
if (typeof timeout === 'object' && 'unref' in timeout) timeout.unref();
|
|
127
|
+
});
|
|
128
|
+
const request = Promise.race([Promise.resolve().then(read), deadline]).then((data) => {
|
|
129
|
+
if (canCommit()) publish(value, { ...value.snapshot, data, hasData: true, isFetching: false, error: null, updatedAt: now() });
|
|
130
|
+
return data;
|
|
131
|
+
}, (reason) => {
|
|
132
|
+
const error = reason instanceof Error ? reason : new Error(String(reason));
|
|
133
|
+
if (canCommit()) publish(value, { ...value.snapshot, isFetching: false, error });
|
|
134
|
+
throw error;
|
|
135
|
+
}).finally(() => { clearTimeout(timeout); if (value.pending === request) value.pending = undefined; prune(); });
|
|
136
|
+
value.pending = request;
|
|
137
|
+
publish(value, { ...value.snapshot, isFetching: true, error: null });
|
|
138
|
+
return request;
|
|
139
|
+
},
|
|
140
|
+
prefetch<T>(key: string, read: () => Promise<T>, fetchOptions?: QueryFetchOptions) {
|
|
141
|
+
const requestEpoch = epoch;
|
|
142
|
+
return schedule(() => {
|
|
143
|
+
if (epoch !== requestEpoch) throw new Error('App query scope was cleared.');
|
|
144
|
+
return client.fetch(key, read, fetchOptions);
|
|
145
|
+
});
|
|
146
|
+
},
|
|
147
|
+
getRevision: () => revision,
|
|
148
|
+
invalidate(key) {
|
|
149
|
+
revision += 1;
|
|
150
|
+
for (const [entryKey, value] of entries) {
|
|
151
|
+
if (key !== undefined && entryKey !== key) continue;
|
|
152
|
+
value.generation += 1;
|
|
153
|
+
value.pending = undefined;
|
|
154
|
+
publish(value, { ...value.snapshot, updatedAt: 0, isFetching: false, invalidation: value.snapshot.invalidation + 1 });
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
dispose() { disposed = true; client.clear(); },
|
|
158
|
+
clear() {
|
|
159
|
+
revision += 1;
|
|
160
|
+
epoch += 1;
|
|
161
|
+
for (const value of entries.values()) {
|
|
162
|
+
value.generation += 1;
|
|
163
|
+
value.pending = undefined;
|
|
164
|
+
publish(value, { ...EMPTY, invalidation: value.snapshot.invalidation + 1 });
|
|
165
|
+
}
|
|
166
|
+
for (const [key, value] of entries) if (!value.listeners.size) entries.delete(key);
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
return client;
|
|
170
|
+
}
|