@pitcher/js-api 1.27.3 → 1.29.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.
Files changed (41) hide show
  1. package/js-api.esm.js +325 -7
  2. package/js-api.esm.js.map +1 -1
  3. package/js-api.umd.min.js +11 -11
  4. package/js-api.umd.min.js.map +1 -1
  5. package/lib/apps/canvas-builder/components/ui/DynamicContent/dynamicContent.util.d.ts +36 -0
  6. package/lib/apps/canvas-builder/components/ui/DynamicContent/dynamicContent.util.spec.d.ts +1 -0
  7. package/lib/apps/canvas-builder/composables/useCanvas.d.ts +63 -0
  8. package/lib/apps/canvas-builder/composables/useCanvasBlocks.d.ts +14 -0
  9. package/lib/apps/canvas-builder/composables/useCanvasHistory.d.ts +14 -0
  10. package/lib/apps/canvas-builder/composables/useCanvasTheme.d.ts +18 -18
  11. package/lib/apps/canvas-builder/composables/usePopupApps.d.ts +28 -0
  12. package/lib/apps/canvas-builder/types/canvas.d.ts +7 -0
  13. package/lib/apps/canvas-builder/util/canvas.util.d.ts +19 -0
  14. package/lib/components/CFileViewer/pdf/CFileViewer.pdf.util.d.ts +17 -0
  15. package/lib/components/CFileViewer/pdf/CFileViewer.pdf.util.spec.d.ts +1 -0
  16. package/lib/components/CRichTextEditor/components/CTextTypeSelect.test.d.ts +1 -0
  17. package/lib/composables/useCourseOnlyApps.d.ts +11 -0
  18. package/lib/sdk/api/HighLevelApi.d.ts +13 -3
  19. package/lib/sdk/api/modules/admin/types.admin.d.ts +1 -0
  20. package/lib/sdk/api/modules/appsDb.d.ts +57 -0
  21. package/lib/sdk/api/modules/appsDb.spec.d.ts +1 -0
  22. package/lib/sdk/api/modules/index.d.ts +3 -1
  23. package/lib/sdk/api/modules/stt.d.ts +65 -0
  24. package/lib/sdk/api/modules/stt.spec.d.ts +1 -0
  25. package/lib/sdk/api/modules/tts.d.ts +41 -0
  26. package/lib/sdk/api/modules/tts.spec.d.ts +1 -0
  27. package/lib/sdk/interfaces.d.ts +26 -1
  28. package/lib/sdk/main.d.ts +39 -6
  29. package/lib/sdk/payload.types.d.ts +253 -0
  30. package/lib/types/app.d.ts +14 -0
  31. package/lib/types/canvases.d.ts +0 -3
  32. package/lib/types/launchDarkly.types.d.ts +1 -1
  33. package/lib/types/sfdc.d.ts +15 -0
  34. package/lib/util/soql.util.d.ts +36 -0
  35. package/package.json +1 -1
  36. package/types/openapi/index.d.ts +0 -1
  37. package/types/openapi/models/PatchedUserRequest.d.ts +1 -2
  38. package/types/openapi/models/User.d.ts +1 -2
  39. package/types/openapi/models/UserRequest.d.ts +1 -2
  40. package/lib/sdk/api/modules/languages.d.ts +0 -8
  41. package/types/openapi/models/LanguageEnum.d.ts +0 -12
package/js-api.esm.js CHANGED
@@ -2511,6 +2511,251 @@ async function piaSearchAnswer(payload) {
2511
2511
  }
2512
2512
  }
