@pitcher/js-api 1.27.2 → 1.28.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/js-api.esm.js +266 -13
- package/js-api.esm.js.map +1 -1
- package/js-api.umd.min.js +11 -11
- package/js-api.umd.min.js.map +1 -1
- package/lib/apps/canvas-builder/components/ui/DynamicContent/dynamicContent.util.d.ts +36 -0
- package/lib/apps/canvas-builder/components/ui/DynamicContent/dynamicContent.util.spec.d.ts +1 -0
- package/lib/apps/canvas-builder/composables/useCanvas.d.ts +63 -0
- package/lib/apps/canvas-builder/composables/useCanvasBlocks.d.ts +14 -0
- package/lib/apps/canvas-builder/composables/useCanvasHistory.d.ts +14 -0
- package/lib/apps/canvas-builder/composables/usePopupApps.d.ts +28 -0
- package/lib/apps/canvas-builder/types/canvas.d.ts +7 -0
- package/lib/apps/canvas-builder/util/canvas.util.d.ts +11 -0
- package/lib/components/CFileViewer/pdf/CFileViewer.pdf.util.d.ts +17 -0
- package/lib/components/CFileViewer/pdf/CFileViewer.pdf.util.spec.d.ts +1 -0
- package/lib/components/CRichTextEditor/components/CTextTypeSelect.test.d.ts +1 -0
- package/lib/composables/useCourseOnlyApps.d.ts +11 -0
- package/lib/sdk/api/HighLevelApi.d.ts +4 -0
- package/lib/sdk/api/modules/appsDb.d.ts +57 -0
- package/lib/sdk/api/modules/appsDb.spec.d.ts +1 -0
- package/lib/sdk/api/modules/index.d.ts +1 -0
- package/lib/sdk/main.d.ts +12 -0
- package/lib/sdk/payload.types.d.ts +73 -0
- package/lib/types/app.d.ts +14 -0
- package/package.json +1 -1
package/js-api.esm.js
CHANGED
|
@@ -2476,9 +2476,8 @@ async function piaSearchAnswer(payload) {
|
|
|
2476
2476
|
}
|
|
2477
2477
|
const controller = new AbortController();
|
|
2478
2478
|
const timer = setTimeout(() => controller.abort(), ONLINE_TIMEOUT_MS);
|
|
2479
|
-
let response;
|
|
2480
2479
|
try {
|
|
2481
|
-
response = await fetch(`${apiOriginFromEnv(env)}${PIA_SEARCH_ROUTE}`, {
|
|
2480
|
+
const response = await fetch(`${apiOriginFromEnv(env)}${PIA_SEARCH_ROUTE}`, {
|
|
2482
2481
|
method: "POST",
|
|
2483
2482
|
signal: controller.signal,
|
|
2484
2483
|
headers: {
|
|
@@ -2491,6 +2490,17 @@ async function piaSearchAnswer(payload) {
|
|
|
2491
2490
|
...Array.isArray(payload.file_ids) && payload.file_ids.length > 0 && { file_ids: payload.file_ids }
|
|
2492
2491
|
})
|
|
2493
2492
|
});
|
|
2493
|
+
if (!response.ok) {
|
|
2494
|
+
throw new Error(`pia_search ${response.status}`);
|
|
2495
|
+
}
|
|
2496
|
+
const data = await response.json();
|
|
2497
|
+
const output = data?.pia_search ?? {};
|
|
2498
|
+
return {
|
|
2499
|
+
answer: typeof output.answer === "string" ? output.answer : "",
|
|
2500
|
+
cited_file_ids: Array.isArray(output.cited_file_ids) ? output.cited_file_ids : [],
|
|
2501
|
+
...output.confidence && { confidence: output.confidence },
|
|
2502
|
+
source: "online"
|
|
2503
|
+
};
|
|
2494
2504
|
} catch (error) {
|
|
2495
2505
|
if (error?.name === "AbortError") {
|
|
2496
2506
|
throw new Error(`pia_search online request timed out after ${ONLINE_TIMEOUT_MS}ms`);
|
|
@@ -2499,18 +2509,252 @@ async function piaSearchAnswer(payload) {
|
|
|
2499
2509
|
} finally {
|
|
2500
2510
|
clearTimeout(timer);
|
|
2501
2511
|
}
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
const
|
|
2512
|
+
}
|
|
2513
|
+
|
|
2514
|
+
const APPS_DB_BASE_PATH = "/core/api/protected/appsdb";
|
|
2515
|
+
async function fetchAppsDbContext() {
|
|
2516
|
+
const env = await highLevelApi.API.request("get_env");
|
|
2517
|
+
const claimsDomain = env?.pitcher?.token_claims?.["https://pitcher.com/claims/urls"]?.custom_domain;
|
|
2518
|
+
const metadataDomain = env?.pitcher?.organization?.metadata?.custom_domain;
|
|
2519
|
+
let origin = claimsDomain || metadataDomain || "";
|
|
2520
|
+
if (origin && !origin.startsWith("http")) origin = `https://${origin}`;
|
|
2507
2521
|
return {
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2522
|
+
isIos: (env?.mode ?? env?.pitcher?.mode) === "IOS",
|
|
2523
|
+
origin,
|
|
2524
|
+
accessToken: env?.pitcher?.access_token ?? "",
|
|
2525
|
+
instanceId: env?.pitcher?.instance?.id
|
|
2512
2526
|
};
|
|
2513
2527
|
}
|
|
2528
|
+
const CONTEXT_TTL_MS = 6e4;
|
|
2529
|
+
let contextCache = null;
|
|
2530
|
+
function getAppsDbContext() {
|
|
2531
|
+
if (contextCache && Date.now() - contextCache.fetchedAt < CONTEXT_TTL_MS) {
|
|
2532
|
+
return contextCache.promise;
|
|
2533
|
+
}
|
|
2534
|
+
const promise = fetchAppsDbContext();
|
|
2535
|
+
contextCache = { promise, fetchedAt: Date.now() };
|
|
2536
|
+
promise.catch(() => {
|
|
2537
|
+
if (contextCache?.promise === promise) contextCache = null;
|
|
2538
|
+
});
|
|
2539
|
+
return promise;
|
|
2540
|
+
}
|
|
2541
|
+
async function appsDbRestFetch(ctx, path, init) {
|
|
2542
|
+
const base = ctx.origin || (typeof window !== "undefined" ? window.location.origin : "");
|
|
2543
|
+
const url = new URL(`${base}${APPS_DB_BASE_PATH}${path}`);
|
|
2544
|
+
Object.entries(init.params ?? {}).forEach(([key, value]) => {
|
|
2545
|
+
if (value !== void 0 && value !== null) url.searchParams.set(key, String(value));
|
|
2546
|
+
});
|
|
2547
|
+
const response = await fetch(url.toString(), {
|
|
2548
|
+
method: init.method,
|
|
2549
|
+
credentials: "include",
|
|
2550
|
+
headers: {
|
|
2551
|
+
"Content-Type": "application/json",
|
|
2552
|
+
Authorization: `Bearer ${ctx.accessToken}`,
|
|
2553
|
+
...ctx.instanceId ? { "x-instance-id": ctx.instanceId } : {}
|
|
2554
|
+
},
|
|
2555
|
+
...init.body !== void 0 ? { body: JSON.stringify(init.body) } : {}
|
|
2556
|
+
});
|
|
2557
|
+
if (!response.ok) {
|
|
2558
|
+
let errorBody;
|
|
2559
|
+
try {
|
|
2560
|
+
errorBody = await response.json();
|
|
2561
|
+
} catch {
|
|
2562
|
+
errorBody = void 0;
|
|
2563
|
+
}
|
|
2564
|
+
const error = new Error(
|
|
2565
|
+
`AppsDB request failed: ${response.status}${errorBody?.error ? ` — ${errorBody.error}` : ""}`
|
|
2566
|
+
);
|
|
2567
|
+
error.status = response.status;
|
|
2568
|
+
throw error;
|
|
2569
|
+
}
|
|
2570
|
+
if (response.status === 204 || response.status === 205) return void 0;
|
|
2571
|
+
const text = await response.text();
|
|
2572
|
+
return text ? JSON.parse(text) : void 0;
|
|
2573
|
+
}
|
|
2574
|
+
function hasErrorCode(error, code) {
|
|
2575
|
+
return error?.error_code === code || error?.errorCode === code || error?.code === code || typeof error?.reason === "string" && error.reason.includes(code) || typeof error?.message === "string" && error.message.includes(code);
|
|
2576
|
+
}
|
|
2577
|
+
function isTypeNotSyncedError(error) {
|
|
2578
|
+
return hasErrorCode(error, "type_not_synced");
|
|
2579
|
+
}
|
|
2580
|
+
function isBridgeUnsupportedError(error) {
|
|
2581
|
+
return hasErrorCode(error, "requestTypeDoesNotExists");
|
|
2582
|
+
}
|
|
2583
|
+
function parsePsqlLiteral(raw) {
|
|
2584
|
+
if (/^'.*'$/.test(raw)) return raw.slice(1, -1).replace(/''/g, "'");
|
|
2585
|
+
if (/^-?\d+(\.\d+)?$/.test(raw)) return Number(raw);
|
|
2586
|
+
if (/^true$/i.test(raw)) return true;
|
|
2587
|
+
if (/^false$/i.test(raw)) return false;
|
|
2588
|
+
if (/^null$/i.test(raw)) return null;
|
|
2589
|
+
return void 0;
|
|
2590
|
+
}
|
|
2591
|
+
function stripQuotedLiterals(where) {
|
|
2592
|
+
return where.replace(/'(?:[^']|'')*'/g, "''");
|
|
2593
|
+
}
|
|
2594
|
+
function splitTopLevelAnd(where) {
|
|
2595
|
+
const parts = [];
|
|
2596
|
+
let current = "";
|
|
2597
|
+
let inQuote = false;
|
|
2598
|
+
let i = 0;
|
|
2599
|
+
while (i < where.length) {
|
|
2600
|
+
const char = where[i];
|
|
2601
|
+
if (char === "'") {
|
|
2602
|
+
if (inQuote && where[i + 1] === "'") {
|
|
2603
|
+
current += "''";
|
|
2604
|
+
i += 2;
|
|
2605
|
+
continue;
|
|
2606
|
+
}
|
|
2607
|
+
inQuote = !inQuote;
|
|
2608
|
+
current += char;
|
|
2609
|
+
i += 1;
|
|
2610
|
+
continue;
|
|
2611
|
+
}
|
|
2612
|
+
if (!inQuote) {
|
|
2613
|
+
const separator = where.slice(i).match(/^ AND /i);
|
|
2614
|
+
if (separator) {
|
|
2615
|
+
parts.push(current);
|
|
2616
|
+
current = "";
|
|
2617
|
+
i += separator[0].length;
|
|
2618
|
+
continue;
|
|
2619
|
+
}
|
|
2620
|
+
}
|
|
2621
|
+
current += char;
|
|
2622
|
+
i += 1;
|
|
2623
|
+
}
|
|
2624
|
+
if (inQuote) return null;
|
|
2625
|
+
parts.push(current);
|
|
2626
|
+
return parts;
|
|
2627
|
+
}
|
|
2628
|
+
function parseSimplePsql(query) {
|
|
2629
|
+
const normalized = query.replace(/\s+/g, " ").trim();
|
|
2630
|
+
const match = normalized.match(/^SELECT \* FROM ([A-Za-z0-9_]+)( WHERE (.+))?$/i);
|
|
2631
|
+
if (!match) return null;
|
|
2632
|
+
const type = match[1];
|
|
2633
|
+
const where = match[3];
|
|
2634
|
+
if (!where) return { type, conditions: [] };
|
|
2635
|
+
if (/\bOR\b|\bNOT\b|\bLIKE\b|\bIN\b|[<>]|!=/i.test(stripQuotedLiterals(where))) return null;
|
|
2636
|
+
const rawConditions = splitTopLevelAnd(where);
|
|
2637
|
+
if (!rawConditions) return null;
|
|
2638
|
+
const conditions = [];
|
|
2639
|
+
for (const part of rawConditions) {
|
|
2640
|
+
const condition = part.trim().match(/^([A-Za-z0-9_.]+) ?= ?(.+)$/);
|
|
2641
|
+
if (!condition) return null;
|
|
2642
|
+
const value = parsePsqlLiteral(condition[2].trim());
|
|
2643
|
+
if (value === void 0) return null;
|
|
2644
|
+
conditions.push({ path: condition[1], value });
|
|
2645
|
+
}
|
|
2646
|
+
return { type, conditions };
|
|
2647
|
+
}
|
|
2648
|
+
function entryMatches(entry, conditions) {
|
|
2649
|
+
return conditions.every(({ path, value }) => {
|
|
2650
|
+
const actual = path.split(".").reduce((acc, key) => acc == null ? void 0 : acc[key], entry);
|
|
2651
|
+
if (value === null) return actual === null || actual === void 0;
|
|
2652
|
+
if (actual === null || actual === void 0) return false;
|
|
2653
|
+
return String(actual) === String(value);
|
|
2654
|
+
});
|
|
2655
|
+
}
|
|
2656
|
+
async function appsDbGetEntries(payload) {
|
|
2657
|
+
if (!payload?.type || typeof payload.type !== "string") {
|
|
2658
|
+
return Promise.reject(new Error("type is required and must be a non-empty string"));
|
|
2659
|
+
}
|
|
2660
|
+
const ctx = await getAppsDbContext();
|
|
2661
|
+
if (ctx.isIos) {
|
|
2662
|
+
try {
|
|
2663
|
+
return await highLevelApi.API.request("appsdb_get_entries", payload);
|
|
2664
|
+
} catch (error) {
|
|
2665
|
+
if (!isTypeNotSyncedError(error) && !isBridgeUnsupportedError(error)) throw error;
|
|
2666
|
+
}
|
|
2667
|
+
}
|
|
2668
|
+
return appsDbRestFetch(ctx, "", { method: "GET", params: payload });
|
|
2669
|
+
}
|
|
2670
|
+
async function appsDbUpsertEntry(payload) {
|
|
2671
|
+
if (!payload?.data || typeof payload.data !== "object") {
|
|
2672
|
+
return Promise.reject(new Error("data is required and must be an object"));
|
|
2673
|
+
}
|
|
2674
|
+
if (!payload.id && !payload.type) {
|
|
2675
|
+
return Promise.reject(new Error("type is required when creating an entry (no id provided)"));
|
|
2676
|
+
}
|
|
2677
|
+
const ctx = await getAppsDbContext();
|
|
2678
|
+
if (ctx.isIos) {
|
|
2679
|
+
try {
|
|
2680
|
+
return await highLevelApi.API.request("appsdb_upsert_entry", payload);
|
|
2681
|
+
} catch (error) {
|
|
2682
|
+
if (!isBridgeUnsupportedError(error)) throw error;
|
|
2683
|
+
}
|
|
2684
|
+
}
|
|
2685
|
+
if (payload.id) {
|
|
2686
|
+
return appsDbRestFetch(ctx, `/${encodeURIComponent(payload.id)}`, { method: "PUT", body: { data: payload.data } });
|
|
2687
|
+
}
|
|
2688
|
+
return appsDbRestFetch(ctx, "", { method: "POST", body: payload });
|
|
2689
|
+
}
|
|
2690
|
+
async function appsDbDeleteEntry(payload) {
|
|
2691
|
+
if (!payload?.id || typeof payload.id !== "string") {
|
|
2692
|
+
return Promise.reject(new Error("id is required and must be a non-empty string"));
|
|
2693
|
+
}
|
|
2694
|
+
const ctx = await getAppsDbContext();
|
|
2695
|
+
if (ctx.isIos) {
|
|
2696
|
+
try {
|
|
2697
|
+
return await highLevelApi.API.request("appsdb_delete_entry", payload);
|
|
2698
|
+
} catch (error) {
|
|
2699
|
+
if (!isBridgeUnsupportedError(error)) throw error;
|
|
2700
|
+
}
|
|
2701
|
+
}
|
|
2702
|
+
await appsDbRestFetch(ctx, `/${encodeURIComponent(payload.id)}`, { method: "DELETE" });
|
|
2703
|
+
}
|
|
2704
|
+
async function isDeviceOffline() {
|
|
2705
|
+
try {
|
|
2706
|
+
return Boolean(await highLevelApi.API.request("is_offline"));
|
|
2707
|
+
} catch {
|
|
2708
|
+
return false;
|
|
2709
|
+
}
|
|
2710
|
+
}
|
|
2711
|
+
const MIRROR_PAGE_LIMIT = 1e3;
|
|
2712
|
+
async function psqlFromMirror(parsed) {
|
|
2713
|
+
try {
|
|
2714
|
+
const entries = [];
|
|
2715
|
+
let hasMore = true;
|
|
2716
|
+
while (hasMore) {
|
|
2717
|
+
const result = await highLevelApi.API.request("appsdb_get_entries", {
|
|
2718
|
+
type: parsed.type,
|
|
2719
|
+
limit: MIRROR_PAGE_LIMIT,
|
|
2720
|
+
offset: entries.length
|
|
2721
|
+
});
|
|
2722
|
+
const page = result?.entries ?? [];
|
|
2723
|
+
entries.push(...page);
|
|
2724
|
+
hasMore = Boolean(result?.hasMore) && page.length > 0;
|
|
2725
|
+
}
|
|
2726
|
+
const matched = entries.filter((entry) => entryMatches(entry, parsed.conditions));
|
|
2727
|
+
return { entries: matched, totalCount: matched.length, hasMore: false };
|
|
2728
|
+
} catch (error) {
|
|
2729
|
+
if (isTypeNotSyncedError(error) || isBridgeUnsupportedError(error)) return null;
|
|
2730
|
+
throw error;
|
|
2731
|
+
}
|
|
2732
|
+
}
|
|
2733
|
+
async function appsDbPsql(payload) {
|
|
2734
|
+
if (!payload?.query || typeof payload.query !== "string") {
|
|
2735
|
+
return Promise.reject(new Error("query is required and must be a non-empty string"));
|
|
2736
|
+
}
|
|
2737
|
+
const ctx = await getAppsDbContext();
|
|
2738
|
+
if (ctx.isIos) {
|
|
2739
|
+
const parsed = parseSimplePsql(payload.query);
|
|
2740
|
+
let mirrorUnavailable = false;
|
|
2741
|
+
if (parsed && await isDeviceOffline()) {
|
|
2742
|
+
const local = await psqlFromMirror(parsed);
|
|
2743
|
+
if (local) return local;
|
|
2744
|
+
mirrorUnavailable = true;
|
|
2745
|
+
}
|
|
2746
|
+
try {
|
|
2747
|
+
return await appsDbRestFetch(ctx, "/psql", { method: "POST", body: payload });
|
|
2748
|
+
} catch (error) {
|
|
2749
|
+
if (parsed && !mirrorUnavailable && error instanceof TypeError) {
|
|
2750
|
+
const local = await psqlFromMirror(parsed);
|
|
2751
|
+
if (local) return local;
|
|
2752
|
+
}
|
|
2753
|
+
throw error;
|
|
2754
|
+
}
|
|
2755
|
+
}
|
|
2756
|
+
return appsDbRestFetch(ctx, "/psql", { method: "POST", body: payload });
|
|
2757
|
+
}
|
|
2514
2758
|
|
|
2515
2759
|
function open$2(payload = {}) {
|
|
2516
2760
|
return this.API.request("open", payload);
|
|
@@ -3520,6 +3764,10 @@ const modules = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
|
|
|
3520
3764
|
__proto__: null,
|
|
3521
3765
|
aiComplete,
|
|
3522
3766
|
aiGetCapabilities,
|
|
3767
|
+
appsDbDeleteEntry,
|
|
3768
|
+
appsDbGetEntries,
|
|
3769
|
+
appsDbPsql,
|
|
3770
|
+
appsDbUpsertEntry,
|
|
3523
3771
|
assignCanvasTheme,
|
|
3524
3772
|
close: close$2,
|
|
3525
3773
|
createCanvas: createCanvas$1,
|
|
@@ -4149,7 +4397,10 @@ function getTopPitcherWindow(current = window) {
|
|
|
4149
4397
|
}
|
|
4150
4398
|
|
|
4151
4399
|
const TRUNCATE_LENGTH_TRIGGER = 10;
|
|
4400
|
+
const RAW_RESPONSE_METHODS = ["appsdb_get_entries", "appsdb_upsert_entry", "appsdb_delete_entry"];
|
|
4152
4401
|
const RAW_PAYLOAD_METHODS = [
|
|
4402
|
+
// AppsDB `data` is an opaque app-owned blob — snake-casing its keys would corrupt stored payloads
|
|
4403
|
+
"appsdb_upsert_entry",
|
|
4153
4404
|
"crm_create",
|
|
4154
4405
|
"crm_upsert",
|
|
4155
4406
|
"crm_describe",
|
|
@@ -4211,7 +4462,7 @@ class LowLevelApi extends EventEmitter {
|
|
|
4211
4462
|
this.options.logLevel === "debug" && console.log(`Callback ${id} response:`, res);
|
|
4212
4463
|
if (res.response.status === "ok") {
|
|
4213
4464
|
resolve(
|
|
4214
|
-
this.options.casing === "camel" ? camelCaseKeys(res.response.body) : res.response.body
|
|
4465
|
+
this.options.casing === "camel" && !RAW_RESPONSE_METHODS.includes(type) ? camelCaseKeys(res.response.body) : res.response.body
|
|
4215
4466
|
);
|
|
4216
4467
|
if (type === "get_env") updateOnlineHandlersEnv(res.response.body);
|
|
4217
4468
|
} else if (res.response.status === "error") {
|
|
@@ -4222,7 +4473,9 @@ class LowLevelApi extends EventEmitter {
|
|
|
4222
4473
|
js_api_response: JSON.stringify(truncateObject(res, 3, TRUNCATE_LENGTH_TRIGGER))
|
|
4223
4474
|
});
|
|
4224
4475
|
}
|
|
4225
|
-
reject(
|
|
4476
|
+
reject(
|
|
4477
|
+
this.options.casing === "camel" && !RAW_RESPONSE_METHODS.includes(type) ? camelCaseKeys(res.response.body) : res.response.body
|
|
4478
|
+
);
|
|
4226
4479
|
} else {
|
|
4227
4480
|
throw new Error("unsupported response status");
|
|
4228
4481
|
}
|