@ai-matrx/summon 0.0.0 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/LICENSE +21 -0
- package/README.md +379 -1
- package/dist/closeBinding-C6raC3qJ.d.cts +20 -0
- package/dist/closeBinding-C6raC3qJ.d.ts +20 -0
- package/dist/dialog.cjs +60 -0
- package/dist/dialog.cjs.map +1 -0
- package/dist/dialog.d.cts +16 -0
- package/dist/dialog.d.ts +16 -0
- package/dist/dialog.js +38 -0
- package/dist/dialog.js.map +1 -0
- package/dist/errors-eJrKXq33.d.cts +30 -0
- package/dist/errors-eJrKXq33.d.ts +30 -0
- package/dist/handles.cjs +411 -0
- package/dist/handles.cjs.map +1 -0
- package/dist/handles.d.cts +166 -0
- package/dist/handles.d.ts +166 -0
- package/dist/handles.js +388 -0
- package/dist/handles.js.map +1 -0
- package/dist/index.cjs +2383 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1259 -0
- package/dist/index.d.ts +1259 -0
- package/dist/index.js +2363 -0
- package/dist/index.js.map +1 -0
- package/dist/lite.cjs +460 -0
- package/dist/lite.cjs.map +1 -0
- package/dist/lite.d.cts +56 -0
- package/dist/lite.d.ts +56 -0
- package/dist/lite.js +444 -0
- package/dist/lite.js.map +1 -0
- package/dist/next.cjs +61 -0
- package/dist/next.cjs.map +1 -0
- package/dist/next.d.cts +5 -0
- package/dist/next.d.ts +5 -0
- package/dist/next.js +39 -0
- package/dist/next.js.map +1 -0
- package/dist/persistence-data.cjs +143 -0
- package/dist/persistence-data.cjs.map +1 -0
- package/dist/persistence-data.d.cts +72 -0
- package/dist/persistence-data.d.ts +72 -0
- package/dist/persistence-data.js +120 -0
- package/dist/persistence-data.js.map +1 -0
- package/dist/persistence.cjs +1073 -0
- package/dist/persistence.cjs.map +1 -0
- package/dist/persistence.d.cts +236 -0
- package/dist/persistence.d.ts +236 -0
- package/dist/persistence.js +1077 -0
- package/dist/persistence.js.map +1 -0
- package/dist/react.cjs +733 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.cts +261 -0
- package/dist/react.d.ts +261 -0
- package/dist/react.js +718 -0
- package/dist/react.js.map +1 -0
- package/dist/sheet.cjs +71 -0
- package/dist/sheet.cjs.map +1 -0
- package/dist/sheet.d.cts +17 -0
- package/dist/sheet.d.ts +17 -0
- package/dist/sheet.js +49 -0
- package/dist/sheet.js.map +1 -0
- package/dist/styles.css +78 -0
- package/dist/testing.cjs +132 -0
- package/dist/testing.cjs.map +1 -0
- package/dist/testing.d.cts +44 -0
- package/dist/testing.d.ts +44 -0
- package/dist/testing.js +115 -0
- package/dist/testing.js.map +1 -0
- package/dist/url.cjs +362 -0
- package/dist/url.cjs.map +1 -0
- package/dist/url.d.cts +153 -0
- package/dist/url.d.ts +153 -0
- package/dist/url.js +352 -0
- package/dist/url.js.map +1 -0
- package/dist/window-host.cjs +123 -0
- package/dist/window-host.cjs.map +1 -0
- package/dist/window-host.d.cts +72 -0
- package/dist/window-host.d.ts +72 -0
- package/dist/window-host.js +104 -0
- package/dist/window-host.js.map +1 -0
- package/dist/window.cjs +3978 -0
- package/dist/window.cjs.map +1 -0
- package/dist/window.d.cts +796 -0
- package/dist/window.d.ts +796 -0
- package/dist/window.js +4003 -0
- package/dist/window.js.map +1 -0
- package/package.json +239 -1
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import * as _reduxjs_toolkit from '@reduxjs/toolkit';
|
|
2
|
+
import * as _ai_matrx_summon from '@ai-matrx/summon';
|
|
3
|
+
import { IdentityPort, RouterPort, OverlayPersistencePort, StoredValue, NotifyPort, ErrorSinkPort, SummonPorts } from '@ai-matrx/summon';
|
|
4
|
+
|
|
5
|
+
interface ControllableIdentity extends IdentityPort {
|
|
6
|
+
set(key: string, ready?: boolean): void;
|
|
7
|
+
}
|
|
8
|
+
declare function createTestIdentity(key?: string, ready?: boolean): ControllableIdentity;
|
|
9
|
+
interface ControllableRouter extends RouterPort {
|
|
10
|
+
/** Simulate a navigation: new pathname (and optional search), listeners notified. */
|
|
11
|
+
navigate(pathname: string, search?: string): void;
|
|
12
|
+
}
|
|
13
|
+
declare function createTestRouter(pathname?: string, search?: string): ControllableRouter;
|
|
14
|
+
declare class TestVersionConflictError extends Error {
|
|
15
|
+
readonly key: string;
|
|
16
|
+
readonly expected: number;
|
|
17
|
+
readonly actual: number;
|
|
18
|
+
constructor(key: string, expected: number, actual: number);
|
|
19
|
+
}
|
|
20
|
+
/** An in-memory, versioned persistence port (per person, like the database adapter). */
|
|
21
|
+
declare function createMemoryPersistence(options?: {
|
|
22
|
+
readonly perPerson?: boolean;
|
|
23
|
+
}): OverlayPersistencePort & {
|
|
24
|
+
readonly rows: Map<string, StoredValue>;
|
|
25
|
+
};
|
|
26
|
+
interface RecordedMessage {
|
|
27
|
+
readonly level: "info" | "warn" | "error";
|
|
28
|
+
readonly message: string;
|
|
29
|
+
}
|
|
30
|
+
/** Notify + error ports that record instead of printing. */
|
|
31
|
+
declare function createRecordingPorts(): {
|
|
32
|
+
notify: NotifyPort;
|
|
33
|
+
errors: ErrorSinkPort;
|
|
34
|
+
messages: RecordedMessage[];
|
|
35
|
+
captured: unknown[];
|
|
36
|
+
};
|
|
37
|
+
/** Test ports: quiet recording notify/errors, controllable identity and router, persistence off. */
|
|
38
|
+
declare function createTestPorts(overrides?: Partial<SummonPorts>): SummonPorts & ReturnType<typeof createRecordingPorts>;
|
|
39
|
+
/** A real overlays store wired to test ports. */
|
|
40
|
+
declare function createTestSummonStore(ports?: SummonPorts): _reduxjs_toolkit.EnhancedStore<_ai_matrx_summon.SummonRootState, _reduxjs_toolkit.UnknownAction, _reduxjs_toolkit.Tuple<[_reduxjs_toolkit.StoreEnhancer<{
|
|
41
|
+
dispatch: _reduxjs_toolkit.ThunkDispatch<_ai_matrx_summon.SummonRootState, undefined, _reduxjs_toolkit.UnknownAction>;
|
|
42
|
+
}>, _reduxjs_toolkit.StoreEnhancer]>>;
|
|
43
|
+
|
|
44
|
+
export { type ControllableIdentity, type ControllableRouter, type RecordedMessage, TestVersionConflictError, createMemoryPersistence, createRecordingPorts, createTestIdentity, createTestPorts, createTestRouter, createTestSummonStore };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import * as _reduxjs_toolkit from '@reduxjs/toolkit';
|
|
2
|
+
import * as _ai_matrx_summon from '@ai-matrx/summon';
|
|
3
|
+
import { IdentityPort, RouterPort, OverlayPersistencePort, StoredValue, NotifyPort, ErrorSinkPort, SummonPorts } from '@ai-matrx/summon';
|
|
4
|
+
|
|
5
|
+
interface ControllableIdentity extends IdentityPort {
|
|
6
|
+
set(key: string, ready?: boolean): void;
|
|
7
|
+
}
|
|
8
|
+
declare function createTestIdentity(key?: string, ready?: boolean): ControllableIdentity;
|
|
9
|
+
interface ControllableRouter extends RouterPort {
|
|
10
|
+
/** Simulate a navigation: new pathname (and optional search), listeners notified. */
|
|
11
|
+
navigate(pathname: string, search?: string): void;
|
|
12
|
+
}
|
|
13
|
+
declare function createTestRouter(pathname?: string, search?: string): ControllableRouter;
|
|
14
|
+
declare class TestVersionConflictError extends Error {
|
|
15
|
+
readonly key: string;
|
|
16
|
+
readonly expected: number;
|
|
17
|
+
readonly actual: number;
|
|
18
|
+
constructor(key: string, expected: number, actual: number);
|
|
19
|
+
}
|
|
20
|
+
/** An in-memory, versioned persistence port (per person, like the database adapter). */
|
|
21
|
+
declare function createMemoryPersistence(options?: {
|
|
22
|
+
readonly perPerson?: boolean;
|
|
23
|
+
}): OverlayPersistencePort & {
|
|
24
|
+
readonly rows: Map<string, StoredValue>;
|
|
25
|
+
};
|
|
26
|
+
interface RecordedMessage {
|
|
27
|
+
readonly level: "info" | "warn" | "error";
|
|
28
|
+
readonly message: string;
|
|
29
|
+
}
|
|
30
|
+
/** Notify + error ports that record instead of printing. */
|
|
31
|
+
declare function createRecordingPorts(): {
|
|
32
|
+
notify: NotifyPort;
|
|
33
|
+
errors: ErrorSinkPort;
|
|
34
|
+
messages: RecordedMessage[];
|
|
35
|
+
captured: unknown[];
|
|
36
|
+
};
|
|
37
|
+
/** Test ports: quiet recording notify/errors, controllable identity and router, persistence off. */
|
|
38
|
+
declare function createTestPorts(overrides?: Partial<SummonPorts>): SummonPorts & ReturnType<typeof createRecordingPorts>;
|
|
39
|
+
/** A real overlays store wired to test ports. */
|
|
40
|
+
declare function createTestSummonStore(ports?: SummonPorts): _reduxjs_toolkit.EnhancedStore<_ai_matrx_summon.SummonRootState, _reduxjs_toolkit.UnknownAction, _reduxjs_toolkit.Tuple<[_reduxjs_toolkit.StoreEnhancer<{
|
|
41
|
+
dispatch: _reduxjs_toolkit.ThunkDispatch<_ai_matrx_summon.SummonRootState, undefined, _reduxjs_toolkit.UnknownAction>;
|
|
42
|
+
}>, _reduxjs_toolkit.StoreEnhancer]>>;
|
|
43
|
+
|
|
44
|
+
export { type ControllableIdentity, type ControllableRouter, type RecordedMessage, TestVersionConflictError, createMemoryPersistence, createRecordingPorts, createTestIdentity, createTestPorts, createTestRouter, createTestSummonStore };
|
package/dist/testing.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/testing.ts
|
|
4
|
+
import {
|
|
5
|
+
createSummonStore,
|
|
6
|
+
DEFAULT_PORTS
|
|
7
|
+
} from "@ai-matrx/summon";
|
|
8
|
+
function createTestIdentity(key = "test-person", ready = true) {
|
|
9
|
+
let current = { key, ready };
|
|
10
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
11
|
+
return {
|
|
12
|
+
current: () => current,
|
|
13
|
+
subscribe: (listener) => {
|
|
14
|
+
listeners.add(listener);
|
|
15
|
+
return () => listeners.delete(listener);
|
|
16
|
+
},
|
|
17
|
+
set(next, nextReady = true) {
|
|
18
|
+
current = { key: next, ready: nextReady };
|
|
19
|
+
for (const listener of listeners) listener();
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function createTestRouter(pathname = "/", search = "") {
|
|
24
|
+
let path = pathname;
|
|
25
|
+
let query = search;
|
|
26
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
27
|
+
const notify = () => {
|
|
28
|
+
for (const listener of listeners) listener();
|
|
29
|
+
};
|
|
30
|
+
return {
|
|
31
|
+
pathname: () => path,
|
|
32
|
+
search: () => query,
|
|
33
|
+
replaceSearch: (next) => {
|
|
34
|
+
query = next && !next.startsWith("?") ? `?${next}` : next;
|
|
35
|
+
},
|
|
36
|
+
subscribe: (listener) => {
|
|
37
|
+
listeners.add(listener);
|
|
38
|
+
return () => listeners.delete(listener);
|
|
39
|
+
},
|
|
40
|
+
navigate(nextPath, nextSearch = "") {
|
|
41
|
+
path = nextPath;
|
|
42
|
+
query = nextSearch;
|
|
43
|
+
notify();
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
var TestVersionConflictError = class extends Error {
|
|
48
|
+
constructor(key, expected, actual) {
|
|
49
|
+
super(`version conflict on "${key}": expected ${expected}, found ${actual}`);
|
|
50
|
+
this.key = key;
|
|
51
|
+
this.expected = expected;
|
|
52
|
+
this.actual = actual;
|
|
53
|
+
this.name = "TestVersionConflictError";
|
|
54
|
+
}
|
|
55
|
+
key;
|
|
56
|
+
expected;
|
|
57
|
+
actual;
|
|
58
|
+
};
|
|
59
|
+
function createMemoryPersistence(options = {}) {
|
|
60
|
+
const rows = /* @__PURE__ */ new Map();
|
|
61
|
+
return {
|
|
62
|
+
perPerson: options.perPerson ?? true,
|
|
63
|
+
rows,
|
|
64
|
+
load: async (key) => rows.get(key) ?? null,
|
|
65
|
+
save: async (key, value, saveOptions) => {
|
|
66
|
+
const current = rows.get(key)?.version ?? 0;
|
|
67
|
+
if (saveOptions?.expectedVersion !== void 0 && saveOptions.expectedVersion !== current) {
|
|
68
|
+
throw new TestVersionConflictError(key, saveOptions.expectedVersion, current);
|
|
69
|
+
}
|
|
70
|
+
rows.set(key, { value: structuredClone(value), version: current + 1 });
|
|
71
|
+
return { version: current + 1 };
|
|
72
|
+
},
|
|
73
|
+
remove: async (key) => {
|
|
74
|
+
rows.delete(key);
|
|
75
|
+
},
|
|
76
|
+
list: async (prefix) => [...rows.keys()].filter((key) => key.startsWith(prefix))
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function createRecordingPorts() {
|
|
80
|
+
const messages = [];
|
|
81
|
+
const captured = [];
|
|
82
|
+
return {
|
|
83
|
+
messages,
|
|
84
|
+
captured,
|
|
85
|
+
notify: {
|
|
86
|
+
info: (message) => void messages.push({ level: "info", message }),
|
|
87
|
+
warn: (message) => void messages.push({ level: "warn", message }),
|
|
88
|
+
error: (message) => void messages.push({ level: "error", message })
|
|
89
|
+
},
|
|
90
|
+
errors: { capture: (error) => void captured.push(error) }
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
function createTestPorts(overrides = {}) {
|
|
94
|
+
const recording = createRecordingPorts();
|
|
95
|
+
return {
|
|
96
|
+
...DEFAULT_PORTS,
|
|
97
|
+
identity: createTestIdentity(),
|
|
98
|
+
router: createTestRouter(),
|
|
99
|
+
...recording,
|
|
100
|
+
...overrides
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function createTestSummonStore(ports = createTestPorts()) {
|
|
104
|
+
return createSummonStore({ ports });
|
|
105
|
+
}
|
|
106
|
+
export {
|
|
107
|
+
TestVersionConflictError,
|
|
108
|
+
createMemoryPersistence,
|
|
109
|
+
createRecordingPorts,
|
|
110
|
+
createTestIdentity,
|
|
111
|
+
createTestPorts,
|
|
112
|
+
createTestRouter,
|
|
113
|
+
createTestSummonStore
|
|
114
|
+
};
|
|
115
|
+
//# sourceMappingURL=testing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/testing.ts"],"sourcesContent":["// @ai-matrx/summon/testing — fakes for hosts testing code that opens overlays: controllable\n// identity and router ports, an in-memory versioned persistence port, a quiet notify/error pair,\n// and a test store with all of them wired. No test-runner dependency.\nimport {\n createSummonStore,\n DEFAULT_PORTS,\n type IdentityPort,\n type NotifyPort,\n type ErrorSinkPort,\n type OverlayPersistencePort,\n type SummonPorts,\n type RouterPort,\n type StoredValue,\n} from \"@ai-matrx/summon\";\n\nexport interface ControllableIdentity extends IdentityPort {\n set(key: string, ready?: boolean): void;\n}\n\nexport function createTestIdentity(key = \"test-person\", ready = true): ControllableIdentity {\n let current = { key, ready };\n const listeners = new Set<() => void>();\n return {\n current: () => current,\n subscribe: (listener) => {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n set(next, nextReady = true) {\n current = { key: next, ready: nextReady };\n for (const listener of listeners) listener();\n },\n };\n}\n\nexport interface ControllableRouter extends RouterPort {\n /** Simulate a navigation: new pathname (and optional search), listeners notified. */\n navigate(pathname: string, search?: string): void;\n}\n\nexport function createTestRouter(pathname = \"/\", search = \"\"): ControllableRouter {\n let path = pathname;\n let query = search;\n const listeners = new Set<() => void>();\n const notify = () => {\n for (const listener of listeners) listener();\n };\n return {\n pathname: () => path,\n search: () => query,\n replaceSearch: (next) => {\n query = next && !next.startsWith(\"?\") ? `?${next}` : next;\n },\n subscribe: (listener) => {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n navigate(nextPath, nextSearch = \"\") {\n path = nextPath;\n query = nextSearch;\n notify();\n },\n };\n}\n\nexport class TestVersionConflictError extends Error {\n constructor(readonly key: string, readonly expected: number, readonly actual: number) {\n super(`version conflict on \"${key}\": expected ${expected}, found ${actual}`);\n this.name = \"TestVersionConflictError\";\n }\n}\n\n/** An in-memory, versioned persistence port (per person, like the database adapter). */\nexport function createMemoryPersistence(options: { readonly perPerson?: boolean } = {}): OverlayPersistencePort & {\n readonly rows: Map<string, StoredValue>;\n} {\n const rows = new Map<string, StoredValue>();\n return {\n perPerson: options.perPerson ?? true,\n rows,\n load: async <T>(key: string) => (rows.get(key) as StoredValue<T> | undefined) ?? null,\n save: async (key, value, saveOptions) => {\n const current = rows.get(key)?.version ?? 0;\n if (saveOptions?.expectedVersion !== undefined && saveOptions.expectedVersion !== current) {\n throw new TestVersionConflictError(key, saveOptions.expectedVersion, current);\n }\n rows.set(key, { value: structuredClone(value), version: current + 1 });\n return { version: current + 1 };\n },\n remove: async (key) => {\n rows.delete(key);\n },\n list: async (prefix) => [...rows.keys()].filter((key) => key.startsWith(prefix)),\n };\n}\n\nexport interface RecordedMessage {\n readonly level: \"info\" | \"warn\" | \"error\";\n readonly message: string;\n}\n\n/** Notify + error ports that record instead of printing. */\nexport function createRecordingPorts(): { notify: NotifyPort; errors: ErrorSinkPort; messages: RecordedMessage[]; captured: unknown[] } {\n const messages: RecordedMessage[] = [];\n const captured: unknown[] = [];\n return {\n messages,\n captured,\n notify: {\n info: (message) => void messages.push({ level: \"info\", message }),\n warn: (message) => void messages.push({ level: \"warn\", message }),\n error: (message) => void messages.push({ level: \"error\", message }),\n },\n errors: { capture: (error) => void captured.push(error) },\n };\n}\n\n/** Test ports: quiet recording notify/errors, controllable identity and router, persistence off. */\nexport function createTestPorts(overrides: Partial<SummonPorts> = {}): SummonPorts & ReturnType<typeof createRecordingPorts> {\n const recording = createRecordingPorts();\n return {\n ...DEFAULT_PORTS,\n identity: createTestIdentity(),\n router: createTestRouter(),\n ...recording,\n ...overrides,\n };\n}\n\n/** A real overlays store wired to test ports. */\nexport function createTestSummonStore(ports: SummonPorts = createTestPorts()) {\n return createSummonStore({ ports });\n}\n"],"mappings":"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","names":[]}
|
package/dist/url.cjs
ADDED
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use strict";
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
|
|
21
|
+
// src/url.ts
|
|
22
|
+
var url_exports = {};
|
|
23
|
+
__export(url_exports, {
|
|
24
|
+
OverlayLinkManager: () => OverlayLinkManager,
|
|
25
|
+
canonicalLinkKey: () => canonicalLinkKey,
|
|
26
|
+
canonicalizeToken: () => canonicalizeToken,
|
|
27
|
+
decodeLinkPart: () => decodeLinkPart,
|
|
28
|
+
encodeLinkArg: () => encodeLinkArg,
|
|
29
|
+
encodeLinkId: () => encodeLinkId,
|
|
30
|
+
formatLinkToken: () => formatLinkToken,
|
|
31
|
+
hydrateLinkToken: () => hydrateLinkToken,
|
|
32
|
+
mergeManagedLinkParam: () => mergeManagedLinkParam,
|
|
33
|
+
openFromLink: () => openFromLink,
|
|
34
|
+
openPanelRequest: () => openPanelRequest,
|
|
35
|
+
openRegisteredOverlay: () => openRegisteredOverlay,
|
|
36
|
+
parseLinkParam: () => parseLinkParam,
|
|
37
|
+
parseLinkToken: () => parseLinkToken,
|
|
38
|
+
readPanelsParam: () => readPanelsParam,
|
|
39
|
+
serializeLinkEntries: () => serializeLinkEntries,
|
|
40
|
+
unopenedWindowNotice: () => unopenedWindowNotice,
|
|
41
|
+
useOverlayLink: () => useOverlayLink,
|
|
42
|
+
withPanelsParam: () => withPanelsParam,
|
|
43
|
+
withUnresolvedTokens: () => withUnresolvedTokens
|
|
44
|
+
});
|
|
45
|
+
module.exports = __toCommonJS(url_exports);
|
|
46
|
+
|
|
47
|
+
// src/url/useOverlayLink.ts
|
|
48
|
+
var import_react = require("react");
|
|
49
|
+
var import_summon = require("@ai-matrx/summon");
|
|
50
|
+
var import_react2 = require("@ai-matrx/summon/react");
|
|
51
|
+
function useOverlayLink(linkKey, id, args) {
|
|
52
|
+
const dispatch = (0, import_react2.useSummonDispatch)();
|
|
53
|
+
const argsKey = args ? JSON.stringify(
|
|
54
|
+
Object.keys(args).sort().map((k) => [k, args[k]])
|
|
55
|
+
) : "";
|
|
56
|
+
const stableArgs = (0, import_react.useMemo)(() => args ? { ...args } : void 0, [argsKey]);
|
|
57
|
+
(0, import_react.useEffect)(() => {
|
|
58
|
+
if (!linkKey || !id) return void 0;
|
|
59
|
+
dispatch((0, import_summon.registerSyncEntry)(stableArgs ? { typeKey: linkKey, instanceId: id, args: stableArgs } : { typeKey: linkKey, instanceId: id }));
|
|
60
|
+
return () => {
|
|
61
|
+
dispatch((0, import_summon.unregisterSyncEntry)({ typeKey: linkKey, instanceId: id }));
|
|
62
|
+
};
|
|
63
|
+
}, [dispatch, linkKey, id, stableArgs]);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// src/url/grammar.ts
|
|
67
|
+
var hex = (ch) => `%${ch.charCodeAt(0).toString(16).toUpperCase().padStart(2, "0")}`;
|
|
68
|
+
function encodeLinkId(id) {
|
|
69
|
+
return id.replace(/[%,:]/g, hex);
|
|
70
|
+
}
|
|
71
|
+
function encodeLinkArg(value) {
|
|
72
|
+
return value.replace(/[%,:_-]/g, hex);
|
|
73
|
+
}
|
|
74
|
+
function decodeLinkPart(value) {
|
|
75
|
+
if (!value.includes("%")) return value;
|
|
76
|
+
return value.replace(/(?:%[0-9A-Fa-f]{2})+/g, (run) => {
|
|
77
|
+
try {
|
|
78
|
+
return decodeURIComponent(run);
|
|
79
|
+
} catch {
|
|
80
|
+
return run;
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
function parseLinkToken(raw) {
|
|
85
|
+
if (!raw) return null;
|
|
86
|
+
const first = raw.indexOf(":");
|
|
87
|
+
const key = first === -1 ? raw : raw.slice(0, first);
|
|
88
|
+
if (!key) return null;
|
|
89
|
+
if (first === -1) return { raw, key, id: void 0, args: void 0 };
|
|
90
|
+
const rest = raw.slice(first + 1);
|
|
91
|
+
const second = rest.indexOf(":");
|
|
92
|
+
const idPart = second === -1 ? rest : rest.slice(0, second);
|
|
93
|
+
const argsPart = second === -1 ? void 0 : rest.slice(second + 1);
|
|
94
|
+
let args;
|
|
95
|
+
if (argsPart) {
|
|
96
|
+
args = {};
|
|
97
|
+
for (const pair of argsPart.split("_")) {
|
|
98
|
+
const at = pair.indexOf("-");
|
|
99
|
+
if (at <= 0 || at === pair.length - 1) continue;
|
|
100
|
+
args[decodeLinkPart(pair.slice(0, at))] = decodeLinkPart(pair.slice(at + 1));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return { raw, key: decodeLinkPart(key), id: decodeLinkPart(idPart), args };
|
|
104
|
+
}
|
|
105
|
+
function parseLinkParam(param) {
|
|
106
|
+
if (!param) return [];
|
|
107
|
+
const out = [];
|
|
108
|
+
for (const raw of param.split(",")) {
|
|
109
|
+
const token = parseLinkToken(raw);
|
|
110
|
+
if (token) out.push(token);
|
|
111
|
+
}
|
|
112
|
+
return out;
|
|
113
|
+
}
|
|
114
|
+
function formatLinkToken(entry) {
|
|
115
|
+
let token = `${encodeLinkId(entry.key)}:${encodeLinkId(entry.id)}`;
|
|
116
|
+
const args = entry.args;
|
|
117
|
+
if (args) {
|
|
118
|
+
const keys = Object.keys(args).sort();
|
|
119
|
+
if (keys.length > 0) {
|
|
120
|
+
token += `:${keys.map((k) => `${encodeLinkArg(k)}-${encodeLinkArg(args[k] ?? "")}`).join("_")}`;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return token;
|
|
124
|
+
}
|
|
125
|
+
function readPanelsParam(searchOrUrl) {
|
|
126
|
+
const q = searchOrUrl.indexOf("?");
|
|
127
|
+
const hash = searchOrUrl.indexOf("#");
|
|
128
|
+
let search;
|
|
129
|
+
if (q === -1) {
|
|
130
|
+
search = searchOrUrl.includes("=") && !searchOrUrl.includes("/") ? searchOrUrl : "";
|
|
131
|
+
} else {
|
|
132
|
+
search = searchOrUrl.slice(q + 1, hash > q ? hash : void 0);
|
|
133
|
+
}
|
|
134
|
+
return new URLSearchParams(search).get("panels");
|
|
135
|
+
}
|
|
136
|
+
function withPanelsParam(search, param) {
|
|
137
|
+
const params = new URLSearchParams(search.startsWith("?") ? search.slice(1) : search);
|
|
138
|
+
if (param) params.set("panels", param);
|
|
139
|
+
else params.delete("panels");
|
|
140
|
+
const qs = params.toString();
|
|
141
|
+
return qs ? `?${qs}` : "";
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// src/url/OverlayLinkManager.tsx
|
|
145
|
+
var import_react3 = require("react");
|
|
146
|
+
var import_summon3 = require("@ai-matrx/summon");
|
|
147
|
+
var import_react4 = require("@ai-matrx/summon/react");
|
|
148
|
+
|
|
149
|
+
// src/url/door.ts
|
|
150
|
+
var import_summon2 = require("@ai-matrx/summon");
|
|
151
|
+
function openRegisteredOverlay(dispatch, overlayId, instanceId = import_summon2.DEFAULT_INSTANCE_ID, data) {
|
|
152
|
+
const defaults = (0, import_summon2.getOverlayMeta)(overlayId)?.defaults;
|
|
153
|
+
dispatch(
|
|
154
|
+
import_summon2.overlaysRawActions.openOverlay({
|
|
155
|
+
overlayId,
|
|
156
|
+
instanceId,
|
|
157
|
+
...data === void 0 ? {} : { data },
|
|
158
|
+
...isRecord(defaults) ? { defaults } : {}
|
|
159
|
+
})
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
function isRecord(value) {
|
|
163
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
164
|
+
}
|
|
165
|
+
function canonicalLinkKey(key) {
|
|
166
|
+
return (0, import_summon2.getOverlayMetaByLinkKey)(key)?.link?.key ?? key;
|
|
167
|
+
}
|
|
168
|
+
function canonicalizeToken(raw) {
|
|
169
|
+
const at = raw.indexOf(":");
|
|
170
|
+
const key = at === -1 ? raw : raw.slice(0, at);
|
|
171
|
+
const canonical = canonicalLinkKey(key);
|
|
172
|
+
if (canonical === key) return raw;
|
|
173
|
+
return at === -1 ? canonical : `${canonical}${raw.slice(at)}`;
|
|
174
|
+
}
|
|
175
|
+
function hydrateLinkToken(token, dispatch, onError) {
|
|
176
|
+
const meta = (0, import_summon2.getOverlayMetaByLinkKey)(token.key);
|
|
177
|
+
const link = meta?.link;
|
|
178
|
+
if (!meta || !link) return "unknown";
|
|
179
|
+
const ownerId = meta.id;
|
|
180
|
+
const settle = (result) => result === "refused" ? "refused" : "opened";
|
|
181
|
+
const fail = (error) => {
|
|
182
|
+
onError?.(error, token);
|
|
183
|
+
return "failed";
|
|
184
|
+
};
|
|
185
|
+
try {
|
|
186
|
+
const result = link.hydrate({
|
|
187
|
+
id: token.id ?? "",
|
|
188
|
+
args: token.args ?? {},
|
|
189
|
+
open: (options) => openRegisteredOverlay(dispatch, ownerId, options?.instanceId ?? import_summon2.DEFAULT_INSTANCE_ID, options?.data),
|
|
190
|
+
// The host's store dispatch IS the host dispatch at runtime; its thunk-aware type is the
|
|
191
|
+
// host's augmentation (`OverlayHostTypes`), which the package cannot see.
|
|
192
|
+
dispatch
|
|
193
|
+
});
|
|
194
|
+
if (result && typeof result.then === "function") {
|
|
195
|
+
return Promise.resolve(result).then(settle, fail);
|
|
196
|
+
}
|
|
197
|
+
return settle(result);
|
|
198
|
+
} catch (error) {
|
|
199
|
+
return fail(error);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
async function openFromLink(searchOrUrl, options) {
|
|
203
|
+
const tokens = parseLinkParam(readPanelsParam(searchOrUrl));
|
|
204
|
+
const result = { opened: [], refused: [], unknown: [], failed: [] };
|
|
205
|
+
const unknown = tokens.filter((token) => !(0, import_summon2.getOverlayMetaByLinkKey)(token.key)?.link);
|
|
206
|
+
if (options.requireAll && unknown.length > 0) {
|
|
207
|
+
result.unknown.push(...unknown.map((token) => token.raw));
|
|
208
|
+
return result;
|
|
209
|
+
}
|
|
210
|
+
const outcomes = tokens.map((token) => hydrateLinkToken(token, options.dispatch, options.onError));
|
|
211
|
+
const settled = await Promise.all(outcomes.map((outcome) => Promise.resolve(outcome)));
|
|
212
|
+
settled.forEach((outcome, index) => {
|
|
213
|
+
const raw = tokens[index]?.raw ?? "";
|
|
214
|
+
result[outcome === "opened" ? "opened" : outcome].push(raw);
|
|
215
|
+
});
|
|
216
|
+
return result;
|
|
217
|
+
}
|
|
218
|
+
function readOpenPanelRequest(raw) {
|
|
219
|
+
if (!isRecord(raw)) return "payload must be an object";
|
|
220
|
+
const { panelId, instanceId, data } = raw;
|
|
221
|
+
if (typeof panelId !== "string" || panelId.length === 0) return "panelId must be a non-empty string";
|
|
222
|
+
if (instanceId !== void 0 && instanceId !== null && (typeof instanceId !== "string" || instanceId.length === 0)) {
|
|
223
|
+
return "instanceId must be a non-empty string when given";
|
|
224
|
+
}
|
|
225
|
+
if (data !== void 0 && data !== null && !isRecord(data)) return "data must be an object when given";
|
|
226
|
+
return {
|
|
227
|
+
panelId,
|
|
228
|
+
...typeof instanceId === "string" ? { instanceId } : {},
|
|
229
|
+
...isRecord(data) ? { data } : {}
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
function openPanelRequest(rawPayload, options) {
|
|
233
|
+
const request = readOpenPanelRequest(rawPayload);
|
|
234
|
+
if (typeof request === "string") return { ok: false, error: "invalid_payload", details: request };
|
|
235
|
+
if (!(0, import_summon2.isRegisteredOverlay)(request.panelId)) {
|
|
236
|
+
return {
|
|
237
|
+
ok: false,
|
|
238
|
+
error: "unknown_panel",
|
|
239
|
+
details: { panelId: request.panelId, hint: "panelId must be the id of a registered overlay definition." }
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
openRegisteredOverlay(options.dispatch, request.panelId, request.instanceId ?? import_summon2.DEFAULT_INSTANCE_ID, request.data);
|
|
243
|
+
return { ok: true, opened: true, panelId: request.panelId, instanceId: request.instanceId ?? null };
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// src/url/OverlayLinkManager.tsx
|
|
247
|
+
function serializeLinkEntries(entries, managedKeys) {
|
|
248
|
+
const managed = managedKeys ? new Set(managedKeys) : null;
|
|
249
|
+
return Object.values(entries).filter((entry) => !managed || managed.has(entry.typeKey)).map((entry) => formatLinkToken({ key: entry.typeKey, id: entry.instanceId, args: entry.args })).join(",");
|
|
250
|
+
}
|
|
251
|
+
function mergeManagedLinkParam(currentParam, nextManaged, managedKeys) {
|
|
252
|
+
if (!managedKeys) return nextManaged;
|
|
253
|
+
const managed = new Set(managedKeys);
|
|
254
|
+
const unmanaged = currentParam.split(",").filter(Boolean).filter((token) => !managed.has(token.split(":")[0] ?? ""));
|
|
255
|
+
return [...unmanaged, ...nextManaged.split(",").filter(Boolean)].join(",");
|
|
256
|
+
}
|
|
257
|
+
function withUnresolvedTokens(managedParam, unresolvedTokens) {
|
|
258
|
+
if (unresolvedTokens.length === 0) return managedParam;
|
|
259
|
+
const managed = managedParam.split(",").filter(Boolean);
|
|
260
|
+
const represented = new Set(managed.map((token) => canonicalLinkKey(token.split(":")[0] ?? "")));
|
|
261
|
+
const kept = unresolvedTokens.filter((token) => Boolean(token) && !represented.has(canonicalLinkKey(token.split(":")[0] ?? "")));
|
|
262
|
+
return [...managed, ...kept].join(",");
|
|
263
|
+
}
|
|
264
|
+
function unopenedWindowNotice(keys) {
|
|
265
|
+
return `This link names a window this build could not open: ${keys.join(", ")}. The link is unchanged in your address bar \u2014 reload to try again, or report it if it keeps failing.`;
|
|
266
|
+
}
|
|
267
|
+
var noopSubscribe = () => () => {
|
|
268
|
+
};
|
|
269
|
+
function OverlayLinkManager({ managedKeys, noticeDeadlineMs }) {
|
|
270
|
+
const ports = (0, import_react4.useSummonPorts)();
|
|
271
|
+
const dispatch = (0, import_react4.useSummonDispatch)();
|
|
272
|
+
const entries = (0, import_react4.useSummonSelector)(import_summon3.selectUrlSyncEntries);
|
|
273
|
+
const isHydrated = (0, import_react4.useSummonSelector)(import_summon3.selectIsUrlHydrated);
|
|
274
|
+
const router = ports.router;
|
|
275
|
+
const search = (0, import_react3.useSyncExternalStore)(router.subscribe ?? noopSubscribe, router.search, () => "");
|
|
276
|
+
const initialLoadDone = (0, import_react3.useRef)(false);
|
|
277
|
+
const unresolved = (0, import_react3.useRef)(/* @__PURE__ */ new Map());
|
|
278
|
+
const announced = (0, import_react3.useRef)(/* @__PURE__ */ new Set());
|
|
279
|
+
const [noticeDue, setNoticeDue] = (0, import_react3.useState)(false);
|
|
280
|
+
const [asyncTick, setAsyncTick] = (0, import_react3.useState)(0);
|
|
281
|
+
const deadline = noticeDeadlineMs ?? import_summon3.LAZY_WINDOW_MOUNT_DEADLINE_MS;
|
|
282
|
+
const announce = (keys, reason) => {
|
|
283
|
+
const fresh = keys.filter((key) => !announced.current.has(key));
|
|
284
|
+
if (fresh.length === 0) return;
|
|
285
|
+
for (const key of fresh) announced.current.add(key);
|
|
286
|
+
const joined = fresh.join(", ");
|
|
287
|
+
const why = reason === "no-hydrator" ? `no registered definition owns ${fresh.length > 1 ? "these keys" : "this key"}` : reason === "hydrator-threw" ? "its hydrator threw" : `hydrated, but no window registered its link within ${deadline}ms`;
|
|
288
|
+
const message = `[overlays/url] ?panels= token(s) [${joined}]: ${why}. The token is KEPT in the address. Give the overlay definition a matching \`link\` (key or alias), or stop publishing the link.`;
|
|
289
|
+
ports.errors.capture(new Error(message), {
|
|
290
|
+
source: "url-panel-unopened",
|
|
291
|
+
relation: `?panels=${joined}`,
|
|
292
|
+
code: reason,
|
|
293
|
+
typeKeys: fresh,
|
|
294
|
+
deadlineMs: deadline,
|
|
295
|
+
userMessage: unopenedWindowNotice(fresh)
|
|
296
|
+
});
|
|
297
|
+
ports.notify.error(unopenedWindowNotice(fresh), { duration: 12e3 });
|
|
298
|
+
};
|
|
299
|
+
const announceRef = (0, import_react3.useRef)(announce);
|
|
300
|
+
announceRef.current = announce;
|
|
301
|
+
(0, import_react3.useEffect)(() => {
|
|
302
|
+
if (initialLoadDone.current) return;
|
|
303
|
+
initialLoadDone.current = true;
|
|
304
|
+
const managed = managedKeys ? new Set(managedKeys) : null;
|
|
305
|
+
const tokens = parseLinkParam(readPanelsParam(router.search()));
|
|
306
|
+
const noHydrator = [];
|
|
307
|
+
for (const token of tokens) {
|
|
308
|
+
if (managed && !managed.has(token.key)) continue;
|
|
309
|
+
const canonicalKey = canonicalLinkKey(token.key);
|
|
310
|
+
const pendingKey = `${canonicalKey}\0${token.id ?? ""}`;
|
|
311
|
+
const pending = { token: canonicalizeToken(token.raw), tokenKey: token.key, canonicalKey };
|
|
312
|
+
if (!(0, import_summon3.getOverlayMetaByLinkKey)(token.key)?.link) {
|
|
313
|
+
unresolved.current.set(pendingKey, { ...pending, token: token.raw });
|
|
314
|
+
noHydrator.push(token.key);
|
|
315
|
+
continue;
|
|
316
|
+
}
|
|
317
|
+
unresolved.current.set(pendingKey, pending);
|
|
318
|
+
const outcome = hydrateLinkToken(token, dispatch, (error) => {
|
|
319
|
+
ports.errors.capture(error, { source: "url-panel-hydrator", token: token.raw });
|
|
320
|
+
});
|
|
321
|
+
const apply = (result) => {
|
|
322
|
+
if (result === "refused" && unresolved.current.get(pendingKey) === pending) {
|
|
323
|
+
unresolved.current.delete(pendingKey);
|
|
324
|
+
setAsyncTick((n) => n + 1);
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
if (typeof outcome === "string") apply(outcome);
|
|
328
|
+
else void outcome.then(apply);
|
|
329
|
+
}
|
|
330
|
+
if (noHydrator.length > 0) announceRef.current(noHydrator, "no-hydrator");
|
|
331
|
+
dispatch((0, import_summon3.setHydrated)());
|
|
332
|
+
}, [dispatch, managedKeys, router, ports]);
|
|
333
|
+
(0, import_react3.useEffect)(() => {
|
|
334
|
+
if (!isHydrated || unresolved.current.size === 0) return void 0;
|
|
335
|
+
const timer = setTimeout(() => setNoticeDue(true), deadline);
|
|
336
|
+
return () => clearTimeout(timer);
|
|
337
|
+
}, [isHydrated, deadline]);
|
|
338
|
+
(0, import_react3.useEffect)(() => {
|
|
339
|
+
if (!isHydrated) return;
|
|
340
|
+
const observed = new Set(Object.values(entries).map((entry) => canonicalLinkKey(entry.typeKey)));
|
|
341
|
+
for (const [key, pending] of [...unresolved.current.entries()]) {
|
|
342
|
+
if (observed.has(pending.canonicalKey)) unresolved.current.delete(key);
|
|
343
|
+
}
|
|
344
|
+
const stillPending = [...unresolved.current.values()];
|
|
345
|
+
if (noticeDue && stillPending.length > 0) {
|
|
346
|
+
announceRef.current(
|
|
347
|
+
stillPending.map((pending) => pending.tokenKey),
|
|
348
|
+
"never-registered"
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
const liveSearch = router.search();
|
|
352
|
+
const currentParam = readPanelsParam(liveSearch) ?? "";
|
|
353
|
+
const nextManaged = withUnresolvedTokens(
|
|
354
|
+
serializeLinkEntries(entries, managedKeys),
|
|
355
|
+
stillPending.map((pending) => pending.token)
|
|
356
|
+
);
|
|
357
|
+
const nextParam = mergeManagedLinkParam(currentParam, nextManaged, managedKeys);
|
|
358
|
+
if (currentParam !== nextParam) router.replaceSearch(withPanelsParam(liveSearch, nextParam));
|
|
359
|
+
}, [entries, isHydrated, managedKeys, router, search, noticeDue, asyncTick]);
|
|
360
|
+
return null;
|
|
361
|
+
}
|
|
362
|
+
//# sourceMappingURL=url.cjs.map
|