@driggsby/sdk 0.2.2 → 0.2.4
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/driggsby-sdk.js +35 -5
- package/dist/sdk-core.d.ts +3 -0
- package/dist/sdk-core.js +33 -3
- package/package.json +1 -1
package/dist/driggsby-sdk.js
CHANGED
|
@@ -11,6 +11,10 @@ function isAllowedHostOrigin(origin, allowLocalHostOrigins) {
|
|
|
11
11
|
if (EXACT_ALLOWED_HOST_ORIGINS.has(origin)) return true;
|
|
12
12
|
return allowLocalHostOrigins && LOCAL_HOST_ORIGIN_PATTERN.test(origin);
|
|
13
13
|
}
|
|
14
|
+
var APP_ROUTE_PATTERN = /^#[A-Za-z0-9/\-._]{1,256}$/;
|
|
15
|
+
function sanitizeAppRoute(value) {
|
|
16
|
+
return typeof value === "string" && APP_ROUTE_PATTERN.test(value) ? value : "";
|
|
17
|
+
}
|
|
14
18
|
function parseProtocolMessage(data) {
|
|
15
19
|
if (typeof data !== "object" || data === null) return null;
|
|
16
20
|
const record = data;
|
|
@@ -27,15 +31,17 @@ function parseProtocolMessage(data) {
|
|
|
27
31
|
type,
|
|
28
32
|
id: typeof record.id === "string" ? record.id : null,
|
|
29
33
|
result: record.result,
|
|
30
|
-
errorMessage
|
|
34
|
+
errorMessage,
|
|
35
|
+
route: sanitizeAppRoute(record.route)
|
|
31
36
|
};
|
|
32
37
|
}
|
|
33
38
|
var SdkCore = class {
|
|
34
39
|
// Wired by the page entry: remove the standalone note, log tool errors,
|
|
35
|
-
// reload on a new deployed version.
|
|
40
|
+
// reload on a new deployed version, restore the host-remembered route.
|
|
36
41
|
onHostReady = null;
|
|
37
42
|
onToolError = null;
|
|
38
43
|
onNewVersion = null;
|
|
44
|
+
onRestoreRoute = null;
|
|
39
45
|
post;
|
|
40
46
|
allowLocalHostOrigins;
|
|
41
47
|
hostOrigin = null;
|
|
@@ -73,7 +79,7 @@ var SdkCore = class {
|
|
|
73
79
|
const message = parseProtocolMessage(data);
|
|
74
80
|
if (message === null) return;
|
|
75
81
|
if (message.type === "hello") {
|
|
76
|
-
this.handleHello(origin);
|
|
82
|
+
this.handleHello(origin, message.route);
|
|
77
83
|
return;
|
|
78
84
|
}
|
|
79
85
|
if (this.hostOrigin === null || origin !== this.hostOrigin) return;
|
|
@@ -85,13 +91,27 @@ var SdkCore = class {
|
|
|
85
91
|
this.onNewVersion?.();
|
|
86
92
|
}
|
|
87
93
|
}
|
|
88
|
-
handleHello(origin) {
|
|
94
|
+
handleHello(origin, route) {
|
|
89
95
|
if (this.hostOrigin !== null) return;
|
|
90
96
|
if (!isAllowedHostOrigin(origin, this.allowLocalHostOrigins)) return;
|
|
91
97
|
this.hostOrigin = origin;
|
|
92
98
|
this.onHostReady?.();
|
|
99
|
+
if (route !== "") this.onRestoreRoute?.(route);
|
|
93
100
|
this.requestAllWatches();
|
|
94
101
|
}
|
|
102
|
+
// The app moved to a new in-page location; tell the pinned host so it
|
|
103
|
+
// can mirror the fragment into its own page URL. Anything unmirrorable —
|
|
104
|
+
// an empty hash (the app is back at its no-hash location) or a hash
|
|
105
|
+
// outside the strict shape (a query-bearing route, say) — is reported
|
|
106
|
+
// as the bare-"#" cleared sentinel: the host must forget rather than
|
|
107
|
+
// keep claiming a location the app already left, or a refresh or
|
|
108
|
+
// redeploy reload would yank the user to a stale page. Before the
|
|
109
|
+
// handshake there is nobody to tell.
|
|
110
|
+
reportRoute(hash) {
|
|
111
|
+
if (this.hostOrigin === null) return;
|
|
112
|
+
const route = sanitizeAppRoute(hash) || "#";
|
|
113
|
+
this.post({ protocol: PROTOCOL, type: "route", hash: route }, this.hostOrigin);
|
|
114
|
+
}
|
|
95
115
|
handleResult(message) {
|
|
96
116
|
const entry = message.id === null ? void 0 : this.watches.get(message.id);
|
|
97
117
|
if (entry === void 0) return;
|
|
@@ -150,13 +170,23 @@ var core = new SdkCore((message, targetOrigin) => {
|
|
|
150
170
|
// exactly the Driggsby console origin.
|
|
151
171
|
allowLocalHostOrigins: isLocalAppHostname(window.location.hostname)
|
|
152
172
|
});
|
|
153
|
-
core.onHostReady = removeStandaloneNote;
|
|
154
173
|
core.onNewVersion = () => {
|
|
155
174
|
window.location.reload();
|
|
156
175
|
};
|
|
157
176
|
core.onToolError = (tool, message) => {
|
|
158
177
|
console.error(`Driggsby: the ${tool} call didn't work \u2014 ${message}`);
|
|
159
178
|
};
|
|
179
|
+
core.onHostReady = () => {
|
|
180
|
+
removeStandaloneNote();
|
|
181
|
+
if (window.location.hash !== "") core.reportRoute(window.location.hash);
|
|
182
|
+
};
|
|
183
|
+
core.onRestoreRoute = (route) => {
|
|
184
|
+
if (window.location.hash === route) return;
|
|
185
|
+
window.location.replace((window.location.href.split("#")[0] ?? "") + route);
|
|
186
|
+
};
|
|
187
|
+
window.addEventListener("hashchange", () => {
|
|
188
|
+
core.reportRoute(window.location.hash);
|
|
189
|
+
});
|
|
160
190
|
window.addEventListener("message", (event) => {
|
|
161
191
|
core.handleMessage(event.origin, event.source === window.parent, event.data);
|
|
162
192
|
});
|
package/dist/sdk-core.d.ts
CHANGED
|
@@ -4,10 +4,12 @@ export declare function isLocalAppHostname(hostname: string): boolean;
|
|
|
4
4
|
export declare function isAllowedHostOrigin(origin: string, allowLocalHostOrigins: boolean): boolean;
|
|
5
5
|
export type WatchCallback = (result: unknown) => void;
|
|
6
6
|
export type PostFunction = (message: Record<string, unknown>, targetOrigin: string) => void;
|
|
7
|
+
export declare function sanitizeAppRoute(value: unknown): string;
|
|
7
8
|
export declare class SdkCore {
|
|
8
9
|
onHostReady: (() => void) | null;
|
|
9
10
|
onToolError: ((tool: string, message: string) => void) | null;
|
|
10
11
|
onNewVersion: (() => void) | null;
|
|
12
|
+
onRestoreRoute: ((route: string) => void) | null;
|
|
11
13
|
private readonly post;
|
|
12
14
|
private readonly allowLocalHostOrigins;
|
|
13
15
|
private hostOrigin;
|
|
@@ -20,6 +22,7 @@ export declare class SdkCore {
|
|
|
20
22
|
watch(tool: string, params: Record<string, unknown> | null | undefined, callback: WatchCallback): () => void;
|
|
21
23
|
handleMessage(origin: string, fromParent: boolean, data: unknown): void;
|
|
22
24
|
private handleHello;
|
|
25
|
+
reportRoute(hash: string): void;
|
|
23
26
|
private handleResult;
|
|
24
27
|
private requestAllWatches;
|
|
25
28
|
private requestWatch;
|
package/dist/sdk-core.js
CHANGED
|
@@ -38,6 +38,15 @@ export function isAllowedHostOrigin(origin, allowLocalHostOrigins) {
|
|
|
38
38
|
return true;
|
|
39
39
|
return allowLocalHostOrigins && LOCAL_HOST_ORIGIN_PATTERN.test(origin);
|
|
40
40
|
}
|
|
41
|
+
// The app's in-page location, as a URL fragment. The host mirrors it into
|
|
42
|
+
// its own page URL (so refresh and shared links restore it) and hands it
|
|
43
|
+
// back in the hello after a reload. The shape is deliberately narrow — a
|
|
44
|
+
// short, boring hash — and must stay identical to the host's sanitizer;
|
|
45
|
+
// anything else normalizes to ''.
|
|
46
|
+
const APP_ROUTE_PATTERN = /^#[A-Za-z0-9/\-._]{1,256}$/;
|
|
47
|
+
export function sanitizeAppRoute(value) {
|
|
48
|
+
return typeof value === "string" && APP_ROUTE_PATTERN.test(value) ? value : "";
|
|
49
|
+
}
|
|
41
50
|
// Untrusted wire data -> a normalized message, or null when it is not ours.
|
|
42
51
|
function parseProtocolMessage(data) {
|
|
43
52
|
if (typeof data !== "object" || data === null)
|
|
@@ -59,14 +68,16 @@ function parseProtocolMessage(data) {
|
|
|
59
68
|
id: typeof record.id === "string" ? record.id : null,
|
|
60
69
|
result: record.result,
|
|
61
70
|
errorMessage,
|
|
71
|
+
route: sanitizeAppRoute(record.route),
|
|
62
72
|
};
|
|
63
73
|
}
|
|
64
74
|
export class SdkCore {
|
|
65
75
|
// Wired by the page entry: remove the standalone note, log tool errors,
|
|
66
|
-
// reload on a new deployed version.
|
|
76
|
+
// reload on a new deployed version, restore the host-remembered route.
|
|
67
77
|
onHostReady = null;
|
|
68
78
|
onToolError = null;
|
|
69
79
|
onNewVersion = null;
|
|
80
|
+
onRestoreRoute = null;
|
|
70
81
|
post;
|
|
71
82
|
allowLocalHostOrigins;
|
|
72
83
|
hostOrigin = null;
|
|
@@ -112,7 +123,7 @@ export class SdkCore {
|
|
|
112
123
|
if (message === null)
|
|
113
124
|
return;
|
|
114
125
|
if (message.type === "hello") {
|
|
115
|
-
this.handleHello(origin);
|
|
126
|
+
this.handleHello(origin, message.route);
|
|
116
127
|
return;
|
|
117
128
|
}
|
|
118
129
|
// Everything after the handshake must come from the pinned host origin.
|
|
@@ -128,15 +139,34 @@ export class SdkCore {
|
|
|
128
139
|
this.onNewVersion?.();
|
|
129
140
|
}
|
|
130
141
|
}
|
|
131
|
-
handleHello(origin) {
|
|
142
|
+
handleHello(origin, route) {
|
|
132
143
|
if (this.hostOrigin !== null)
|
|
133
144
|
return; // pinned; later hellos are ignored
|
|
134
145
|
if (!isAllowedHostOrigin(origin, this.allowLocalHostOrigins))
|
|
135
146
|
return;
|
|
136
147
|
this.hostOrigin = origin;
|
|
137
148
|
this.onHostReady?.();
|
|
149
|
+
// Restore before the watches fire so the app renders the remembered
|
|
150
|
+
// page (the host page's fragment, or the pre-redeploy location) with
|
|
151
|
+
// its first data, not after it.
|
|
152
|
+
if (route !== "")
|
|
153
|
+
this.onRestoreRoute?.(route);
|
|
138
154
|
this.requestAllWatches();
|
|
139
155
|
}
|
|
156
|
+
// The app moved to a new in-page location; tell the pinned host so it
|
|
157
|
+
// can mirror the fragment into its own page URL. Anything unmirrorable —
|
|
158
|
+
// an empty hash (the app is back at its no-hash location) or a hash
|
|
159
|
+
// outside the strict shape (a query-bearing route, say) — is reported
|
|
160
|
+
// as the bare-"#" cleared sentinel: the host must forget rather than
|
|
161
|
+
// keep claiming a location the app already left, or a refresh or
|
|
162
|
+
// redeploy reload would yank the user to a stale page. Before the
|
|
163
|
+
// handshake there is nobody to tell.
|
|
164
|
+
reportRoute(hash) {
|
|
165
|
+
if (this.hostOrigin === null)
|
|
166
|
+
return;
|
|
167
|
+
const route = sanitizeAppRoute(hash) || "#";
|
|
168
|
+
this.post({ protocol: PROTOCOL, type: "route", hash: route }, this.hostOrigin);
|
|
169
|
+
}
|
|
140
170
|
handleResult(message) {
|
|
141
171
|
const entry = message.id === null ? undefined : this.watches.get(message.id);
|
|
142
172
|
if (entry === undefined)
|