@oxy-hq/sdk 2.1.0 → 2.3.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/dist/email.cjs +14 -0
- package/dist/email.cjs.map +1 -0
- package/dist/email.d.cts +8 -0
- package/dist/email.d.cts.map +1 -0
- package/dist/email.d.mts +8 -0
- package/dist/email.d.mts.map +1 -0
- package/dist/email.mjs +13 -0
- package/dist/email.mjs.map +1 -0
- package/dist/index.cjs +132 -75
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +176 -25
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +176 -25
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +132 -76
- package/dist/index.mjs.map +1 -1
- package/package.json +30 -8
package/dist/email.cjs
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// @oxy/sdk - TypeScript SDK for Oxy data platform
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
3
|
+
let preact = require("preact");
|
|
4
|
+
let preact_render_to_string = require("preact-render-to-string");
|
|
5
|
+
|
|
6
|
+
//#region src/email.ts
|
|
7
|
+
/** Render an email template component to an HTML string. */
|
|
8
|
+
function render(Component, props) {
|
|
9
|
+
return (0, preact_render_to_string.render)((0, preact.h)(Component, props));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
//#endregion
|
|
13
|
+
exports.render = render;
|
|
14
|
+
//# sourceMappingURL=email.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email.cjs","names":[],"sources":["../src/email.ts"],"sourcesContent":["// `@oxy-hq/sdk/email` — render an email template to an HTML string for use in\n// an Oxy Function's `ctx.email.send`.\n//\n// You write email templates as plain JSX components (point JSX at preact with\n// `jsxImportSource: \"preact\"` in the template's tsconfig — see the examples),\n// then:\n//\n// ```ts\n// import { render } from \"@oxy-hq/sdk/email\";\n// import { Welcome } from \"../emails/Welcome\";\n// await ctx.email.send({ to, subject, html: render(Welcome, { name }) });\n// ```\n//\n// Rendering uses preact-render-to-string — pure JS, no react-dom/server, no node\n// builtins, no Web Streams — so it bundles under esbuild `--platform=neutral`\n// and runs inside the Oxy Functions isolate (React Email / react-dom cannot).\n// Kept in a separate subpath entry so it never bloats the main SDK bundle.\n\nimport { type ComponentType, h } from \"preact\";\nimport { render as prerender } from \"preact-render-to-string\";\n\n/** Render an email template component to an HTML string. */\nexport function render<P extends Record<string, unknown>>(\n Component: ComponentType<P>,\n props: P\n): string {\n return prerender(h(Component, props));\n}\n"],"mappings":";;;;;;;AAsBA,SAAgB,OACd,WACA,OACQ;CACR,yDAAmB,WAAW,KAAK,CAAC;AACtC"}
|
package/dist/email.d.cts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
|
|
2
|
+
import { ComponentType } from "preact";
|
|
3
|
+
//#region src/email.d.ts
|
|
4
|
+
/** Render an email template component to an HTML string. */
|
|
5
|
+
declare function render<P extends Record<string, unknown>>(Component: ComponentType<P>, props: P): string;
|
|
6
|
+
//#endregion
|
|
7
|
+
export { render };
|
|
8
|
+
//# sourceMappingURL=email.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email.d.cts","names":[],"sources":["../src/email.ts"],"mappings":";;;;iBAsBgB,OAAO,UAAU,yBAC/B,WAAW,cAAc,IACzB,OAAO"}
|
package/dist/email.d.mts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
|
|
2
|
+
import { ComponentType } from "preact";
|
|
3
|
+
//#region src/email.d.ts
|
|
4
|
+
/** Render an email template component to an HTML string. */
|
|
5
|
+
declare function render<P extends Record<string, unknown>>(Component: ComponentType<P>, props: P): string;
|
|
6
|
+
//#endregion
|
|
7
|
+
export { render };
|
|
8
|
+
//# sourceMappingURL=email.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email.d.mts","names":[],"sources":["../src/email.ts"],"mappings":";;;;iBAsBgB,OAAO,UAAU,yBAC/B,WAAW,cAAc,IACzB,OAAO"}
|
package/dist/email.mjs
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// @oxy/sdk - TypeScript SDK for Oxy data platform
|
|
2
|
+
import { h } from "preact";
|
|
3
|
+
import { render as render$1 } from "preact-render-to-string";
|
|
4
|
+
|
|
5
|
+
//#region src/email.ts
|
|
6
|
+
/** Render an email template component to an HTML string. */
|
|
7
|
+
function render(Component, props) {
|
|
8
|
+
return render$1(h(Component, props));
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
//#endregion
|
|
12
|
+
export { render };
|
|
13
|
+
//# sourceMappingURL=email.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email.mjs","names":["prerender"],"sources":["../src/email.ts"],"sourcesContent":["// `@oxy-hq/sdk/email` — render an email template to an HTML string for use in\n// an Oxy Function's `ctx.email.send`.\n//\n// You write email templates as plain JSX components (point JSX at preact with\n// `jsxImportSource: \"preact\"` in the template's tsconfig — see the examples),\n// then:\n//\n// ```ts\n// import { render } from \"@oxy-hq/sdk/email\";\n// import { Welcome } from \"../emails/Welcome\";\n// await ctx.email.send({ to, subject, html: render(Welcome, { name }) });\n// ```\n//\n// Rendering uses preact-render-to-string — pure JS, no react-dom/server, no node\n// builtins, no Web Streams — so it bundles under esbuild `--platform=neutral`\n// and runs inside the Oxy Functions isolate (React Email / react-dom cannot).\n// Kept in a separate subpath entry so it never bloats the main SDK bundle.\n\nimport { type ComponentType, h } from \"preact\";\nimport { render as prerender } from \"preact-render-to-string\";\n\n/** Render an email template component to an HTML string. */\nexport function render<P extends Record<string, unknown>>(\n Component: ComponentType<P>,\n props: P\n): string {\n return prerender(h(Component, props));\n}\n"],"mappings":";;;;;;AAsBA,SAAgB,OACd,WACA,OACQ;CACR,OAAOA,SAAU,EAAE,WAAW,KAAK,CAAC;AACtC"}
|
package/dist/index.cjs
CHANGED
|
@@ -176,6 +176,52 @@ async function getCustomerAppDebug(resolved) {
|
|
|
176
176
|
|
|
177
177
|
//#endregion
|
|
178
178
|
//#region src/customer-app/errors.ts
|
|
179
|
+
/**
|
|
180
|
+
* Error thrown by all customer-app hooks when an API call returns a
|
|
181
|
+
* non-2xx response. Carries the structured `code` + `hint` the server
|
|
182
|
+
* emits so bundle UIs can render an actionable message instead of
|
|
183
|
+
* "404: { ...json... }".
|
|
184
|
+
*/
|
|
185
|
+
var OxyApiError = class extends Error {
|
|
186
|
+
constructor(opts) {
|
|
187
|
+
const base = opts.message || `HTTP ${opts.status}`;
|
|
188
|
+
const code = opts.code ? ` [${opts.code}]` : "";
|
|
189
|
+
const hint = opts.hint ? `\n\n${opts.hint}` : "";
|
|
190
|
+
super(`${base}${code}${hint}`);
|
|
191
|
+
this.name = "OxyApiError";
|
|
192
|
+
this.status = opts.status;
|
|
193
|
+
this.code = opts.code ?? null;
|
|
194
|
+
this.hint = opts.hint ?? null;
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
/**
|
|
198
|
+
* Read a non-2xx response from oxy and return an `OxyApiError`.
|
|
199
|
+
* Parses the JSON envelope when present; falls back to raw text
|
|
200
|
+
* (truncated to 240 chars so a runaway HTML error page doesn't
|
|
201
|
+
* dominate the bundle UI).
|
|
202
|
+
*/
|
|
203
|
+
async function apiErrorFromResponse(resp) {
|
|
204
|
+
let body;
|
|
205
|
+
let raw = "";
|
|
206
|
+
try {
|
|
207
|
+
raw = await resp.text();
|
|
208
|
+
body = raw ? JSON.parse(raw) : void 0;
|
|
209
|
+
} catch {}
|
|
210
|
+
if (body && typeof body === "object") {
|
|
211
|
+
const b = body;
|
|
212
|
+
return new OxyApiError({
|
|
213
|
+
status: resp.status,
|
|
214
|
+
message: typeof b.message === "string" ? b.message : `HTTP ${resp.status}`,
|
|
215
|
+
code: typeof b.code === "string" ? b.code : null,
|
|
216
|
+
hint: typeof b.hint === "string" ? b.hint : null
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
const snippet = raw.length > 240 ? `${raw.slice(0, 237)}…` : raw;
|
|
220
|
+
return new OxyApiError({
|
|
221
|
+
status: resp.status,
|
|
222
|
+
message: snippet || `HTTP ${resp.status}`
|
|
223
|
+
});
|
|
224
|
+
}
|
|
179
225
|
const ARCH_DOC = "internal-docs/customer-apps.md";
|
|
180
226
|
/** Interpret a thrown error as a structured report for UI display. */
|
|
181
227
|
function interpretCustomerAppError(err) {
|
|
@@ -422,6 +468,24 @@ function validateFunctions(raw) {
|
|
|
422
468
|
fn.cache = { ttlSeconds: ttl };
|
|
423
469
|
}
|
|
424
470
|
}
|
|
471
|
+
if (value.retries !== void 0) {
|
|
472
|
+
const r = value.retries;
|
|
473
|
+
if (!isRecord(r)) throw new Error(`oxy-app.json: function "${fnName}" \`retries\` must be an object`);
|
|
474
|
+
const retries = {};
|
|
475
|
+
for (const key of [
|
|
476
|
+
"maxAttempts",
|
|
477
|
+
"minTimeoutMs",
|
|
478
|
+
"maxTimeoutMs"
|
|
479
|
+
]) {
|
|
480
|
+
const n = r[key];
|
|
481
|
+
if (n !== void 0) {
|
|
482
|
+
if (typeof n !== "number" || !Number.isInteger(n) || n < 1) throw new Error(`oxy-app.json: function "${fnName}" \`retries.${key}\` must be a positive integer`);
|
|
483
|
+
retries[key] = n;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
fn.retries = retries;
|
|
487
|
+
}
|
|
488
|
+
if (value.inputExample !== void 0) fn.inputExample = value.inputExample;
|
|
425
489
|
const hasSchedule = fn.schedule !== void 0;
|
|
426
490
|
const hasAirway = fn.airwayStep !== void 0;
|
|
427
491
|
if (!(fn.route ?? !(hasSchedule || hasAirway)) && !hasSchedule && !hasAirway) throw new Error(`oxy-app.json: function "${fnName}" must enable at least one of route/schedule/airwayStep`);
|
|
@@ -435,7 +499,7 @@ function isRecord(v) {
|
|
|
435
499
|
|
|
436
500
|
//#endregion
|
|
437
501
|
//#region src/customer-app/function-invoke.ts
|
|
438
|
-
const inflight = /* @__PURE__ */ new Map();
|
|
502
|
+
const inflight$1 = /* @__PURE__ */ new Map();
|
|
439
503
|
/**
|
|
440
504
|
* Dedup key for an invocation: function name + its (stable-serialized) body,
|
|
441
505
|
* joined by a newline. Function names are `[a-z][a-z0-9-]*` (no newline), so
|
|
@@ -450,12 +514,12 @@ function functionInvokeKey(name, body) {
|
|
|
450
514
|
* this dedups concurrency only, it does NOT memoize the result.
|
|
451
515
|
*/
|
|
452
516
|
function sharedFunctionInvoke(key, run) {
|
|
453
|
-
const existing = inflight.get(key);
|
|
517
|
+
const existing = inflight$1.get(key);
|
|
454
518
|
if (existing) return existing;
|
|
455
519
|
const p = run().finally(() => {
|
|
456
|
-
inflight.delete(key);
|
|
520
|
+
inflight$1.delete(key);
|
|
457
521
|
});
|
|
458
|
-
inflight.set(key, p);
|
|
522
|
+
inflight$1.set(key, p);
|
|
459
523
|
return p;
|
|
460
524
|
}
|
|
461
525
|
|
|
@@ -580,6 +644,50 @@ function isSafeLinkHref(raw) {
|
|
|
580
644
|
return lower.startsWith("http://") || lower.startsWith("https://") || lower.startsWith("mailto:");
|
|
581
645
|
}
|
|
582
646
|
|
|
647
|
+
//#endregion
|
|
648
|
+
//#region src/customer-app/query-cache.ts
|
|
649
|
+
const SWR_TTL_MS = 3e4;
|
|
650
|
+
const inflight = /* @__PURE__ */ new Map();
|
|
651
|
+
const cache = /* @__PURE__ */ new Map();
|
|
652
|
+
function queryKey(projectId, db, sql) {
|
|
653
|
+
return `${projectId} ${db ?? ""} ${sql}`;
|
|
654
|
+
}
|
|
655
|
+
function getCached(projectId, sql, db) {
|
|
656
|
+
const e = cache.get(queryKey(projectId, db, sql));
|
|
657
|
+
return e && Date.now() - e.at < SWR_TTL_MS ? e.data : void 0;
|
|
658
|
+
}
|
|
659
|
+
/** Fetch with in-flight dedup + cache. `force` bypasses the fresh-cache
|
|
660
|
+
* short-circuit (used by refetch) but still dedupes a concurrent in-flight. */
|
|
661
|
+
async function sharedQuery(fetcher, projectId, sql, db, opts = {}) {
|
|
662
|
+
const key = queryKey(projectId, db, sql);
|
|
663
|
+
if (!opts.force) {
|
|
664
|
+
const fresh = getCached(projectId, sql, db);
|
|
665
|
+
if (fresh) return fresh;
|
|
666
|
+
}
|
|
667
|
+
const existing = inflight.get(key);
|
|
668
|
+
if (existing) return existing;
|
|
669
|
+
const body = JSON.stringify({
|
|
670
|
+
sql,
|
|
671
|
+
...db ? { database: db } : {}
|
|
672
|
+
});
|
|
673
|
+
const p = (async () => {
|
|
674
|
+
const resp = await fetcher(`/api/projects/${projectId}/query`, {
|
|
675
|
+
method: "POST",
|
|
676
|
+
headers: { "content-type": "application/json" },
|
|
677
|
+
body
|
|
678
|
+
});
|
|
679
|
+
if (!resp.ok) throw await apiErrorFromResponse(resp);
|
|
680
|
+
const data = await resp.json();
|
|
681
|
+
cache.set(key, {
|
|
682
|
+
at: Date.now(),
|
|
683
|
+
data
|
|
684
|
+
});
|
|
685
|
+
return data;
|
|
686
|
+
})().finally(() => inflight.delete(key));
|
|
687
|
+
inflight.set(key, p);
|
|
688
|
+
return p;
|
|
689
|
+
}
|
|
690
|
+
|
|
583
691
|
//#endregion
|
|
584
692
|
//#region src/customer-app/react.tsx
|
|
585
693
|
function defaultFetcher(input, init) {
|
|
@@ -684,58 +792,6 @@ function warnBetaOnce(name) {
|
|
|
684
792
|
if (typeof console !== "undefined" && typeof console.warn === "function") console.warn(`[@oxy-hq/sdk] \`${name}\` is in beta — interface and behavior may change. See https://github.com/oxy-hq/customer-apps for caveats and the migration guide.`);
|
|
685
793
|
}
|
|
686
794
|
/**
|
|
687
|
-
* Error thrown by all customer-app hooks when an API call returns a
|
|
688
|
-
* non-2xx response. Carries the structured `code` + `hint` the server
|
|
689
|
-
* emits so bundle UIs can render an actionable message instead of
|
|
690
|
-
* "404: { ...json... }".
|
|
691
|
-
*
|
|
692
|
-
* The server contract is documented in
|
|
693
|
-
* `crates/app/src/server/api/projects/agent_ask.rs` and
|
|
694
|
-
* `procedure_run.rs` — both emit `{ message, code?, hint? }` as JSON.
|
|
695
|
-
* Hooks that previously wrapped the raw text in `new Error()` now
|
|
696
|
-
* throw this type instead.
|
|
697
|
-
*/
|
|
698
|
-
var OxyApiError = class extends Error {
|
|
699
|
-
constructor(opts) {
|
|
700
|
-
const base = opts.message || `HTTP ${opts.status}`;
|
|
701
|
-
const code = opts.code ? ` [${opts.code}]` : "";
|
|
702
|
-
const hint = opts.hint ? `\n\n${opts.hint}` : "";
|
|
703
|
-
super(`${base}${code}${hint}`);
|
|
704
|
-
this.name = "OxyApiError";
|
|
705
|
-
this.status = opts.status;
|
|
706
|
-
this.code = opts.code ?? null;
|
|
707
|
-
this.hint = opts.hint ?? null;
|
|
708
|
-
}
|
|
709
|
-
};
|
|
710
|
-
/**
|
|
711
|
-
* Read a non-2xx response from oxy and return an `OxyApiError`.
|
|
712
|
-
* Parses the JSON envelope when present; falls back to raw text
|
|
713
|
-
* (truncated to 240 chars so a runaway HTML error page doesn't
|
|
714
|
-
* dominate the bundle UI).
|
|
715
|
-
*/
|
|
716
|
-
async function apiErrorFromResponse(resp) {
|
|
717
|
-
let body;
|
|
718
|
-
let raw = "";
|
|
719
|
-
try {
|
|
720
|
-
raw = await resp.text();
|
|
721
|
-
body = raw ? JSON.parse(raw) : void 0;
|
|
722
|
-
} catch {}
|
|
723
|
-
if (body && typeof body === "object") {
|
|
724
|
-
const b = body;
|
|
725
|
-
return new OxyApiError({
|
|
726
|
-
status: resp.status,
|
|
727
|
-
message: typeof b.message === "string" ? b.message : `HTTP ${resp.status}`,
|
|
728
|
-
code: typeof b.code === "string" ? b.code : null,
|
|
729
|
-
hint: typeof b.hint === "string" ? b.hint : null
|
|
730
|
-
});
|
|
731
|
-
}
|
|
732
|
-
const snippet = raw.length > 240 ? `${raw.slice(0, 237)}…` : raw;
|
|
733
|
-
return new OxyApiError({
|
|
734
|
-
status: resp.status,
|
|
735
|
-
message: snippet || `HTTP ${resp.status}`
|
|
736
|
-
});
|
|
737
|
-
}
|
|
738
|
-
/**
|
|
739
795
|
* Read the resolved manifest from context. Throws if called outside
|
|
740
796
|
* `<OxyAppProvider>` — that's a programmer error worth surfacing
|
|
741
797
|
* loudly, not silently swallowing.
|
|
@@ -789,36 +845,35 @@ function useQuery(input, opts = {}) {
|
|
|
789
845
|
} : s);
|
|
790
846
|
return;
|
|
791
847
|
}
|
|
792
|
-
const ctrl = new AbortController();
|
|
793
848
|
let cancelled = false;
|
|
849
|
+
const cached = getCached(projectId, sqlWithParams, input.database);
|
|
850
|
+
if (cached && nonce === 0) {
|
|
851
|
+
const { columns, rows } = cached;
|
|
852
|
+
const objects = rows.map((r) => Object.fromEntries(columns.map((c, i) => [c, r[i]])));
|
|
853
|
+
setState({
|
|
854
|
+
rows: objects,
|
|
855
|
+
columns,
|
|
856
|
+
loading: false,
|
|
857
|
+
error: null
|
|
858
|
+
});
|
|
859
|
+
return;
|
|
860
|
+
}
|
|
794
861
|
setState((s) => ({
|
|
795
862
|
...s,
|
|
796
863
|
loading: true,
|
|
797
864
|
error: null
|
|
798
865
|
}));
|
|
799
|
-
|
|
800
|
-
sql: sqlWithParams,
|
|
801
|
-
...input.database ? { database: input.database } : {}
|
|
802
|
-
});
|
|
803
|
-
fetcher(`/api/projects/${projectId}/query`, {
|
|
804
|
-
method: "POST",
|
|
805
|
-
headers: { "content-type": "application/json" },
|
|
806
|
-
body,
|
|
807
|
-
signal: ctrl.signal
|
|
808
|
-
}).then(async (resp) => {
|
|
809
|
-
if (!resp.ok) throw await apiErrorFromResponse(resp);
|
|
810
|
-
return resp.json();
|
|
811
|
-
}).then(({ columns, rows }) => {
|
|
866
|
+
sharedQuery(fetcher, projectId, sqlWithParams, input.database, { force: nonce > 0 }).then(({ columns, rows }) => {
|
|
812
867
|
if (cancelled) return;
|
|
868
|
+
const objects = rows.map((r) => Object.fromEntries(columns.map((c, i) => [c, r[i]])));
|
|
813
869
|
setState({
|
|
814
|
-
rows:
|
|
870
|
+
rows: objects,
|
|
815
871
|
columns,
|
|
816
872
|
loading: false,
|
|
817
873
|
error: null
|
|
818
874
|
});
|
|
819
875
|
}).catch((err) => {
|
|
820
876
|
if (cancelled) return;
|
|
821
|
-
if (err instanceof DOMException && err.name === "AbortError") return;
|
|
822
877
|
setState((s) => ({
|
|
823
878
|
...s,
|
|
824
879
|
loading: false,
|
|
@@ -827,7 +882,6 @@ function useQuery(input, opts = {}) {
|
|
|
827
882
|
});
|
|
828
883
|
return () => {
|
|
829
884
|
cancelled = true;
|
|
830
|
-
ctrl.abort();
|
|
831
885
|
};
|
|
832
886
|
}, [
|
|
833
887
|
enabled,
|
|
@@ -968,7 +1022,8 @@ function useSemanticQuery(input, opts = {}) {
|
|
|
968
1022
|
filters: input.filters ?? [],
|
|
969
1023
|
...input.limit != null ? { limit: input.limit } : {}
|
|
970
1024
|
});
|
|
971
|
-
|
|
1025
|
+
const url = `/api/projects/${projectId}/semantic-query${debug ? "?debug=1" : ""}`;
|
|
1026
|
+
fetcher(url, {
|
|
972
1027
|
method: "POST",
|
|
973
1028
|
headers: { "content-type": "application/json" },
|
|
974
1029
|
body,
|
|
@@ -978,8 +1033,9 @@ function useSemanticQuery(input, opts = {}) {
|
|
|
978
1033
|
return resp.json();
|
|
979
1034
|
}).then(({ columns, rows, truncated, sql }) => {
|
|
980
1035
|
if (cancelled) return;
|
|
1036
|
+
const objects = rows.map((r) => Object.fromEntries(columns.map((c, i) => [c, r[i]])));
|
|
981
1037
|
setState({
|
|
982
|
-
rows:
|
|
1038
|
+
rows: objects,
|
|
983
1039
|
columns,
|
|
984
1040
|
truncated,
|
|
985
1041
|
sql: sql ?? null,
|
|
@@ -2313,6 +2369,7 @@ exports.OxyApiError = OxyApiError;
|
|
|
2313
2369
|
exports.OxyAppProvider = OxyAppProvider;
|
|
2314
2370
|
exports.OxyChat = OxyChat;
|
|
2315
2371
|
exports._resetCustomerAppManifestCacheForTest = _resetCustomerAppManifestCacheForTest;
|
|
2372
|
+
exports.apiErrorFromResponse = apiErrorFromResponse;
|
|
2316
2373
|
exports.getCustomerAppDebug = getCustomerAppDebug;
|
|
2317
2374
|
exports.getOxyAppLogger = getOxyAppLogger;
|
|
2318
2375
|
exports.interpretCustomerAppError = interpretCustomerAppError;
|