2513
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}`;
2521
+ return {
2522
+ isIos: (env?.mode ?? env?.pitcher?.mode) === "IOS",
2523
+ origin,
2524
+ accessToken: env?.pitcher?.access_token ?? "",
2525
+ instanceId: env?.pitcher?.instance?.id
2526
+ };
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
+ }
2758
+
2514
2759
  function open$2(payload = {}) {
2515
2760
  return this.API.request("open", payload);
2516
2761
  }
@@ -2528,10 +2773,6 @@ function isOffline() {
2528
2773
  return this.API.request("is_offline");
2529
2774
  }
2530
2775
 
2531
- function getLanguages() {
2532
- return this.API.request("get_languages");
2533
- }
2534
-
2535
2776
  function getCanvases$1(payload) {
2536
2777
  return this.API.request("get_canvases", payload);
2537
2778
  }
@@ -3515,10 +3756,73 @@ function assignCanvasTheme(payload) {
3515
3756
  return this.API.request("assign_canvas_theme", payload);
3516
3757
  }
3517
3758
 
3759
+ const STT_ERROR_CODES = [
3760
+ "STT_DISABLED",
3761
+ "STT_DEVICE_UNSUPPORTED",
3762
+ "STT_MODEL_NOT_READY",
3763
+ "STT_BUSY",
3764
+ "STT_MIC_PERMISSION_DENIED",
3765
+ "STT_NOT_RECORDING",
3766
+ "STT_CAPTURE_FAILED",
3767
+ "STT_ERROR"
3768
+ ];
3769
+ function asSttUnsupportedError(error) {
3770
+ const marker = "requestTypeDoesNotExists";
3771
+ const e = error;
3772
+ const isUnsupported = e?.error_code === marker || e?.errorCode === marker || e?.code === marker || typeof e?.reason === "string" && e.reason.includes(marker) || typeof e?.message === "string" && e.message.includes(marker);
3773
+ return isUnsupported ? new Error("STT_DEVICE_UNSUPPORTED: dictation is not supported by this app version") : error;
3774
+ }
3775
+ function sttStart(payload) {
3776
+ return this.API.request("stt.start", payload).catch((error) => {
3777
+ throw asSttUnsupportedError(error);
3778
+ });
3779
+ }
3780
+ function sttAvailability() {
3781
+ return this.API.request("stt.availability");
3782
+ }
3783
+ function sttWarmup() {
3784
+ return this.API.request("stt.warmup");
3785
+ }
3786
+ function sttStop(payload) {
3787
+ return this.API.request("stt.stop", payload).catch((error) => {
3788
+ throw asSttUnsupportedError(error);
3789
+ });
3790
+ }
3791
+ function sttErrorCode(error) {
3792
+ const e = error;
3793
+ const direct = e?.code ?? e?.error_code ?? e?.errorCode;
3794
+ if (typeof direct === "string" && STT_ERROR_CODES.includes(direct)) {
3795
+ return direct;
3796
+ }
3797
+ const text = typeof e?.reason === "string" ? e.reason : typeof e?.message === "string" ? e.message : "";
3798
+ return STT_ERROR_CODES.find((code) => text.includes(code));
3799
+ }
3800
+
3801
+ const TTS_ERROR_CODES = ["TTS_EMPTY_TEXT", "TTS_VOICE_UNAVAILABLE", "TTS_ERROR"];
3802
+ function ttsSpeak(payload) {
3803
+ return this.API.request("tts.speak", payload);
3804
+ }
3805
+ function ttsStop() {
3806
+ return this.API.request("tts.stop");
3807
+ }
3808
+ function ttsErrorCode(error) {
3809
+ const e = error;
3810
+ const direct = e?.code ?? e?.error_code ?? e?.errorCode;
3811
+ if (typeof direct === "string" && TTS_ERROR_CODES.includes(direct)) {
3812
+ return direct;
3813
+ }
3814
+ const text = typeof e?.reason === "string" ? e.reason : typeof e?.message === "string" ? e.message : "";
3815
+ return TTS_ERROR_CODES.find((code) => text.includes(code));
3816
+ }
3817
+
3518
3818
  const modules = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
3519
3819
  __proto__: null,
3520
3820
  aiComplete,
3521
3821
  aiGetCapabilities,
3822
+ appsDbDeleteEntry,
3823
+ appsDbGetEntries,
3824
+ appsDbPsql,
3825
+ appsDbUpsertEntry,
3522
3826
  assignCanvasTheme,
3523
3827
  close: close$2,
3524
3828
  createCanvas: createCanvas$1,
@@ -3556,7 +3860,6 @@ const modules = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
3556
3860
  getFolder,
3557
3861
  getFolders,
3558
3862
  getInstanceMetadataTemplates: getInstanceMetadataTemplates$1,
3559
- getLanguages,
3560
3863
  getSectionsByIds,
3561
3864
  getThemes,
3562
3865
  getUsers: getUsers$1,
@@ -3578,10 +3881,18 @@ const modules = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
3578
3881
  shareCanvas,
3579
3882
  showPeerSession,
3580
3883
  showSyncbox,
3884
+ sttAvailability,
3885
+ sttErrorCode,
3886
+ sttStart,
3887
+ sttStop,
3888
+ sttWarmup,
3581
3889
  submitUserFeedback,
3582
3890
  toast: toast$2,
3583
3891
  track,
3584
3892
  triggerNonFilesSync,
3893
+ ttsErrorCode,
3894
+ ttsSpeak,
3895
+ ttsStop,
3585
3896
  unassignCanvasTheme,
3586
3897
  updateCanvas: updateCanvas$2,
3587
3898
  updateCanvasIndicators,
@@ -3698,6 +4009,8 @@ var PitcherEventName = /* @__PURE__ */ ((PitcherEventName2) => {
3698
4009
  PitcherEventName2["SYNC_BADGE_VALUE_REPORTED"] = "sync_badge_value_reported";
3699
4010
  PitcherEventName2["SYNC_BOX_RESOLVE_ERROR_TAPPED"] = "sync_box_resolve_error_tapped";
3700
4011
  PitcherEventName2["PEER_CONNECTIVITY_EVENT"] = "peer_connectivity_event";
4012
+ PitcherEventName2["STT_PARTIAL"] = "stt.partial";
4013
+ PitcherEventName2["STT_INTERRUPTED"] = "stt.interrupted";
3701
4014
  return PitcherEventName2;
3702
4015
  })(PitcherEventName || {});
3703
4016
  var PitcherBroadcastedEventName = /* @__PURE__ */ ((PitcherBroadcastedEventName2) => {
@@ -4148,7 +4461,10 @@ function getTopPitcherWindow(current = window) {
4148
4461
  }
4149
4462
 
4150
4463
  const TRUNCATE_LENGTH_TRIGGER = 10;
4464
+ const RAW_RESPONSE_METHODS = ["appsdb_get_entries", "appsdb_upsert_entry", "appsdb_delete_entry"];
4151
4465
  const RAW_PAYLOAD_METHODS = [
4466
+ // AppsDB `data` is an opaque app-owned blob — snake-casing its keys would corrupt stored payloads
4467
+ "appsdb_upsert_entry",
4152
4468
  "crm_create",
4153
4469
  "crm_upsert",
4154
4470
  "crm_describe",
@@ -4210,7 +4526,7 @@ class LowLevelApi extends EventEmitter {
4210
4526
  this.options.logLevel === "debug" && console.log(`Callback ${id} response:`, res);
4211
4527
  if (res.response.status === "ok") {
4212
4528
  resolve(
4213
- this.options.casing === "camel" ? camelCaseKeys(res.response.body) : res.response.body
4529
+ this.options.casing === "camel" && !RAW_RESPONSE_METHODS.includes(type) ? camelCaseKeys(res.response.body) : res.response.body
4214
4530
  );
4215
4531
  if (type === "get_env") updateOnlineHandlersEnv(res.response.body);
4216
4532
  } else if (res.response.status === "error") {
@@ -4221,7 +4537,9 @@ class LowLevelApi extends EventEmitter {
4221
4537
  js_api_response: JSON.stringify(truncateObject(res, 3, TRUNCATE_LENGTH_TRIGGER))
4222
4538
  });
4223
4539
  }
4224
- reject(this.options.casing === "camel" ? camelCaseKeys(res.response.body) : res.response.body);
4540
+ reject(
4541
+ this.options.casing === "camel" && !RAW_RESPONSE_METHODS.includes(type) ? camelCaseKeys(res.response.body) : res.response.body
4542
+ );
4225
4543
  } else {
4226
4544
  throw new Error("unsupported response status");
4227
4545
  }