@oddington/sdk 0.1.18 → 0.1.20
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/browser.js
CHANGED
|
@@ -475,7 +475,11 @@ var AppRoutineContractError = class extends Error {
|
|
|
475
475
|
this.code = code;
|
|
476
476
|
}
|
|
477
477
|
code;
|
|
478
|
-
}, propertyName = /^[a-z][A-Za-z0-9]*(?![\s\S])/, stepId = /^[a-z0-9]+(?:-[a-z0-9]+)*(?![\s\S])/, routineName = /^[a-z0-9][a-z0-9-]{0,62}(?![\s\S])
|
|
478
|
+
}, propertyName = /^[a-z][A-Za-z0-9]*(?![\s\S])/, stepId = /^[a-z0-9]+(?:-[a-z0-9]+)*(?![\s\S])/, routineName = /^[a-z0-9][a-z0-9-]{0,62}(?![\s\S])/;
|
|
479
|
+
function isAppRoutineName(value) {
|
|
480
|
+
return typeof value == "string" && routineName.test(value);
|
|
481
|
+
}
|
|
482
|
+
var owns = (value, key) => Object.hasOwn(value, key), invalidContract = () => {
|
|
479
483
|
throw new AppRoutineContractError("invalid_contract");
|
|
480
484
|
}, invalidValue = () => {
|
|
481
485
|
throw new AppRoutineContractError("invalid_value");
|
|
@@ -530,7 +534,7 @@ function parseAppRoutineContract(value) {
|
|
|
530
534
|
return invalidContract();
|
|
531
535
|
}
|
|
532
536
|
}
|
|
533
|
-
if (!record(value) || !fields2(value, ["routine", "input", "steps"], ["result"]) ||
|
|
537
|
+
if (!record(value) || !fields2(value, ["routine", "input", "steps"], ["result"]) || !isAppRoutineName(value.routine) || !denseArray(value.steps) || value.steps.length < 1 || value.steps.length > 64) return invalidContract();
|
|
534
538
|
let input = schema2(value.input), result2 = owns(value, "result") ? schema2(value.result) : void 0, ids = /* @__PURE__ */ new Set(), steps = value.steps.map((step) => !record(step) || !fields2(step, ["id"], ["summary"]) || typeof step.id != "string" || !stepId.test(step.id) || ids.has(step.id) ? invalidContract() : (ids.add(step.id), { id: step.id, ...owns(step, "summary") ? { summary: schema2(step.summary) } : {} }));
|
|
535
539
|
return { routine: value.routine, input, steps, ...result2 === void 0 ? {} : { result: result2 } };
|
|
536
540
|
}
|
|
@@ -1937,14 +1941,14 @@ function response(value) {
|
|
|
1937
1941
|
let answer = object3(value);
|
|
1938
1942
|
return assert(Object.keys(answer).every((key) => ["selection", "note"].includes(key))), typeof answer.selection == "string" ? assert(text3(answer.selection) && answer.selection === answer.selection.trim()) : recordReference(answer.selection), Object.hasOwn(answer, "note") && assert(text3(answer.note)), answer;
|
|
1939
1943
|
}
|
|
1940
|
-
function parseInboxRequest(value) {
|
|
1944
|
+
function parseInboxRequest(value, questionListContext = "local") {
|
|
1941
1945
|
try {
|
|
1942
1946
|
let envelope = object3(value);
|
|
1943
1947
|
assert(Object.keys(envelope).sort().join(",") === "input,operation"), assert(typeof envelope.operation == "string" && Object.hasOwn(operations, envelope.operation));
|
|
1944
|
-
let operation2 = envelope.operation, input = object3(envelope.input), required = operations[operation2];
|
|
1945
|
-
assert(required.every((key) => Object.hasOwn(input, key)) && Object.keys(input).every((key) => required.includes(key) || operation2 === "questions.searchRecords" && key === "selected" || (operation2 === "items.list" || operation2 === "questions.list") && key === "beforeNumber"));
|
|
1948
|
+
let operation2 = envelope.operation, input = { ...object3(envelope.input) }, required = operations[operation2];
|
|
1949
|
+
assert(required.every((key) => Object.hasOwn(input, key)) && Object.keys(input).every((key) => required.includes(key) || operation2 === "questions.searchRecords" && key === "selected" || (operation2 === "items.list" || operation2 === "questions.list") && key === "beforeNumber" || operation2 === "questions.list" && questionListContext === "hosted" && key === "routines")), operation2 === "questions.list" && questionListContext === "hosted" && (assert(Object.hasOwn(input, "routines")), assert(array2(input.routines).every(isAppRoutineName)));
|
|
1946
1950
|
for (let key of ["itemId", "personId", "fileId"]) key in input && assert(id2(input[key]));
|
|
1947
|
-
return "revision" in input && assert(integer(input.revision)), "beforeNumber" in input && assert(integer(input.beforeNumber)), "questionId" in input && assert(text3(input.questionId) && input.questionId === input.questionId.trim()), "title" in input && assert(text3(input.title)), "lifecycle" in input && assert(input.lifecycle === "open" || input.lifecycle === "closed"), "query" in input && assert(text3(input.query, !0) && input.query.length <= 256), "selected" in input && recordReference(input.selected), "response" in input && response(input.response), assert(new TextEncoder().encode(JSON.stringify(envelope)).byteLength <= INBOX_INPUT_BYTES), { operation: operation2, input };
|
|
1951
|
+
return "revision" in input && assert(integer(input.revision)), "beforeNumber" in input && assert(integer(input.beforeNumber)), "questionId" in input && assert(text3(input.questionId) && input.questionId === input.questionId.trim()), "title" in input && assert(text3(input.title)), "lifecycle" in input && assert(input.lifecycle === "open" || input.lifecycle === "closed"), "query" in input && assert(text3(input.query, !0) && input.query.length <= 256), "selected" in input && recordReference(input.selected), "response" in input && response(input.response), assert(new TextEncoder().encode(JSON.stringify(envelope)).byteLength <= INBOX_INPUT_BYTES), "routines" in input && (input.routines = [...new Set(input.routines)]), { operation: operation2, input };
|
|
1948
1952
|
} catch {
|
|
1949
1953
|
throw new InboxOperationError(400, "invalid_input");
|
|
1950
1954
|
}
|
|
@@ -2149,6 +2153,19 @@ var sessionKey = "_os.app.session", responseChecks = /* @__PURE__ */ new WeakMap
|
|
|
2149
2153
|
}, AppContextDenied = class extends Error {
|
|
2150
2154
|
}, AppRenewalUnavailable = class extends Error {
|
|
2151
2155
|
};
|
|
2156
|
+
async function untilAborted(work, signal) {
|
|
2157
|
+
if (signal.aborted)
|
|
2158
|
+
throw work.catch(() => {
|
|
2159
|
+
}), new AppRenewalUnavailable();
|
|
2160
|
+
let abort, cancelled = new Promise((_resolve, reject) => {
|
|
2161
|
+
abort = () => reject(new AppRenewalUnavailable()), signal.addEventListener("abort", abort, { once: !0 });
|
|
2162
|
+
});
|
|
2163
|
+
try {
|
|
2164
|
+
return await Promise.race([work, cancelled]);
|
|
2165
|
+
} finally {
|
|
2166
|
+
signal.removeEventListener("abort", abort);
|
|
2167
|
+
}
|
|
2168
|
+
}
|
|
2152
2169
|
async function appResponse(response2, maximum, signal) {
|
|
2153
2170
|
if (signal.aborted || !response2.body || response2.headers.get("content-type")?.split(";")[0]?.trim().toLowerCase() !== "application/json") throw new Error();
|
|
2154
2171
|
let reader = response2.body.getReader(), abort = () => {
|
|
@@ -2188,159 +2205,164 @@ function isAppOrigin(value) {
|
|
|
2188
2205
|
}
|
|
2189
2206
|
}
|
|
2190
2207
|
function appContext(origin, active2) {
|
|
2191
|
-
let binding, credential, denied = !1, controller = new AbortController(), expiry, startup, renewalTimer, renewal, observation, resolution2, disposed = !1, stored = () => globalThis.localStorage.getItem(sessionKey), alive = () => {
|
|
2208
|
+
let binding, credential, pair, serialized, denied = !1, controller = new AbortController(), access = new AbortController(), expiry, startup, renewalTimer, renewal, observation, resolution2, disposed = !1, stored = () => globalThis.localStorage.getItem(sessionKey), alive = () => {
|
|
2192
2209
|
try {
|
|
2193
|
-
return !denied && !disposed && active2() && globalThis.location.origin === origin
|
|
2210
|
+
return !denied && !disposed && active2() && globalThis.location.origin === origin;
|
|
2194
2211
|
} catch {
|
|
2195
2212
|
return !1;
|
|
2196
2213
|
}
|
|
2197
2214
|
}, cancel = () => {
|
|
2198
|
-
controller.abort(), clearTimeout(expiry), clearTimeout(startup), clearTimeout(renewalTimer), clearInterval(monitor), globalThis.removeEventListener?.("storage", storageChanged);
|
|
2215
|
+
controller.abort(), access.abort(), clearTimeout(expiry), clearTimeout(startup), clearTimeout(renewalTimer), clearInterval(monitor), globalThis.removeEventListener?.("storage", storageChanged);
|
|
2216
|
+
for (let event2 of ["pageshow", "focus", "visibilitychange"]) globalThis.removeEventListener?.(event2, wake);
|
|
2199
2217
|
}, invalidate = () => {
|
|
2200
2218
|
denied = !0, cancel();
|
|
2201
2219
|
try {
|
|
2202
|
-
!disposed && active2() && globalThis.location.origin === origin && (!
|
|
2220
|
+
!disposed && active2() && globalThis.location.origin === origin && (!serialized || globalThis.localStorage.getItem(sessionKey) === serialized) && globalThis.localStorage.removeItem(sessionKey);
|
|
2203
2221
|
} catch {
|
|
2204
2222
|
}
|
|
2205
2223
|
}, current2 = () => {
|
|
2206
2224
|
try {
|
|
2207
|
-
return alive() && stored() ===
|
|
2225
|
+
return alive() && !!binding && binding.expiresAt > Date.now() && stored() === serialized;
|
|
2208
2226
|
} catch {
|
|
2209
2227
|
return !1;
|
|
2210
2228
|
}
|
|
2211
|
-
}, sameSession = (value) => !binding ||
|
|
2212
|
-
|
|
2213
|
-
if (!
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2229
|
+
}, sameSession = (value) => !binding || ["appId", "memberId", "workspace", "sessionId"].every((key) => value[key] === binding[key]), bounded = (value) => validBinding(value) && value.expiresAt > Date.now() && value.expiresAt <= Date.now() + 6e5 && sameSession(value), readPair = (candidate) => {
|
|
2230
|
+
let value = JSON.parse(candidate ?? "null");
|
|
2231
|
+
if (!validPair(value) || value.context.expiresAt > Date.now() + 6e5 || !sameSession(value.context)) throw new Error();
|
|
2232
|
+
return value;
|
|
2233
|
+
}, expireAccess = () => {
|
|
2234
|
+
binding && binding.expiresAt <= Date.now() && access.abort();
|
|
2235
|
+
};
|
|
2236
|
+
async function send3(path, candidate, signal, init2 = {}) {
|
|
2237
|
+
let url = `${origin}/_os/app/${path}`, response2;
|
|
2238
|
+
try {
|
|
2239
|
+
response2 = await fetch(url, {
|
|
2240
|
+
...init2,
|
|
2241
|
+
credentials: "omit",
|
|
2242
|
+
redirect: "error",
|
|
2243
|
+
cache: "no-store",
|
|
2244
|
+
referrerPolicy: "no-referrer",
|
|
2245
|
+
signal,
|
|
2246
|
+
headers: {
|
|
2247
|
+
...init2.body === void 0 ? {} : { "content-type": "application/json" },
|
|
2248
|
+
authorization: `Bearer ${candidate}`
|
|
2249
|
+
}
|
|
2250
|
+
});
|
|
2251
|
+
} catch {
|
|
2252
|
+
throw new AppRenewalUnavailable();
|
|
2253
|
+
}
|
|
2222
2254
|
if (response2.redirected || response2.url && response2.url !== url) throw new Error();
|
|
2255
|
+
return response2;
|
|
2256
|
+
}
|
|
2257
|
+
async function proof(candidate, signal = controller.signal) {
|
|
2258
|
+
if (!alive() || !validProof(candidate)) throw new Error();
|
|
2259
|
+
let response2 = await send3("context", candidate, signal);
|
|
2223
2260
|
if (response2.status === 401 || response2.status === 403) throw new AppContextDenied();
|
|
2261
|
+
if (response2.status >= 500) throw new AppRenewalUnavailable();
|
|
2224
2262
|
if (!response2.ok) throw new Error();
|
|
2225
2263
|
let value = await appResponse(response2, 4096, signal);
|
|
2226
2264
|
if (!alive() || !bounded(value)) throw new Error();
|
|
2227
2265
|
return value;
|
|
2228
2266
|
}
|
|
2229
|
-
function install(value, candidate) {
|
|
2230
|
-
binding = { ...value }, credential = candidate, clearTimeout(expiry), clearTimeout(renewalTimer);
|
|
2267
|
+
function install(value, candidate, raw) {
|
|
2268
|
+
expireAccess(), access.signal.aborted && (access = new AbortController()), binding = { ...value }, pair = candidate, credential = candidate.credential, serialized = raw, clearTimeout(expiry), clearTimeout(renewalTimer);
|
|
2231
2269
|
let remaining = binding.expiresAt - Date.now();
|
|
2232
|
-
expiry = setTimeout(
|
|
2233
|
-
renew()
|
|
2270
|
+
expiry = setTimeout(expireAccess, remaining), renewalTimer = setTimeout(() => {
|
|
2271
|
+
renew().catch(() => {
|
|
2272
|
+
});
|
|
2234
2273
|
}, remaining - Math.min(6e4, remaining / 5));
|
|
2235
2274
|
}
|
|
2236
|
-
async function synchronize() {
|
|
2275
|
+
async function synchronize(signal = controller.signal) {
|
|
2237
2276
|
if (!alive()) throw new Error();
|
|
2238
|
-
if (
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2277
|
+
if (signal.aborted) throw new AppRenewalUnavailable();
|
|
2278
|
+
if (!credential || stored() === serialized) return;
|
|
2279
|
+
if (!binding) throw new Error();
|
|
2280
|
+
if (observation) return untilAborted(observation, signal);
|
|
2281
|
+
observation = (async () => {
|
|
2282
|
+
for (; alive(); ) {
|
|
2283
|
+
let candidate = stored();
|
|
2284
|
+
if (!candidate) throw new Error();
|
|
2285
|
+
let value, replacement;
|
|
2286
|
+
try {
|
|
2287
|
+
replacement = readPair(candidate), value = await proof(replacement.credential, signal);
|
|
2288
|
+
} catch (error) {
|
|
2289
|
+
if (stored() !== candidate) continue;
|
|
2290
|
+
throw error;
|
|
2291
|
+
}
|
|
2292
|
+
if (stored() === candidate) {
|
|
2293
|
+
if (value.expiresAt !== replacement.context.expiresAt) throw new Error();
|
|
2294
|
+
install(value, replacement, candidate);
|
|
2295
|
+
return;
|
|
2256
2296
|
}
|
|
2257
|
-
throw new Error();
|
|
2258
|
-
})();
|
|
2259
|
-
try {
|
|
2260
|
-
await observation;
|
|
2261
|
-
} catch {
|
|
2262
|
-
throw invalidate(), new Error("Local app context unavailable.");
|
|
2263
|
-
} finally {
|
|
2264
|
-
observation = void 0;
|
|
2265
2297
|
}
|
|
2298
|
+
throw new Error();
|
|
2299
|
+
})();
|
|
2300
|
+
let observed = observation;
|
|
2301
|
+
try {
|
|
2302
|
+
await untilAborted(observed, signal);
|
|
2303
|
+
} catch (error) {
|
|
2304
|
+
throw error instanceof AppRenewalUnavailable || error instanceof AppResponseReadError || invalidate(), error;
|
|
2305
|
+
} finally {
|
|
2306
|
+
observation === observed && (observation = void 0);
|
|
2266
2307
|
}
|
|
2267
2308
|
}
|
|
2268
2309
|
async function renew() {
|
|
2269
|
-
if (
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2310
|
+
if (renewal) return renewal;
|
|
2311
|
+
if (!binding || !pair || !alive()) throw new Error();
|
|
2312
|
+
renewal = (async () => {
|
|
2313
|
+
let requestController = new AbortController(), abort = () => requestController.abort();
|
|
2314
|
+
controller.signal.addEventListener("abort", abort, { once: !0 });
|
|
2315
|
+
let timeout = setTimeout(abort, 2e4), previous = serialized;
|
|
2316
|
+
try {
|
|
2317
|
+
if (await synchronize(requestController.signal), serialized !== previous) return;
|
|
2318
|
+
let response2 = await send3("renew", pair.renewalCredential, requestController.signal, { method: "POST" }), value = await appResponse(response2, 4096, requestController.signal);
|
|
2319
|
+
if (response2.status === 503 && value && typeof value == "object" && !Array.isArray(value) && Object.keys(value).join(",") === "error" && "error" in value && value.error === "renewal_unavailable")
|
|
2320
|
+
throw new AppRenewalUnavailable();
|
|
2321
|
+
if (response2.status === 403 && value && typeof value == "object" && !Array.isArray(value) && Object.keys(value).join(",") === "error" && "error" in value && value.error === "renewal_conflict") {
|
|
2322
|
+
for (; alive() && stored() === previous && !requestController.signal.aborted; )
|
|
2323
|
+
await new Promise((resolve2, reject) => {
|
|
2324
|
+
let abortWait = () => {
|
|
2325
|
+
clearTimeout(timer), reject(new Error());
|
|
2326
|
+
}, timer = setTimeout(() => {
|
|
2327
|
+
requestController.signal.removeEventListener("abort", abortWait), resolve2();
|
|
2328
|
+
}, 100);
|
|
2329
|
+
requestController.signal.addEventListener("abort", abortWait, { once: !0 });
|
|
2286
2330
|
});
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
});
|
|
2304
|
-
if (requestController.signal.aborted) throw new Error();
|
|
2305
|
-
await synchronize();
|
|
2306
|
-
return;
|
|
2307
|
-
}
|
|
2308
|
-
if (!response2.ok) throw new Error();
|
|
2309
|
-
if (!alive() || Date.now() >= deadline || !value || typeof value != "object" || Array.isArray(value) || Object.keys(value).sort().join(",") !== "context,credential") throw new Error();
|
|
2310
|
-
let result2 = value;
|
|
2311
|
-
if (!bounded(result2.context) || typeof result2.credential != "string" || !/^[A-Za-z0-9_-]{43}$/.test(result2.credential) || result2.credential === previous) throw new Error();
|
|
2312
|
-
if (stored() !== previous) {
|
|
2313
|
-
await synchronize();
|
|
2331
|
+
if (requestController.signal.aborted) throw new Error();
|
|
2332
|
+
await synchronize(requestController.signal);
|
|
2333
|
+
return;
|
|
2334
|
+
}
|
|
2335
|
+
if (!response2.ok) throw new Error();
|
|
2336
|
+
if (!alive() || !validPair(value) || !bounded(value.context) || (/* @__PURE__ */ new Set([value.credential, value.renewalCredential, pair.credential, pair.renewalCredential])).size !== 4) throw new Error();
|
|
2337
|
+
if (stored() !== previous) {
|
|
2338
|
+
await synchronize(requestController.signal);
|
|
2339
|
+
return;
|
|
2340
|
+
}
|
|
2341
|
+
let raw = JSON.stringify(value);
|
|
2342
|
+
globalThis.localStorage.setItem(sessionKey, raw), install(value.context, value, raw);
|
|
2343
|
+
} catch (error) {
|
|
2344
|
+
try {
|
|
2345
|
+
if (alive() && stored() && stored() !== previous) {
|
|
2346
|
+
await synchronize(requestController.signal);
|
|
2314
2347
|
return;
|
|
2315
2348
|
}
|
|
2316
|
-
|
|
2317
|
-
} catch (error) {
|
|
2318
|
-
try {
|
|
2319
|
-
if (alive() && stored() && stored() !== previous) {
|
|
2320
|
-
await synchronize();
|
|
2321
|
-
return;
|
|
2322
|
-
}
|
|
2323
|
-
} catch {
|
|
2324
|
-
}
|
|
2325
|
-
try {
|
|
2326
|
-
if (alive() && stored() && stored() !== previous) {
|
|
2327
|
-
await synchronize();
|
|
2328
|
-
return;
|
|
2329
|
-
}
|
|
2330
|
-
} catch {
|
|
2331
|
-
}
|
|
2332
|
-
(error instanceof AppRenewalUnavailable || error instanceof AppResponseReadError || requestController.signal.aborted) && current2() && Date.now() < deadline ? renewalTimer = setTimeout(() => {
|
|
2333
|
-
renew();
|
|
2334
|
-
}, Math.min(5e3, (deadline - Date.now()) / 5)) : invalidate();
|
|
2335
|
-
} finally {
|
|
2336
|
-
clearTimeout(timeout), controller.signal.removeEventListener("abort", abort);
|
|
2349
|
+
} catch {
|
|
2337
2350
|
}
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2351
|
+
throw (error instanceof AppRenewalUnavailable || error instanceof AppResponseReadError || requestController.signal.aborted) && alive() ? current2() && (renewalTimer = setTimeout(
|
|
2352
|
+
() => {
|
|
2353
|
+
renew().catch(() => {
|
|
2354
|
+
});
|
|
2355
|
+
},
|
|
2356
|
+
Math.min(5e3, (binding.expiresAt - Date.now()) / 5)
|
|
2357
|
+
)) : invalidate(), new AppRenewalUnavailable();
|
|
2341
2358
|
} finally {
|
|
2342
|
-
|
|
2359
|
+
clearTimeout(timeout), controller.signal.removeEventListener("abort", abort);
|
|
2343
2360
|
}
|
|
2361
|
+
})();
|
|
2362
|
+
try {
|
|
2363
|
+
await renewal;
|
|
2364
|
+
} finally {
|
|
2365
|
+
renewal = void 0;
|
|
2344
2366
|
}
|
|
2345
2367
|
}
|
|
2346
2368
|
let storageChanged = () => {
|
|
@@ -2349,36 +2371,33 @@ function appContext(origin, active2) {
|
|
|
2349
2371
|
invalidate();
|
|
2350
2372
|
return;
|
|
2351
2373
|
}
|
|
2352
|
-
synchronize().catch(
|
|
2374
|
+
expireAccess(), synchronize().catch(() => {
|
|
2375
|
+
});
|
|
2353
2376
|
}
|
|
2354
2377
|
}, monitor = setInterval(() => {
|
|
2355
2378
|
storageChanged();
|
|
2356
2379
|
}, 100);
|
|
2357
2380
|
globalThis.addEventListener?.("storage", storageChanged);
|
|
2381
|
+
let wake = () => {
|
|
2382
|
+
alive() && resolve().catch(() => {
|
|
2383
|
+
});
|
|
2384
|
+
};
|
|
2385
|
+
for (let event2 of ["pageshow", "focus", "visibilitychange"]) globalThis.addEventListener?.(event2, wake);
|
|
2358
2386
|
let dispose = () => {
|
|
2359
2387
|
disposed = !0, cancel();
|
|
2360
2388
|
};
|
|
2389
|
+
async function settledAccess(signal) {
|
|
2390
|
+
for (await synchronize(); alive() && stored() !== serialized; ) await synchronize();
|
|
2391
|
+
if (signal.aborted || !current2()) throw new Error();
|
|
2392
|
+
}
|
|
2361
2393
|
async function requestData(operation2, input) {
|
|
2362
2394
|
try {
|
|
2363
2395
|
if (operation2 !== "data/query" || !current2()) throw new Error();
|
|
2364
|
-
let
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
referrerPolicy: "no-referrer",
|
|
2370
|
-
signal: controller.signal,
|
|
2371
|
-
headers: { "content-type": "application/json", authorization: `Bearer ${credential}` },
|
|
2372
|
-
body: JSON.stringify(input)
|
|
2373
|
-
});
|
|
2374
|
-
for (await synchronize(); alive() && stored() !== credential; ) await synchronize();
|
|
2375
|
-
if (!current2() || response2.redirected || response2.url && response2.url !== url) throw new Error();
|
|
2376
|
-
if (response2.status === 401 || response2.status === 403)
|
|
2377
|
-
throw admitted === credential && invalidate(), new DataOperationError("access_denied");
|
|
2378
|
-
let result2 = await appResponse(response2, Number.MAX_SAFE_INTEGER, controller.signal);
|
|
2379
|
-
for (await synchronize(); alive() && stored() !== credential; ) await synchronize();
|
|
2380
|
-
if (!current2()) throw new Error();
|
|
2381
|
-
if (!response2.ok) throw dataTransportError(response2.status, result2);
|
|
2396
|
+
let admitted = credential, signal = access.signal, response2 = await send3("data", credential, signal, { method: "POST", body: JSON.stringify(input) });
|
|
2397
|
+
if (await settledAccess(signal), response2.status === 401 || response2.status === 403)
|
|
2398
|
+
throw admitted === credential && current2() && invalidate(), new DataOperationError("access_denied");
|
|
2399
|
+
let result2 = await appResponse(response2, Number.MAX_SAFE_INTEGER, signal);
|
|
2400
|
+
if (await settledAccess(signal), !response2.ok) throw dataTransportError(response2.status, result2);
|
|
2382
2401
|
return result2;
|
|
2383
2402
|
} catch (error) {
|
|
2384
2403
|
throw error instanceof DataOperationError ? error : !current2() || controller.signal.aborted ? new DataOperationError("context_unavailable") : new DataOperationError("unavailable", !0);
|
|
@@ -2386,43 +2405,30 @@ function appContext(origin, active2) {
|
|
|
2386
2405
|
}
|
|
2387
2406
|
async function requestInbox(operation2, input) {
|
|
2388
2407
|
if (!["questions.list", "questions.answer", "questions.searchRecords"].includes(operation2)) throw new Error("Inbox operation unavailable");
|
|
2389
|
-
return requestChecked(
|
|
2408
|
+
return requestChecked("inbox", { method: "POST", body: JSON.stringify({ operation: operation2, input }) }, !0);
|
|
2390
2409
|
}
|
|
2391
2410
|
async function requestRoutine(key, suffix, init2 = {}) {
|
|
2392
|
-
return requestChecked(
|
|
2411
|
+
return requestChecked(`routines/${encodeURIComponent(key)}/${suffix}`, init2);
|
|
2393
2412
|
}
|
|
2394
|
-
async function requestChecked(
|
|
2395
|
-
if (!current2())
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
credentials: "omit",
|
|
2400
|
-
redirect: "error",
|
|
2401
|
-
cache: "no-store",
|
|
2402
|
-
referrerPolicy: "no-referrer",
|
|
2403
|
-
signal: controller.signal,
|
|
2404
|
-
headers: { ...init2.body === void 0 ? {} : { "content-type": "application/json" }, authorization: `Bearer ${credential}` }
|
|
2405
|
-
});
|
|
2406
|
-
for (await synchronize(); alive() && stored() !== credential; ) await synchronize();
|
|
2407
|
-
if (!current2() || response2.redirected || response2.url && response2.url !== url)
|
|
2408
|
-
throw invalidate(), new Error("Local app context unavailable.");
|
|
2409
|
-
return (response2.status === 401 || !inbox2 && response2.status === 403) && admitted === credential && invalidate(), responseChecks.set(response2, async (value) => {
|
|
2410
|
-
for (await synchronize(); alive() && stored() !== credential; ) await synchronize();
|
|
2411
|
-
admitted === credential && value && typeof value == "object" && "error" in value && value.error === "authority_unavailable" && invalidate();
|
|
2413
|
+
async function requestChecked(path, init2, inbox2 = !1) {
|
|
2414
|
+
if (!current2()) throw new Error("Local app context unavailable.");
|
|
2415
|
+
let admitted = credential, signal = access.signal, response2 = await send3(path, credential, signal, init2);
|
|
2416
|
+
return await settledAccess(signal), (response2.status === 401 || !inbox2 && response2.status === 403) && admitted === credential && current2() && invalidate(), responseChecks.set(response2, async (value) => {
|
|
2417
|
+
await settledAccess(signal), admitted === credential && current2() && value && typeof value == "object" && "error" in value && value.error === "authority_unavailable" && invalidate();
|
|
2412
2418
|
}), response2;
|
|
2413
2419
|
}
|
|
2414
2420
|
function context2() {
|
|
2415
|
-
let issued = credential;
|
|
2421
|
+
let issued = credential, signal = access.signal;
|
|
2416
2422
|
return {
|
|
2417
2423
|
kind: "local",
|
|
2418
2424
|
...binding,
|
|
2419
2425
|
get expiresAt() {
|
|
2420
2426
|
return binding.expiresAt;
|
|
2421
2427
|
},
|
|
2422
|
-
isCurrent: current2,
|
|
2423
|
-
signal
|
|
2428
|
+
isCurrent: () => !signal.aborted && current2(),
|
|
2429
|
+
signal,
|
|
2424
2430
|
invalidate: () => {
|
|
2425
|
-
issued === credential && invalidate();
|
|
2431
|
+
issued === credential && current2() && invalidate();
|
|
2426
2432
|
},
|
|
2427
2433
|
requestRoutine,
|
|
2428
2434
|
requestData,
|
|
@@ -2433,25 +2439,34 @@ function appContext(origin, active2) {
|
|
|
2433
2439
|
let resolveOnce = async () => {
|
|
2434
2440
|
let attempted;
|
|
2435
2441
|
try {
|
|
2436
|
-
if (
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
return
|
|
2442
|
+
if (!alive()) throw new Error();
|
|
2443
|
+
if (!pair) {
|
|
2444
|
+
let candidate2 = stored(), retained2 = readPair(candidate2);
|
|
2445
|
+
pair = retained2, binding = { ...retained2.context }, credential = retained2.credential, serialized = candidate2;
|
|
2446
|
+
}
|
|
2447
|
+
if (expireAccess(), binding.expiresAt <= Date.now()) {
|
|
2448
|
+
if (await renew(), !current2()) throw new AppRenewalUnavailable();
|
|
2449
|
+
return context2();
|
|
2450
|
+
}
|
|
2451
|
+
await synchronize();
|
|
2452
|
+
let candidate = stored(), retained = readPair(candidate);
|
|
2453
|
+
if (attempted = candidate ?? void 0, !current2()) throw new AppRenewalUnavailable();
|
|
2454
|
+
let value = await proof(retained.credential);
|
|
2455
|
+
if (stored() !== candidate || serialized !== candidate)
|
|
2456
|
+
return await synchronize(), context2();
|
|
2444
2457
|
if (!current2()) throw new Error();
|
|
2445
|
-
if (binding && value.expiresAt > binding.expiresAt) throw new Error();
|
|
2446
|
-
return (!
|
|
2458
|
+
if (expiry && binding && value.expiresAt > binding.expiresAt) throw new Error();
|
|
2459
|
+
return (!expiry || value.expiresAt !== binding.expiresAt) && install(value, retained, candidate), context2();
|
|
2447
2460
|
} catch (error) {
|
|
2461
|
+
if (alive() && error instanceof AppRenewalUnavailable)
|
|
2462
|
+
throw new Error();
|
|
2448
2463
|
try {
|
|
2449
|
-
if (binding && current2() && attempted && attempted !==
|
|
2450
|
-
if (binding && alive() && stored() && stored() !==
|
|
2464
|
+
if (binding && current2() && attempted && attempted !== serialized) return context2();
|
|
2465
|
+
if (binding && alive() && stored() && stored() !== serialized)
|
|
2451
2466
|
return await synchronize(), context2();
|
|
2452
2467
|
} catch {
|
|
2453
2468
|
}
|
|
2454
|
-
throw error instanceof AppContextDenied && binding && alive() && renewal ? new Error(
|
|
2469
|
+
throw error instanceof AppContextDenied && binding && alive() && renewal ? new Error() : alive() && (error instanceof AppResponseReadError || pair && stored() === serialized && !current2()) ? new Error() : (invalidate(), new Error());
|
|
2455
2470
|
}
|
|
2456
2471
|
}, resolve = () => resolution2 || (resolution2 = resolveOnce().finally(() => {
|
|
2457
2472
|
resolution2 = void 0;
|
|
@@ -2463,10 +2478,18 @@ function appContext(origin, active2) {
|
|
|
2463
2478
|
}, 0);
|
|
2464
2479
|
}, invalidate, dispose };
|
|
2465
2480
|
}
|
|
2481
|
+
function validProof(value) {
|
|
2482
|
+
return typeof value == "string" && /^[A-Za-z0-9_-]{43}$/.test(value);
|
|
2483
|
+
}
|
|
2466
2484
|
function validBinding(value) {
|
|
2467
2485
|
if (!value || typeof value != "object" || Array.isArray(value) || Object.keys(value).sort().join(",") !== "appId,expiresAt,memberId,sessionId,workspace") return !1;
|
|
2468
2486
|
let binding = value;
|
|
2469
|
-
return typeof binding.appId == "string" && /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(binding.appId) && typeof binding.workspace == "string" && /^[a-z0-9][a-z0-9-]{0,62}$/.test(binding.workspace) && typeof binding.memberId == "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(binding.memberId) &&
|
|
2487
|
+
return typeof binding.appId == "string" && /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(binding.appId) && typeof binding.workspace == "string" && /^[a-z0-9][a-z0-9-]{0,62}$/.test(binding.workspace) && typeof binding.memberId == "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(binding.memberId) && validProof(binding.sessionId) && typeof binding.expiresAt == "number" && Number.isFinite(binding.expiresAt);
|
|
2488
|
+
}
|
|
2489
|
+
function validPair(value) {
|
|
2490
|
+
if (!value || typeof value != "object" || Array.isArray(value) || Object.keys(value).sort().join(",") !== "context,credential,renewalCredential") return !1;
|
|
2491
|
+
let pair = value;
|
|
2492
|
+
return validProof(pair.credential) && validProof(pair.renewalCredential) && pair.credential !== pair.renewalCredential && validBinding(pair.context);
|
|
2470
2493
|
}
|
|
2471
2494
|
|
|
2472
2495
|
var unavailable2 = () => new InboxOperationError(503, "unavailable"), stale = () => new InboxOperationError(401, "authority_unavailable");
|
|
@@ -2474,13 +2497,13 @@ function hostFailure(error, context2) {
|
|
|
2474
2497
|
return !context2.isCurrent() || context2.expiresAt <= Date.now() || error instanceof InboxContextError && error.status === 401 ? stale() : error instanceof InboxContextError && error.status === 403 ? new InboxOperationError(403, "access_denied") : unavailable2();
|
|
2475
2498
|
}
|
|
2476
2499
|
async function send2(operation2, input) {
|
|
2477
|
-
let envelope = parseInboxRequest({ operation: operation2, input }), context2;
|
|
2500
|
+
let envelope = operation2 === "questions.list" ? void 0 : parseInboxRequest({ operation: operation2, input }), context2;
|
|
2478
2501
|
try {
|
|
2479
2502
|
context2 = await resolveAppContext();
|
|
2480
2503
|
} catch {
|
|
2481
2504
|
throw stale();
|
|
2482
2505
|
}
|
|
2483
|
-
if (context2.kind === "local") {
|
|
2506
|
+
if (envelope ??= parseInboxRequest({ operation: operation2, input }, context2.kind === "local" ? "local" : "hosted"), context2.kind === "local") {
|
|
2484
2507
|
if (!isAppQuestionOperation(operation2)) throw new InboxOperationError(403, "access_denied");
|
|
2485
2508
|
let check2 = () => {
|
|
2486
2509
|
if (!context2.isCurrent() || context2.signal.aborted || context2.expiresAt <= Date.now()) throw stale();
|
|
@@ -2617,13 +2640,17 @@ var inbox = {
|
|
|
2617
2640
|
addRecipient: async (input) => await send2("items.addRecipient", input)
|
|
2618
2641
|
},
|
|
2619
2642
|
questions: {
|
|
2620
|
-
async list() {
|
|
2621
|
-
let
|
|
2643
|
+
async list(input) {
|
|
2644
|
+
let supplied = input === void 0 ? void 0 : parseInboxRequest({ operation: "questions.list", input }, "hosted").input;
|
|
2645
|
+
if (supplied && Object.hasOwn(supplied, "beforeNumber")) throw new InboxOperationError(400, "invalid_input");
|
|
2646
|
+
let routines = supplied ? Object.freeze(supplied.routines) : void 0, questions = [], beforeNumber, initial;
|
|
2622
2647
|
try {
|
|
2623
2648
|
initial = await resolveAppContext();
|
|
2624
2649
|
} catch {
|
|
2625
2650
|
throw stale();
|
|
2626
2651
|
}
|
|
2652
|
+
if (initial.kind === "local" ? input !== void 0 : routines === void 0)
|
|
2653
|
+
throw new InboxOperationError(400, "invalid_input");
|
|
2627
2654
|
let host;
|
|
2628
2655
|
try {
|
|
2629
2656
|
host = initial.kind !== "local" && await resolveHostedInboxContext(initial);
|
|
@@ -2636,8 +2663,11 @@ var inbox = {
|
|
|
2636
2663
|
};
|
|
2637
2664
|
do {
|
|
2638
2665
|
check();
|
|
2639
|
-
let page = await send2("questions.list",
|
|
2640
|
-
|
|
2666
|
+
let page = await send2("questions.list", {
|
|
2667
|
+
...routines === void 0 ? {} : { routines },
|
|
2668
|
+
...beforeNumber === void 0 ? {} : { beforeNumber }
|
|
2669
|
+
});
|
|
2670
|
+
if (check(), page.questions.some((question2) => question2.item.href !== `https://${initial.workspace}.oddington.ai/inbox/${question2.item.id}` || routines !== void 0 && !routines.includes(question2.origin.routine)) || beforeNumber !== void 0 && (page.questions.some((question2) => question2.item.number >= beforeNumber) || page.nextBeforeNumber !== null && page.nextBeforeNumber >= beforeNumber)) throw unavailable2();
|
|
2641
2671
|
if (questions.push(...page.questions), page.nextBeforeNumber === null) return questions;
|
|
2642
2672
|
beforeNumber = page.nextBeforeNumber;
|
|
2643
2673
|
} while (!0);
|