@usergist/sdk-core 0.1.0 → 0.1.2
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/CHANGELOG.md +5 -0
- package/dist/{mobile-H9slSAy8.d.cts → client-DGHJLNJu.d.cts} +1395 -15
- package/dist/{mobile-H9slSAy8.d.ts → client-DGHJLNJu.d.ts} +1395 -15
- package/dist/client.cjs +1235 -0
- package/dist/client.cjs.map +1 -0
- package/dist/client.d.cts +2 -0
- package/dist/client.d.ts +2 -0
- package/dist/client.js +1170 -0
- package/dist/client.js.map +1 -0
- package/dist/index.cjs +1218 -38
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +880 -17
- package/dist/index.d.ts +880 -17
- package/dist/index.js +1189 -39
- package/dist/index.js.map +1 -1
- package/dist/mobile.cjs +495 -23
- package/dist/mobile.cjs.map +1 -1
- package/dist/mobile.d.cts +2 -1
- package/dist/mobile.d.ts +2 -1
- package/dist/mobile.js +484 -24
- package/dist/mobile.js.map +1 -1
- package/package.json +7 -2
package/dist/index.js
CHANGED
|
@@ -25,6 +25,16 @@ function err(code, message, details) {
|
|
|
25
25
|
};
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
// src/types/workspace.ts
|
|
29
|
+
function defaultOnboardingMessage(appName) {
|
|
30
|
+
return {
|
|
31
|
+
title: `Welcome to ${appName.trim() || "our app"}`,
|
|
32
|
+
body: "Glad you\u2019re here. Take a look around and make yourself at home.",
|
|
33
|
+
buttonLabel: "Got it",
|
|
34
|
+
format: "modal"
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
28
38
|
// src/types/survey.ts
|
|
29
39
|
var SURVEY_END_SENTINEL = "__end__";
|
|
30
40
|
|
|
@@ -268,8 +278,8 @@ var THEME_PRESETS = [
|
|
|
268
278
|
swatches: ["#8B5CF6", "#EC4899", "#E9D5FF"]
|
|
269
279
|
}
|
|
270
280
|
];
|
|
271
|
-
function getThemePresetById(
|
|
272
|
-
return THEME_PRESETS.find((preset) => preset.id ===
|
|
281
|
+
function getThemePresetById(id2) {
|
|
282
|
+
return THEME_PRESETS.find((preset) => preset.id === id2);
|
|
273
283
|
}
|
|
274
284
|
function defaultThemePreset() {
|
|
275
285
|
return THEME_PRESETS[0];
|
|
@@ -320,7 +330,7 @@ var USER_IDENTIFIED_EVENT_NAME = "$user_identified";
|
|
|
320
330
|
function camelize(value) {
|
|
321
331
|
return value.replace(/_([a-z])/g, (_match, letter) => letter.toUpperCase());
|
|
322
332
|
}
|
|
323
|
-
var ids = (...keys) => Object.fromEntries(keys.map((
|
|
333
|
+
var ids = (...keys) => Object.fromEntries(keys.map((key2) => [key2, [key2, camelize(key2)]]));
|
|
324
334
|
var pushEvents = [
|
|
325
335
|
["$push_sent", "UserGist Push Sent"],
|
|
326
336
|
["$push_delivered", "UserGist Push Delivered"],
|
|
@@ -377,8 +387,8 @@ var CORE_INTEGRATION_EVENTS = [
|
|
|
377
387
|
{ key: "$inapp_dismissed", name: "UserGist In-App Message Dismissed", category: "inapp", subjectRole: "actor", properties: { ...ids("message_id", "dismiss_reason") } },
|
|
378
388
|
{ key: "$inapp_auto_dismissed", name: "UserGist In-App Message Auto-Dismissed", category: "inapp", subjectRole: "actor", properties: { ...ids("message_id", "dismiss_reason") } },
|
|
379
389
|
{ key: "$inapp_cta_clicked", name: "UserGist In-App Message CTA Clicked", category: "inapp", subjectRole: "actor", properties: { ...ids("message_id", "cta_id", "cta_index"), action: ["action", "cta_action"] } },
|
|
380
|
-
...pushEvents.map(([
|
|
381
|
-
key,
|
|
390
|
+
...pushEvents.map(([key2, name]) => ({
|
|
391
|
+
key: key2,
|
|
382
392
|
name,
|
|
383
393
|
category: "push",
|
|
384
394
|
subjectRole: "actor",
|
|
@@ -387,11 +397,11 @@ var CORE_INTEGRATION_EVENTS = [
|
|
|
387
397
|
action_id: ["action_id", "actionId", "action_button", "actionButton"]
|
|
388
398
|
}
|
|
389
399
|
})),
|
|
390
|
-
...requestEvents.map(([
|
|
400
|
+
...requestEvents.map(([key2, name, subjectRole]) => ({ key: key2, name, category: "requests", subjectRole, properties: { ...ids("request_id", "comment_id", "source", "old_status", "new_status", "deleted_by"), usergist_actor_type: ["usergist_actor_type", "actor_type"] } }))
|
|
391
401
|
];
|
|
392
402
|
var CORE_EVENT_BY_KEY = new Map(CORE_INTEGRATION_EVENTS.map((event) => [event.key, event]));
|
|
393
|
-
function getCoreIntegrationEvent(
|
|
394
|
-
return CORE_EVENT_BY_KEY.get(
|
|
403
|
+
function getCoreIntegrationEvent(key2) {
|
|
404
|
+
return CORE_EVENT_BY_KEY.get(key2) ?? null;
|
|
395
405
|
}
|
|
396
406
|
function sanitizeCoreIntegrationProperties(definition, input) {
|
|
397
407
|
if (!input) return {};
|
|
@@ -498,15 +508,15 @@ function evaluateGroup(group, user, now) {
|
|
|
498
508
|
}
|
|
499
509
|
return group.predicates.some((p) => evaluateNode(p, user, now));
|
|
500
510
|
}
|
|
501
|
-
function evaluateNode(
|
|
502
|
-
if ("combinator" in
|
|
503
|
-
switch (
|
|
511
|
+
function evaluateNode(node2, user, now) {
|
|
512
|
+
if ("combinator" in node2) return evaluateGroup(node2, user, now);
|
|
513
|
+
switch (node2.kind) {
|
|
504
514
|
case "user_property":
|
|
505
|
-
return evaluateUserProperty(
|
|
515
|
+
return evaluateUserProperty(node2, user);
|
|
506
516
|
case "event_count":
|
|
507
|
-
return evaluateEventCount(
|
|
517
|
+
return evaluateEventCount(node2, user);
|
|
508
518
|
case "event_occurred":
|
|
509
|
-
return evaluateEventOccurred(
|
|
519
|
+
return evaluateEventOccurred(node2, user, now);
|
|
510
520
|
}
|
|
511
521
|
}
|
|
512
522
|
function evaluateUserProperty(p, user) {
|
|
@@ -660,15 +670,15 @@ function reachableQuestions(flow, startId) {
|
|
|
660
670
|
const visited = /* @__PURE__ */ new Set();
|
|
661
671
|
const stack = [start];
|
|
662
672
|
while (stack.length > 0) {
|
|
663
|
-
const
|
|
664
|
-
if (
|
|
665
|
-
if (visited.has(
|
|
666
|
-
visited.add(
|
|
667
|
-
const outgoing = flow.branches.filter((b) => b.fromQuestionId ===
|
|
673
|
+
const id2 = stack.pop();
|
|
674
|
+
if (id2 === void 0) continue;
|
|
675
|
+
if (visited.has(id2)) continue;
|
|
676
|
+
visited.add(id2);
|
|
677
|
+
const outgoing = flow.branches.filter((b) => b.fromQuestionId === id2);
|
|
668
678
|
for (const b of outgoing) {
|
|
669
679
|
if (b.toQuestionId !== SURVEY_END_SENTINEL) stack.push(b.toQuestionId);
|
|
670
680
|
}
|
|
671
|
-
const idx = flow.questions.findIndex((q) => q.id ===
|
|
681
|
+
const idx = flow.questions.findIndex((q) => q.id === id2);
|
|
672
682
|
if (idx >= 0 && idx < flow.questions.length - 1) {
|
|
673
683
|
const next = flow.questions[idx + 1];
|
|
674
684
|
if (next) stack.push(next.id);
|
|
@@ -707,7 +717,7 @@ function tzOffsetMinutes(at, tz) {
|
|
|
707
717
|
function startCandidate(schedule, now, tz) {
|
|
708
718
|
const localNow = wallClock(now, tz);
|
|
709
719
|
const target = new Date(localNow);
|
|
710
|
-
target.
|
|
720
|
+
target.setUTCHours(schedule.hourLocal, schedule.minuteLocal, 0, 0);
|
|
711
721
|
return localToUtc(target, tz);
|
|
712
722
|
}
|
|
713
723
|
function walkForward(candidate, schedule, now, tz) {
|
|
@@ -725,30 +735,30 @@ function walkForward(candidate, schedule, now, tz) {
|
|
|
725
735
|
function matchesFrequency(local, schedule) {
|
|
726
736
|
if (schedule.frequency === "daily") return true;
|
|
727
737
|
if (schedule.frequency === "weekly") {
|
|
728
|
-
return schedule.weekday === void 0 || local.
|
|
738
|
+
return schedule.weekday === void 0 || local.getUTCDay() === schedule.weekday;
|
|
729
739
|
}
|
|
730
|
-
return schedule.dayOfMonth === void 0 || local.
|
|
740
|
+
return schedule.dayOfMonth === void 0 || local.getUTCDate() === schedule.dayOfMonth;
|
|
731
741
|
}
|
|
732
742
|
function step(cursor, cursorLocal, schedule, tz) {
|
|
733
743
|
if (schedule.frequency === "daily" || schedule.frequency === "weekly") {
|
|
734
744
|
return addDays(cursor, 1);
|
|
735
745
|
}
|
|
736
746
|
const next = new Date(cursorLocal);
|
|
737
|
-
next.
|
|
747
|
+
next.setUTCMonth(next.getUTCMonth() + 1);
|
|
738
748
|
if (schedule.dayOfMonth !== void 0) {
|
|
739
|
-
const lastDay = lastDayOfMonth(next.
|
|
740
|
-
next.
|
|
749
|
+
const lastDay = lastDayOfMonth(next.getUTCFullYear(), next.getUTCMonth());
|
|
750
|
+
next.setUTCDate(Math.min(schedule.dayOfMonth, lastDay));
|
|
741
751
|
}
|
|
742
|
-
next.
|
|
752
|
+
next.setUTCHours(schedule.hourLocal, schedule.minuteLocal, 0, 0);
|
|
743
753
|
return localToUtc(next, tz);
|
|
744
754
|
}
|
|
745
755
|
function addDays(d, days) {
|
|
746
756
|
const next = new Date(d.getTime());
|
|
747
|
-
next.
|
|
757
|
+
next.setUTCDate(next.getUTCDate() + days);
|
|
748
758
|
return next;
|
|
749
759
|
}
|
|
750
760
|
function lastDayOfMonth(year, monthIndex) {
|
|
751
|
-
return new Date(year, monthIndex + 1, 0).
|
|
761
|
+
return new Date(Date.UTC(year, monthIndex + 1, 0)).getUTCDate();
|
|
752
762
|
}
|
|
753
763
|
function wallClock(d, tz) {
|
|
754
764
|
const offsetMin = tzOffsetMinutes(d, tz);
|
|
@@ -776,11 +786,151 @@ function getFormatter(tz) {
|
|
|
776
786
|
return fmt;
|
|
777
787
|
}
|
|
778
788
|
|
|
789
|
+
// src/timezone.ts
|
|
790
|
+
var PARTS_FORMATTER_CACHE = /* @__PURE__ */ new Map();
|
|
791
|
+
function isValidIanaTimeZone(value) {
|
|
792
|
+
if (!value.trim()) return false;
|
|
793
|
+
try {
|
|
794
|
+
new Intl.DateTimeFormat("en-US", { timeZone: value }).format();
|
|
795
|
+
return true;
|
|
796
|
+
} catch {
|
|
797
|
+
return false;
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
function zonedDateTimeParts(instant, timeZone) {
|
|
801
|
+
if (!isValidIanaTimeZone(timeZone)) {
|
|
802
|
+
throw new RangeError(`Invalid IANA timezone: ${timeZone}`);
|
|
803
|
+
}
|
|
804
|
+
if (!Number.isFinite(instant.getTime())) {
|
|
805
|
+
throw new RangeError("Invalid date");
|
|
806
|
+
}
|
|
807
|
+
const formatter = getPartsFormatter(timeZone);
|
|
808
|
+
const parts = formatter.formatToParts(instant).reduce(
|
|
809
|
+
(result, part) => {
|
|
810
|
+
if (part.type !== "literal") result[part.type] = part.value;
|
|
811
|
+
return result;
|
|
812
|
+
},
|
|
813
|
+
{}
|
|
814
|
+
);
|
|
815
|
+
const year = Number(parts.year);
|
|
816
|
+
const month = Number(parts.month);
|
|
817
|
+
const day = Number(parts.day);
|
|
818
|
+
const hour = Number(parts.hour);
|
|
819
|
+
const minute = Number(parts.minute);
|
|
820
|
+
return {
|
|
821
|
+
date: `${year}-${pad2(month)}-${pad2(day)}`,
|
|
822
|
+
time: `${pad2(hour)}:${pad2(minute)}`,
|
|
823
|
+
weekday: new Date(Date.UTC(year, month - 1, day)).getUTCDay(),
|
|
824
|
+
dayOfMonth: day
|
|
825
|
+
};
|
|
826
|
+
}
|
|
827
|
+
function zonedDateTimeToUtc(date, time, timeZone) {
|
|
828
|
+
if (!isValidIanaTimeZone(timeZone)) {
|
|
829
|
+
throw new RangeError(`Invalid IANA timezone: ${timeZone}`);
|
|
830
|
+
}
|
|
831
|
+
const dateMatch = /^(\d{4})-(\d{2})-(\d{2})$/.exec(date);
|
|
832
|
+
const timeMatch = /^(\d{2}):(\d{2})$/.exec(time);
|
|
833
|
+
if (!dateMatch || !timeMatch) throw new RangeError("Invalid local date or time");
|
|
834
|
+
const year = Number(dateMatch[1]);
|
|
835
|
+
const month = Number(dateMatch[2]);
|
|
836
|
+
const day = Number(dateMatch[3]);
|
|
837
|
+
const hour = Number(timeMatch[1]);
|
|
838
|
+
const minute = Number(timeMatch[2]);
|
|
839
|
+
const naive = Date.UTC(year, month - 1, day, hour, minute, 0, 0);
|
|
840
|
+
const calendarCheck = new Date(naive);
|
|
841
|
+
if (calendarCheck.getUTCFullYear() !== year || calendarCheck.getUTCMonth() !== month - 1 || calendarCheck.getUTCDate() !== day || hour > 23 || minute > 59) {
|
|
842
|
+
throw new RangeError("Invalid local date or time");
|
|
843
|
+
}
|
|
844
|
+
const offsets = new Set(
|
|
845
|
+
[-36, -12, 0, 12, 36].map(
|
|
846
|
+
(hours) => timeZoneOffsetMinutes(new Date(naive + hours * 36e5), timeZone)
|
|
847
|
+
)
|
|
848
|
+
);
|
|
849
|
+
const candidates = [...offsets].map((offset) => new Date(naive - offset * 6e4)).filter((candidate2) => {
|
|
850
|
+
const roundTrip = zonedDateTimeParts(candidate2, timeZone);
|
|
851
|
+
return roundTrip.date === date && roundTrip.time === time;
|
|
852
|
+
}).sort((left, right) => left.getTime() - right.getTime());
|
|
853
|
+
const candidate = candidates[0];
|
|
854
|
+
if (!candidate) {
|
|
855
|
+
throw new RangeError("This local time does not exist in the selected timezone");
|
|
856
|
+
}
|
|
857
|
+
return candidate;
|
|
858
|
+
}
|
|
859
|
+
function zonedDateEndToUtc(date, timeZone) {
|
|
860
|
+
const lastMinute = zonedDateTimeToUtc(date, "23:59", timeZone);
|
|
861
|
+
return new Date(lastMinute.getTime() + 59999);
|
|
862
|
+
}
|
|
863
|
+
function timeZoneOffsetMinutes(instant, timeZone) {
|
|
864
|
+
const parts = getPartsFormatter(timeZone).formatToParts(instant).reduce((result, part) => {
|
|
865
|
+
if (part.type !== "literal") result[part.type] = part.value;
|
|
866
|
+
return result;
|
|
867
|
+
}, {});
|
|
868
|
+
const asUtc = Date.UTC(
|
|
869
|
+
Number(parts.year),
|
|
870
|
+
Number(parts.month) - 1,
|
|
871
|
+
Number(parts.day),
|
|
872
|
+
Number(parts.hour),
|
|
873
|
+
Number(parts.minute),
|
|
874
|
+
Number(parts.second)
|
|
875
|
+
);
|
|
876
|
+
return Math.round((asUtc - instant.getTime()) / 6e4);
|
|
877
|
+
}
|
|
878
|
+
function getPartsFormatter(timeZone) {
|
|
879
|
+
const cached = PARTS_FORMATTER_CACHE.get(timeZone);
|
|
880
|
+
if (cached) return cached;
|
|
881
|
+
const formatter = new Intl.DateTimeFormat("en-US", {
|
|
882
|
+
timeZone,
|
|
883
|
+
hourCycle: "h23",
|
|
884
|
+
year: "numeric",
|
|
885
|
+
month: "2-digit",
|
|
886
|
+
day: "2-digit",
|
|
887
|
+
hour: "2-digit",
|
|
888
|
+
minute: "2-digit",
|
|
889
|
+
second: "2-digit"
|
|
890
|
+
});
|
|
891
|
+
PARTS_FORMATTER_CACHE.set(timeZone, formatter);
|
|
892
|
+
return formatter;
|
|
893
|
+
}
|
|
894
|
+
function pad2(value) {
|
|
895
|
+
return String(value).padStart(2, "0");
|
|
896
|
+
}
|
|
897
|
+
|
|
779
898
|
// src/contract/endpoints.ts
|
|
780
899
|
var endpoints = {
|
|
900
|
+
"GET /v1/apps/:appId/search": {},
|
|
901
|
+
"POST /v1/apps/:appId/delivery-diagnostics": {},
|
|
902
|
+
"GET /v1/workspaces/:wid/portal": {},
|
|
903
|
+
"PUT /v1/workspaces/:wid/portal": {},
|
|
904
|
+
"GET /v1/workspaces/:wid/portal/slug-available": {},
|
|
905
|
+
"PUT /v1/workspaces/:wid/portal/apps/:appId": {},
|
|
906
|
+
"POST /v1/workspaces/:wid/portal/publish": {},
|
|
907
|
+
"POST /v1/workspaces/:wid/portal/unpublish": {},
|
|
908
|
+
"GET /v1/workspaces/:wid/portal/preview/:appId": {},
|
|
909
|
+
"PATCH /v1/apps/:appId/requests/portal-visibility": {},
|
|
910
|
+
"GET /v1/portal/:portalSlug": {},
|
|
911
|
+
"GET /v1/portal/:portalSlug/apps/:appSlug/requests": {},
|
|
912
|
+
"GET /v1/portal/:portalSlug/apps/:appSlug/requests/counts": {},
|
|
913
|
+
"GET /v1/portal/:portalSlug/apps/:appSlug/requests/:requestId": {},
|
|
914
|
+
"POST /v1/portal/:portalSlug/auth/start": {},
|
|
915
|
+
"POST /v1/portal/:portalSlug/auth/verify": {},
|
|
916
|
+
"GET /v1/portal/:portalSlug/session": {},
|
|
917
|
+
"DELETE /v1/portal/:portalSlug/session": {},
|
|
918
|
+
"GET /v1/portal/:portalSlug/apps/:appSlug/votes": {},
|
|
919
|
+
"POST /v1/portal/:portalSlug/apps/:appSlug/requests": {},
|
|
920
|
+
"PUT /v1/portal/:portalSlug/apps/:appSlug/requests/:requestId/vote": {},
|
|
921
|
+
"POST /v1/sdk/clients": {},
|
|
922
|
+
"POST /v1/sdk/clients/:id/end": {},
|
|
923
|
+
"GET /v1/sdk/clients/:id/instructions": {},
|
|
924
|
+
"POST /v1/sdk/presentations/authorize": {},
|
|
925
|
+
"POST /v1/sdk/presentations/:id/receipt": {},
|
|
781
926
|
"GET /v1/me": {},
|
|
927
|
+
"PATCH /v1/me": {},
|
|
928
|
+
"PATCH /v1/me/onboarding": {},
|
|
929
|
+
"GET /v1/features": {},
|
|
930
|
+
"GET /v1/signup-status": {},
|
|
782
931
|
"GET /v1/workspaces": {},
|
|
783
932
|
"POST /v1/workspaces": {},
|
|
933
|
+
"PATCH /v1/workspaces/:wid": {},
|
|
784
934
|
"GET /v1/workspaces/:wid/members": {},
|
|
785
935
|
"GET /v1/workspaces/:wid/invites": {},
|
|
786
936
|
"POST /v1/workspaces/:wid/invites": {},
|
|
@@ -789,11 +939,18 @@ var endpoints = {
|
|
|
789
939
|
"GET /v1/workspaces/:wid/apps": {},
|
|
790
940
|
"POST /v1/workspaces/:wid/apps": {},
|
|
791
941
|
"GET /v1/workspaces/:wid/api-tokens": {},
|
|
942
|
+
"GET /v1/workspaces/:wid/ai-connections": {},
|
|
943
|
+
"PATCH /v1/workspaces/:wid/ai-connections/:id": {},
|
|
944
|
+
"POST /v1/workspaces/:wid/ai-connections/:id/revoke": {},
|
|
945
|
+
"GET /v1/workspaces/:wid/ai-connections/:id/activity": {},
|
|
946
|
+
"POST /v1/mcp/consent": {},
|
|
792
947
|
"POST /v1/workspaces/:wid/api-tokens": {},
|
|
793
948
|
"DELETE /v1/workspaces/:wid/api-tokens/:tokenId": {},
|
|
794
949
|
"GET /v1/apps/:appId": {},
|
|
795
950
|
"PATCH /v1/apps/:appId": {},
|
|
796
951
|
"DELETE /v1/apps/:appId": {},
|
|
952
|
+
"GET /v1/apps/:appId/onboarding": {},
|
|
953
|
+
"PATCH /v1/apps/:appId/onboarding": {},
|
|
797
954
|
"POST /v1/apps/:appId/sdk/subject-tokens": {},
|
|
798
955
|
// ---------- outbound analytics integrations ----------
|
|
799
956
|
"GET /v1/apps/:appId/integrations": {},
|
|
@@ -850,6 +1007,10 @@ var endpoints = {
|
|
|
850
1007
|
"POST /v1/sdk/ingest": {},
|
|
851
1008
|
"GET /v1/sdk/armed-triggers": {},
|
|
852
1009
|
"POST /v1/sdk/consent": {},
|
|
1010
|
+
"PATCH /v1/apps/:appId/users/properties": {},
|
|
1011
|
+
"POST /v1/sdk/user-properties": {},
|
|
1012
|
+
"GET /v1/apps/:appId/personalization/fields": {},
|
|
1013
|
+
"POST /v1/apps/:appId/personalization/preview": {},
|
|
853
1014
|
"POST /v1/sdk/identify": {},
|
|
854
1015
|
"POST /v1/sdk/responses": {},
|
|
855
1016
|
// GDPR
|
|
@@ -954,6 +1115,9 @@ var endpoints = {
|
|
|
954
1115
|
"GET /v1/apps/:appId/request-settings": {},
|
|
955
1116
|
"PUT /v1/apps/:appId/request-settings": {},
|
|
956
1117
|
"GET /v1/apps/:appId/request-settings/slug-available": {},
|
|
1118
|
+
/** Multipart `file` field; the processed logo URL is saved into branding.logoUrl. */
|
|
1119
|
+
"POST /v1/apps/:appId/request-settings/logo": {},
|
|
1120
|
+
"DELETE /v1/apps/:appId/request-settings/logo": {},
|
|
957
1121
|
"POST /v1/apps/:appId/requests/seed-segments": {},
|
|
958
1122
|
// ---------- feature requests — SDK (write-key) ----------
|
|
959
1123
|
"GET /v1/sdk/requests": {},
|
|
@@ -981,8 +1145,15 @@ var endpoints = {
|
|
|
981
1145
|
"GET /v1/workspaces/:wid/billing/periods": {},
|
|
982
1146
|
// ---------- super admin ----------
|
|
983
1147
|
"GET /v1/admin/session": {},
|
|
1148
|
+
"GET /v1/admin/feature-flags": {},
|
|
1149
|
+
"PATCH /v1/admin/feature-flags/:key": {},
|
|
984
1150
|
"GET /v1/admin/customers": {},
|
|
985
1151
|
"GET /v1/admin/customers/:workspaceId": {},
|
|
1152
|
+
"POST /v1/admin/customers/:workspaceId/permanent-deletion": {},
|
|
1153
|
+
"GET /v1/admin/dashboard-users": {},
|
|
1154
|
+
"POST /v1/admin/dashboard-users/:userId/permanent-deletion": {},
|
|
1155
|
+
"GET /v1/admin/deletion-jobs/:jobId": {},
|
|
1156
|
+
"POST /v1/admin/deletion-jobs/:jobId/retry": {},
|
|
986
1157
|
"POST /v1/admin/customers/:workspaceId/billing-events/:eventId/replay": {},
|
|
987
1158
|
"POST /v1/admin/customers/:workspaceId/grants": {},
|
|
988
1159
|
"POST /v1/admin/customers/:workspaceId/grants/:grantId/extend": {},
|
|
@@ -995,7 +1166,43 @@ var endpoints = {
|
|
|
995
1166
|
// ---------- feature requests — public web roadmap (no auth) ----------
|
|
996
1167
|
"GET /v1/public/roadmap/:slug": {},
|
|
997
1168
|
"GET /v1/public/roadmap/:slug/r/:requestId": {},
|
|
998
|
-
"GET /v1/public/roadmap/:slug/branding": {}
|
|
1169
|
+
"GET /v1/public/roadmap/:slug/branding": {},
|
|
1170
|
+
"GET /v1/apps/:appId/help/articles": {},
|
|
1171
|
+
"POST /v1/apps/:appId/help/articles": {},
|
|
1172
|
+
"GET /v1/apps/:appId/help/articles/:documentId": {},
|
|
1173
|
+
"PUT /v1/apps/:appId/help/articles/:documentId": {},
|
|
1174
|
+
"POST /v1/apps/:appId/help/articles/:documentId/lifecycle": {},
|
|
1175
|
+
"GET /v1/apps/:appId/changelog": {},
|
|
1176
|
+
"POST /v1/apps/:appId/changelog": {},
|
|
1177
|
+
"GET /v1/apps/:appId/changelog/:documentId": {},
|
|
1178
|
+
"PUT /v1/apps/:appId/changelog/:documentId": {},
|
|
1179
|
+
"POST /v1/apps/:appId/changelog/:documentId/lifecycle": {},
|
|
1180
|
+
"GET /v1/apps/:appId/help/collections": {},
|
|
1181
|
+
"POST /v1/apps/:appId/help/collections": {},
|
|
1182
|
+
"PATCH /v1/apps/:appId/help/collections/:collectionId": {},
|
|
1183
|
+
"POST /v1/apps/:appId/help/collections/:collectionId/position": {},
|
|
1184
|
+
"POST /v1/apps/:appId/help/articles/:documentId/position": {},
|
|
1185
|
+
"GET /v1/apps/:appId/roadmap": {},
|
|
1186
|
+
"POST /v1/apps/:appId/roadmap": {},
|
|
1187
|
+
"GET /v1/apps/:appId/roadmap/:itemId": {},
|
|
1188
|
+
"PUT /v1/apps/:appId/roadmap/:itemId": {},
|
|
1189
|
+
"POST /v1/apps/:appId/roadmap/:itemId/status": {},
|
|
1190
|
+
"POST /v1/apps/:appId/roadmap/:itemId/lifecycle": {},
|
|
1191
|
+
"GET /v1/portal/:portalSlug/apps/:appSlug/help/collections": {},
|
|
1192
|
+
"GET /v1/portal/:portalSlug/apps/:appSlug/help/collections/:collectionId": {},
|
|
1193
|
+
"GET /v1/portal/:portalSlug/apps/:appSlug/help/articles": {},
|
|
1194
|
+
"GET /v1/portal/:portalSlug/apps/:appSlug/help/articles/:documentId": {},
|
|
1195
|
+
"GET /v1/portal/:portalSlug/apps/:appSlug/changelog": {},
|
|
1196
|
+
"GET /v1/portal/:portalSlug/apps/:appSlug/changelog/:documentId": {},
|
|
1197
|
+
"GET /v1/portal/:portalSlug/apps/:appSlug/roadmap": {},
|
|
1198
|
+
"GET /v1/portal/:portalSlug/apps/:appSlug/roadmap/:itemId": {},
|
|
1199
|
+
"GET /v1/portal/:portalSlug/apps/:appSlug/requests/:requestId/changelog": {},
|
|
1200
|
+
"GET /v1/portal/:portalSlug/apps/:appSlug/roadmap/:itemId/changelog": {},
|
|
1201
|
+
"GET /v1/apps/:appId/portal-content/link-targets": {},
|
|
1202
|
+
// Binary routes use multipart/stream transports, not the JSON client helper.
|
|
1203
|
+
"POST /v1/apps/:appId/portal-content/assets/:kind/:documentId": {},
|
|
1204
|
+
"GET /v1/apps/:appId/portal-content/assets/:assetId": {},
|
|
1205
|
+
"GET /v1/portal/:portalSlug/apps/:appSlug/assets/:assetId": {}
|
|
999
1206
|
};
|
|
1000
1207
|
|
|
1001
1208
|
// src/schemas/index.ts
|
|
@@ -1003,24 +1210,74 @@ var schemas_exports = {};
|
|
|
1003
1210
|
__export(schemas_exports, {
|
|
1004
1211
|
acceptWorkspaceInviteSchema: () => acceptWorkspaceInviteSchema,
|
|
1005
1212
|
apiTokenScopeSchema: () => apiTokenScopeSchema,
|
|
1213
|
+
contentActionSchema: () => contentActionSchema,
|
|
1214
|
+
contentBodySchema: () => contentBodySchema,
|
|
1215
|
+
contentKindSchema: () => contentKindSchema,
|
|
1216
|
+
contentLinkQuerySchema: () => contentLinkQuerySchema,
|
|
1217
|
+
contentLinkSchema: () => contentLinkSchema,
|
|
1218
|
+
contentPageQuerySchema: () => contentPageQuerySchema,
|
|
1219
|
+
contentQuerySchema: () => contentQuerySchema,
|
|
1220
|
+
contentStateSchema: () => contentStateSchema,
|
|
1006
1221
|
createApiTokenSchema: () => createApiTokenSchema,
|
|
1007
1222
|
createAppSchema: () => createAppSchema,
|
|
1223
|
+
createCollectionSchema: () => createCollectionSchema,
|
|
1224
|
+
createDocumentSchema: () => createDocumentSchema,
|
|
1225
|
+
createRoadmapSchema: () => createRoadmapSchema,
|
|
1008
1226
|
createWorkspaceSchema: () => createWorkspaceSchema,
|
|
1009
1227
|
createWriteKeySchema: () => createWriteKeySchema,
|
|
1228
|
+
deferCurrentUserOnboardingSchema: () => deferCurrentUserOnboardingSchema,
|
|
1229
|
+
deliveryPlatformsSchema: () => deliveryPlatformsSchema,
|
|
1230
|
+
documentDraftSchema: () => documentDraftSchema,
|
|
1010
1231
|
emailSchema: () => emailSchema,
|
|
1011
1232
|
inviteMemberSchema: () => inviteMemberSchema,
|
|
1012
1233
|
isoDateTimeSchema: () => isoDateTimeSchema,
|
|
1013
1234
|
loginMagicLinkConsumeRequestSchema: () => loginMagicLinkConsumeRequestSchema,
|
|
1014
1235
|
loginMagicLinkRequestSchema: () => loginMagicLinkRequestSchema,
|
|
1015
1236
|
loginPasswordRequestSchema: () => loginPasswordRequestSchema,
|
|
1237
|
+
mcpCapabilitySchema: () => mcpCapabilitySchema,
|
|
1238
|
+
mcpConsentSchema: () => mcpConsentSchema,
|
|
1239
|
+
mcpGrantSchema: () => mcpGrantSchema,
|
|
1240
|
+
mcpPageSchema: () => mcpPageSchema,
|
|
1241
|
+
mcpSearchSchema: () => mcpSearchSchema,
|
|
1242
|
+
mcpUpdateConnectionSchema: () => mcpUpdateConnectionSchema,
|
|
1243
|
+
moveRoadmapSchema: () => moveRoadmapSchema,
|
|
1244
|
+
onboardingGoalSchema: () => onboardingGoalSchema,
|
|
1245
|
+
onboardingPushChoiceSchema: () => onboardingPushChoiceSchema,
|
|
1246
|
+
onboardingStatusSchema: () => onboardingStatusSchema,
|
|
1247
|
+
onboardingStepSchema: () => onboardingStepSchema,
|
|
1016
1248
|
passwordSchema: () => passwordSchema,
|
|
1017
1249
|
platformSchema: () => platformSchema,
|
|
1250
|
+
portalAssetUploadKeySchema: () => portalAssetUploadKeySchema,
|
|
1251
|
+
portalAuthStartSchema: () => portalAuthStartSchema,
|
|
1252
|
+
portalAuthVerifySchema: () => portalAuthVerifySchema,
|
|
1253
|
+
portalEmailSchema: () => portalEmailSchema,
|
|
1254
|
+
portalRequestQuerySchema: () => portalRequestQuerySchema,
|
|
1255
|
+
portalSlugSchema: () => portalSlugSchema,
|
|
1256
|
+
portalSubmissionSchema: () => portalSubmissionSchema,
|
|
1257
|
+
portalVisibilitySchema: () => portalVisibilitySchema,
|
|
1258
|
+
portalVoteSchema: () => portalVoteSchema,
|
|
1259
|
+
reorderContentSchema: () => reorderContentSchema,
|
|
1260
|
+
roadmapQuerySchema: () => roadmapQuerySchema,
|
|
1261
|
+
roadmapStatusSchema: () => roadmapStatusSchema,
|
|
1018
1262
|
rotateWriteKeySchema: () => rotateWriteKeySchema,
|
|
1263
|
+
saveDocumentSchema: () => saveDocumentSchema,
|
|
1264
|
+
saveRoadmapSchema: () => saveRoadmapSchema,
|
|
1019
1265
|
signupRequestSchema: () => signupRequestSchema,
|
|
1020
1266
|
slugSchema: () => slugSchema,
|
|
1267
|
+
subjectRefSchema: () => subjectRefSchema,
|
|
1021
1268
|
updateAppSchema: () => updateAppSchema,
|
|
1269
|
+
updateCollectionSchema: () => updateCollectionSchema,
|
|
1270
|
+
updateCurrentUserSchema: () => updateCurrentUserSchema,
|
|
1271
|
+
updateOnboardingSchema: () => updateOnboardingSchema,
|
|
1272
|
+
updatePortalAppSchema: () => updatePortalAppSchema,
|
|
1273
|
+
updatePortalSchema: () => updatePortalSchema,
|
|
1274
|
+
updateWorkspaceSchema: () => updateWorkspaceSchema,
|
|
1022
1275
|
uuidSchema: () => uuidSchema,
|
|
1276
|
+
webAppConfigSchema: () => webAppConfigSchema,
|
|
1277
|
+
webOriginSchema: () => webOriginSchema,
|
|
1278
|
+
webPresentationSchema: () => webPresentationSchema,
|
|
1023
1279
|
workspaceRoleSchema: () => workspaceRoleSchema,
|
|
1280
|
+
workspaceTimezoneSchema: () => workspaceTimezoneSchema,
|
|
1024
1281
|
writeKeyEnvironmentSchema: () => writeKeyEnvironmentSchema
|
|
1025
1282
|
});
|
|
1026
1283
|
var slugSchema = z.string().min(1).max(64).regex(/^[a-z0-9-]+$/, "Must be lowercase, digits, and dashes only");
|
|
@@ -1047,21 +1304,199 @@ var loginMagicLinkRequestSchema = z.object({
|
|
|
1047
1304
|
var loginMagicLinkConsumeRequestSchema = z.object({
|
|
1048
1305
|
token: z.string().min(16).max(512)
|
|
1049
1306
|
});
|
|
1050
|
-
var
|
|
1051
|
-
|
|
1052
|
-
|
|
1307
|
+
var updateCurrentUserSchema = z.object({
|
|
1308
|
+
name: z.string().trim().min(1).max(120).optional()
|
|
1309
|
+
}).strict().refine((value) => Object.keys(value).length > 0, {
|
|
1310
|
+
message: "At least one field is required"
|
|
1311
|
+
});
|
|
1312
|
+
var deliveryPlatformsSchema = z.array(z.enum(["ios", "android", "web"])).min(1).max(3).transform((values) => [...new Set(values)]);
|
|
1313
|
+
var webPresentationSchema = z.object({
|
|
1314
|
+
layout: z.enum(["modal", "card", "panel"]).optional(),
|
|
1315
|
+
size: z.enum(["compact", "standard", "wide"]).optional(),
|
|
1316
|
+
position: z.enum(["left", "right"]).optional(),
|
|
1317
|
+
backdrop: z.boolean().optional()
|
|
1318
|
+
}).strict();
|
|
1319
|
+
var webOriginSchema = z.string().max(2048).refine((value) => {
|
|
1320
|
+
try {
|
|
1321
|
+
const url = new URL(value);
|
|
1322
|
+
return url.origin === value && !url.username && !url.password && (url.protocol === "https:" || url.protocol === "http:" && ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname));
|
|
1323
|
+
} catch {
|
|
1324
|
+
return false;
|
|
1325
|
+
}
|
|
1326
|
+
}, "Use an exact HTTPS origin, or an HTTP localhost origin for development");
|
|
1327
|
+
var webAppConfigSchema = z.object({
|
|
1328
|
+
allowedOrigins: z.array(webOriginSchema).max(30).transform((values) => [...new Set(values)])
|
|
1329
|
+
}).strict();
|
|
1330
|
+
|
|
1331
|
+
// src/types/portal.ts
|
|
1332
|
+
var PORTAL_RESERVED_SLUGS = [
|
|
1333
|
+
"www",
|
|
1334
|
+
"app",
|
|
1335
|
+
"api",
|
|
1336
|
+
"admin",
|
|
1337
|
+
"auth",
|
|
1338
|
+
"portal",
|
|
1339
|
+
"docs",
|
|
1340
|
+
"status",
|
|
1341
|
+
"mail",
|
|
1342
|
+
"support",
|
|
1343
|
+
"login",
|
|
1344
|
+
"logout",
|
|
1345
|
+
"signup",
|
|
1346
|
+
"billing",
|
|
1347
|
+
"dashboard",
|
|
1348
|
+
"cdn",
|
|
1349
|
+
"assets",
|
|
1350
|
+
"static",
|
|
1351
|
+
"smtp",
|
|
1352
|
+
"ftp",
|
|
1353
|
+
"help",
|
|
1354
|
+
"account",
|
|
1355
|
+
"accounts",
|
|
1356
|
+
"developer"
|
|
1357
|
+
];
|
|
1358
|
+
var PORTAL_SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]{1,46})[a-z0-9]$/;
|
|
1359
|
+
function validPortalSlug(slug) {
|
|
1360
|
+
return PORTAL_SLUG_PATTERN.test(slug) && !PORTAL_RESERVED_SLUGS.includes(slug);
|
|
1361
|
+
}
|
|
1362
|
+
function suggestPortalSlug(name) {
|
|
1363
|
+
const value = name.toLowerCase().normalize("NFKD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 48).replace(/-$/, "");
|
|
1364
|
+
return validPortalSlug(value) ? value : value.length >= 3 ? `${value.slice(0, 40)}-team` : "";
|
|
1365
|
+
}
|
|
1366
|
+
function buildPortalUrl(slug, baseDomain = "usergist.com", appSlug) {
|
|
1367
|
+
if (!validPortalSlug(slug) || !/^[a-z0-9.-]+$/i.test(baseDomain)) throw new Error("Invalid portal address");
|
|
1368
|
+
const base = `https://${slug}.${baseDomain}`;
|
|
1369
|
+
return appSlug ? `${base}/${encodeURIComponent(appSlug)}/requests` : base;
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
// src/schemas/portal.ts
|
|
1373
|
+
var portalSlugSchema = z.string().refine(
|
|
1374
|
+
validPortalSlug,
|
|
1375
|
+
"Use 3\u201348 lowercase letters, numbers, or interior hyphens. This name must not be reserved."
|
|
1376
|
+
);
|
|
1377
|
+
var updatePortalSchema = z.object({
|
|
1378
|
+
displayName: z.string().trim().min(1).max(100).optional(),
|
|
1379
|
+
slug: portalSlugSchema.optional(),
|
|
1380
|
+
setupDismissed: z.boolean().optional()
|
|
1381
|
+
}).strict();
|
|
1382
|
+
var updatePortalAppSchema = z.object({ slug: portalSlugSchema, enabled: z.boolean() }).strict();
|
|
1383
|
+
var portalVisibilitySchema = z.object({ ids: z.array(z.string().uuid()).min(1).max(100), visible: z.boolean() }).strict();
|
|
1384
|
+
var portalEmailSchema = z.string().trim().email().max(254).transform((value) => value.toLowerCase());
|
|
1385
|
+
var portalAuthStartSchema = z.object({ email: portalEmailSchema }).strict();
|
|
1386
|
+
var portalAuthVerifySchema = z.object({ email: portalEmailSchema, code: z.string().regex(/^\d{6}$/) }).strict();
|
|
1387
|
+
var portalSubmissionSchema = z.object({
|
|
1388
|
+
title: z.string().trim().min(1).max(120),
|
|
1389
|
+
description: z.string().trim().min(1).max(1500),
|
|
1390
|
+
idempotencyKey: z.string().uuid(),
|
|
1391
|
+
feedbackConsent: z.literal(true)
|
|
1392
|
+
}).strict();
|
|
1393
|
+
var portalVoteSchema = z.object({ vote: z.boolean(), feedbackConsent: z.literal(true) }).strict();
|
|
1394
|
+
var portalRequestQuerySchema = z.object({
|
|
1395
|
+
q: z.string().trim().max(200).optional(),
|
|
1396
|
+
status: z.enum(["under_review", "planned", "in_progress", "shipped", "declined"]).optional(),
|
|
1397
|
+
sort: z.enum(["top", "newest", "status_changed"]).default("top"),
|
|
1398
|
+
page: z.coerce.number().int().min(1).max(1e5).default(1),
|
|
1399
|
+
limit: z.coerce.number().int().min(1).max(100).default(20)
|
|
1400
|
+
}).strict();
|
|
1401
|
+
|
|
1402
|
+
// src/schemas/apps.ts
|
|
1403
|
+
var workspaceTimezoneSchema = z.string().trim().min(1).max(64).refine(isValidIanaTimeZone, "Enter a valid IANA timezone");
|
|
1404
|
+
var platformSchema = z.enum([
|
|
1405
|
+
"ios",
|
|
1406
|
+
"android",
|
|
1407
|
+
"react-native",
|
|
1408
|
+
"expo",
|
|
1409
|
+
"flutter",
|
|
1410
|
+
"web"
|
|
1411
|
+
]);
|
|
1412
|
+
var writeKeyEnvironmentSchema = z.enum([
|
|
1413
|
+
"production",
|
|
1414
|
+
"staging",
|
|
1415
|
+
"development"
|
|
1416
|
+
]);
|
|
1417
|
+
var onboardingGoalSchema = z.enum([
|
|
1418
|
+
"feedback",
|
|
1419
|
+
"survey",
|
|
1420
|
+
"inapp",
|
|
1421
|
+
"push",
|
|
1422
|
+
"requests"
|
|
1423
|
+
]);
|
|
1424
|
+
var onboardingStatusSchema = z.enum([
|
|
1425
|
+
"in_progress",
|
|
1426
|
+
"deferred",
|
|
1427
|
+
"completed"
|
|
1428
|
+
]);
|
|
1429
|
+
var onboardingStepSchema = z.enum([
|
|
1430
|
+
"connect",
|
|
1431
|
+
"verify",
|
|
1432
|
+
"experience",
|
|
1433
|
+
"push",
|
|
1434
|
+
"launch"
|
|
1435
|
+
]);
|
|
1436
|
+
var onboardingPushChoiceSchema = z.enum([
|
|
1437
|
+
"pending",
|
|
1438
|
+
"configured",
|
|
1439
|
+
"skipped"
|
|
1440
|
+
]);
|
|
1441
|
+
var apiTokenScopeSchema = z.enum([
|
|
1442
|
+
"sdk:subjects",
|
|
1443
|
+
"push.transactional",
|
|
1444
|
+
"users.properties.write"
|
|
1445
|
+
]);
|
|
1053
1446
|
var createAppSchema = z.object({
|
|
1054
1447
|
name: z.string().min(1).max(120),
|
|
1055
1448
|
slug: slugSchema.optional(),
|
|
1056
1449
|
platforms: z.array(platformSchema).min(1).max(8),
|
|
1057
|
-
environment: writeKeyEnvironmentSchema.default("production")
|
|
1450
|
+
environment: writeKeyEnvironmentSchema.default("production"),
|
|
1451
|
+
onboardingGoal: onboardingGoalSchema.optional(),
|
|
1452
|
+
setupMode: z.enum(["sdk", "portal"]).optional(),
|
|
1453
|
+
portal: z.object({
|
|
1454
|
+
appSlug: portalSlugSchema,
|
|
1455
|
+
company: z.object({
|
|
1456
|
+
displayName: z.string().trim().min(1).max(100),
|
|
1457
|
+
slug: portalSlugSchema
|
|
1458
|
+
}).strict().optional()
|
|
1459
|
+
}).strict().optional(),
|
|
1460
|
+
webConfig: webAppConfigSchema.optional()
|
|
1461
|
+
});
|
|
1462
|
+
var updateOnboardingSchema = z.object({
|
|
1463
|
+
action: z.enum([
|
|
1464
|
+
"resume",
|
|
1465
|
+
"defer",
|
|
1466
|
+
"create_first_inapp",
|
|
1467
|
+
"first_inapp_completed",
|
|
1468
|
+
"create_first_feedback",
|
|
1469
|
+
"first_feedback_completed",
|
|
1470
|
+
"push_configured",
|
|
1471
|
+
"push_skipped",
|
|
1472
|
+
"complete"
|
|
1473
|
+
]).optional(),
|
|
1474
|
+
step: onboardingStepSchema.optional(),
|
|
1475
|
+
question: z.string().trim().min(1).max(500).optional(),
|
|
1476
|
+
message: z.object({
|
|
1477
|
+
title: z.string().trim().min(1).max(200),
|
|
1478
|
+
body: z.string().trim().min(1).max(500),
|
|
1479
|
+
buttonLabel: z.string().trim().min(1).max(40),
|
|
1480
|
+
format: z.enum(["modal", "slideup"])
|
|
1481
|
+
}).strict().optional()
|
|
1482
|
+
}).refine((value) => Boolean(value.action || value.step), {
|
|
1483
|
+
message: "Provide an onboarding action or step"
|
|
1484
|
+
}).refine(
|
|
1485
|
+
(value) => value.action !== "create_first_feedback" || Boolean(value.question),
|
|
1486
|
+
{ message: "Provide a question for the first feedback experience" }
|
|
1487
|
+
);
|
|
1488
|
+
var deferCurrentUserOnboardingSchema = z.object({
|
|
1489
|
+
action: z.literal("defer")
|
|
1058
1490
|
});
|
|
1059
1491
|
var updateAppSchema = z.object({
|
|
1060
1492
|
name: z.string().min(1).max(120).optional(),
|
|
1061
1493
|
platforms: z.array(platformSchema).min(1).max(8).optional(),
|
|
1062
1494
|
piiAllowList: z.array(z.string().min(1).max(120)).max(128).optional(),
|
|
1063
|
-
lifecycleEventsEnabled: z.boolean().optional()
|
|
1064
|
-
|
|
1495
|
+
lifecycleEventsEnabled: z.boolean().optional(),
|
|
1496
|
+
webConfig: webAppConfigSchema.optional()
|
|
1497
|
+
}).refine((v) => Object.keys(v).length > 0, {
|
|
1498
|
+
message: "At least one field is required"
|
|
1499
|
+
});
|
|
1065
1500
|
var createWriteKeySchema = z.object({
|
|
1066
1501
|
label: z.string().min(1).max(120).optional(),
|
|
1067
1502
|
environment: writeKeyEnvironmentSchema.default("production")
|
|
@@ -1076,8 +1511,15 @@ var createApiTokenSchema = z.object({
|
|
|
1076
1511
|
expiresInDays: z.number().int().min(1).max(365).default(90)
|
|
1077
1512
|
});
|
|
1078
1513
|
var createWorkspaceSchema = z.object({
|
|
1079
|
-
name: z.string().min(1).max(120),
|
|
1080
|
-
slug: slugSchema.optional()
|
|
1514
|
+
name: z.string().trim().min(1).max(120),
|
|
1515
|
+
slug: slugSchema.optional(),
|
|
1516
|
+
timezone: workspaceTimezoneSchema.default("UTC")
|
|
1517
|
+
});
|
|
1518
|
+
var updateWorkspaceSchema = z.object({
|
|
1519
|
+
name: z.string().trim().min(1).max(120).optional(),
|
|
1520
|
+
timezone: workspaceTimezoneSchema.optional()
|
|
1521
|
+
}).strict().refine((value) => Object.keys(value).length > 0, {
|
|
1522
|
+
message: "At least one field is required"
|
|
1081
1523
|
});
|
|
1082
1524
|
var inviteMemberSchema = z.object({
|
|
1083
1525
|
email: emailSchema,
|
|
@@ -1087,6 +1529,714 @@ var acceptWorkspaceInviteSchema = z.object({
|
|
|
1087
1529
|
token: z.string().regex(/^[a-f0-9]{64}$/i, "Invalid invitation token")
|
|
1088
1530
|
});
|
|
1089
1531
|
|
|
1090
|
-
|
|
1532
|
+
// src/types/portal-content.ts
|
|
1533
|
+
function emptyDocumentDraft() {
|
|
1534
|
+
return {
|
|
1535
|
+
title: "",
|
|
1536
|
+
summary: "",
|
|
1537
|
+
body: { type: "doc", content: [{ type: "paragraph" }] },
|
|
1538
|
+
collectionId: null,
|
|
1539
|
+
category: "improved",
|
|
1540
|
+
releaseDate: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
|
|
1541
|
+
version: "",
|
|
1542
|
+
links: []
|
|
1543
|
+
};
|
|
1544
|
+
}
|
|
1545
|
+
function contentText(node2) {
|
|
1546
|
+
return node2.text ?? (node2.content ?? []).map(contentText).join(node2.type === "paragraph" || node2.type === "heading" ? "" : "\n");
|
|
1547
|
+
}
|
|
1548
|
+
function contentAssetIds(node2) {
|
|
1549
|
+
return [
|
|
1550
|
+
.../* @__PURE__ */ new Set([
|
|
1551
|
+
...node2.type === "image" && node2.attrs?.assetId ? [node2.attrs.assetId] : [],
|
|
1552
|
+
...(node2.content ?? []).flatMap(contentAssetIds)
|
|
1553
|
+
])
|
|
1554
|
+
];
|
|
1555
|
+
}
|
|
1556
|
+
function contentSlug(title) {
|
|
1557
|
+
return title.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 96).replace(/-$/, "") || "article";
|
|
1558
|
+
}
|
|
1559
|
+
function safeContentHref(href) {
|
|
1560
|
+
return /^(https?:\/\/|mailto:|\/[^/]|#[a-zA-Z0-9_-])/.test(href) && !/[\u0000-\u0020\\]/.test(href);
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1563
|
+
// src/schemas/portal-content.ts
|
|
1564
|
+
var uuid = z.string().uuid();
|
|
1565
|
+
var contentKindSchema = z.enum(["article", "changelog"]);
|
|
1566
|
+
var contentStateSchema = z.enum(["draft", "published", "archived"]);
|
|
1567
|
+
var roadmapStatusSchema = z.enum(["planned", "in_progress", "shipped"]);
|
|
1568
|
+
var contentLinkSchema = z.object({ kind: z.enum(["request", "roadmap"]), id: uuid }).strict();
|
|
1569
|
+
var mark = z.object({
|
|
1570
|
+
type: z.enum(["bold", "italic", "code", "link"]),
|
|
1571
|
+
attrs: z.object({ href: z.string().max(2048).refine(safeContentHref, "Use a safe link URL") }).strict().optional()
|
|
1572
|
+
}).strict().refine((m) => m.type === "link" ? Boolean(m.attrs) : !m.attrs);
|
|
1573
|
+
var node = z.lazy(
|
|
1574
|
+
() => z.object({
|
|
1575
|
+
type: z.enum([
|
|
1576
|
+
"doc",
|
|
1577
|
+
"paragraph",
|
|
1578
|
+
"heading",
|
|
1579
|
+
"text",
|
|
1580
|
+
"bulletList",
|
|
1581
|
+
"orderedList",
|
|
1582
|
+
"listItem",
|
|
1583
|
+
"blockquote",
|
|
1584
|
+
"codeBlock",
|
|
1585
|
+
"hardBreak",
|
|
1586
|
+
"image"
|
|
1587
|
+
]),
|
|
1588
|
+
text: z.string().max(1e5).optional(),
|
|
1589
|
+
attrs: z.object({
|
|
1590
|
+
level: z.number().int().min(2).max(3).optional(),
|
|
1591
|
+
start: z.number().int().min(1).max(1e4).optional(),
|
|
1592
|
+
language: z.string().max(40).nullable().optional(),
|
|
1593
|
+
assetId: uuid.optional(),
|
|
1594
|
+
alt: z.string().max(500).optional()
|
|
1595
|
+
}).strict().optional(),
|
|
1596
|
+
marks: z.array(mark).max(4).optional(),
|
|
1597
|
+
content: z.array(node).max(5e3).optional()
|
|
1598
|
+
}).strict()
|
|
1599
|
+
);
|
|
1600
|
+
var contentBodySchema = z.unknown().superRefine((value, ctx) => {
|
|
1601
|
+
let count = 0, invalid = false;
|
|
1602
|
+
const walk = (v, depth) => {
|
|
1603
|
+
if (depth > 20 || ++count > 5e3) {
|
|
1604
|
+
invalid = true;
|
|
1605
|
+
return;
|
|
1606
|
+
}
|
|
1607
|
+
if (v && typeof v === "object" && "content" in v && Array.isArray(v.content))
|
|
1608
|
+
for (const child of v.content) {
|
|
1609
|
+
if (invalid) break;
|
|
1610
|
+
walk(child, depth + 1);
|
|
1611
|
+
}
|
|
1612
|
+
};
|
|
1613
|
+
walk(value, 0);
|
|
1614
|
+
if (invalid)
|
|
1615
|
+
ctx.addIssue({
|
|
1616
|
+
code: z.ZodIssueCode.custom,
|
|
1617
|
+
message: "Document is too deeply nested or contains too many blocks"
|
|
1618
|
+
});
|
|
1619
|
+
}).pipe(node).superRefine((value, ctx) => {
|
|
1620
|
+
if (value.type !== "doc" || contentText(value).length > 1e5)
|
|
1621
|
+
ctx.addIssue({
|
|
1622
|
+
code: z.ZodIssueCode.custom,
|
|
1623
|
+
message: "Invalid document or body exceeds 100,000 characters"
|
|
1624
|
+
});
|
|
1625
|
+
const walk = (v, root = false) => {
|
|
1626
|
+
const blocks = ["paragraph", "heading", "bulletList", "orderedList", "blockquote", "codeBlock", "image"];
|
|
1627
|
+
const children = v.content ?? [];
|
|
1628
|
+
const allowed = v.type === "doc" || v.type === "blockquote" ? blocks : v.type === "listItem" ? blocks.filter((t) => t !== "heading") : v.type === "bulletList" || v.type === "orderedList" ? ["listItem"] : v.type === "paragraph" || v.type === "heading" ? ["text", "hardBreak"] : v.type === "codeBlock" ? ["text"] : [];
|
|
1629
|
+
const attrs = Object.keys(v.attrs ?? {});
|
|
1630
|
+
const allowedAttrs = v.type === "heading" ? ["level"] : v.type === "orderedList" ? ["start"] : v.type === "codeBlock" ? ["language"] : v.type === "image" ? ["assetId", "alt"] : [];
|
|
1631
|
+
if (!root && v.type === "doc" || v.type === "text" && (!v.text || v.content) || v.type !== "text" && (v.text !== void 0 || Boolean(v.marks?.length)) || v.type === "image" && (!v.attrs?.assetId || v.content) || v.type === "heading" && !v.attrs?.level || attrs.some((a) => !allowedAttrs.includes(a)) || children.some((c) => !allowed.includes(c.type)) || v.type === "listItem" && children[0]?.type !== "paragraph" || ["bulletList", "orderedList", "blockquote", "doc"].includes(v.type) && !children.length || v.type === "codeBlock" && children.some((c) => c.marks?.length))
|
|
1632
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "Invalid content block" });
|
|
1633
|
+
v.content?.forEach((c) => walk(c));
|
|
1634
|
+
};
|
|
1635
|
+
walk(value, true);
|
|
1636
|
+
});
|
|
1637
|
+
var documentDraftSchema = z.object({
|
|
1638
|
+
title: z.string().trim().max(160),
|
|
1639
|
+
summary: z.string().max(280),
|
|
1640
|
+
body: contentBodySchema,
|
|
1641
|
+
collectionId: uuid.nullable(),
|
|
1642
|
+
category: z.enum(["new", "improved", "fixed"]),
|
|
1643
|
+
releaseDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).refine(
|
|
1644
|
+
(v) => !Number.isNaN(Date.parse(v)) && new Date(v).toISOString().slice(0, 10) === v,
|
|
1645
|
+
"Choose a valid date"
|
|
1646
|
+
),
|
|
1647
|
+
version: z.string().max(40),
|
|
1648
|
+
links: z.array(contentLinkSchema).max(100)
|
|
1649
|
+
}).strict();
|
|
1650
|
+
var createDocumentSchema = z.object({
|
|
1651
|
+
idempotencyKey: uuid,
|
|
1652
|
+
title: z.string().trim().max(160).optional(),
|
|
1653
|
+
collectionId: uuid.nullable().optional(),
|
|
1654
|
+
links: z.array(contentLinkSchema).max(100).optional()
|
|
1655
|
+
}).strict();
|
|
1656
|
+
var saveDocumentSchema = z.object({ revision: z.number().int().positive(), draft: documentDraftSchema }).strict();
|
|
1657
|
+
var contentActionSchema = z.object({
|
|
1658
|
+
revision: z.number().int().positive(),
|
|
1659
|
+
action: z.enum(["publish", "unpublish", "archive", "restore"])
|
|
1660
|
+
}).strict();
|
|
1661
|
+
var contentQuerySchema = z.object({
|
|
1662
|
+
q: z.string().trim().max(200).optional(),
|
|
1663
|
+
state: contentStateSchema.optional(),
|
|
1664
|
+
collectionId: uuid.optional(),
|
|
1665
|
+
category: z.enum(["new", "improved", "fixed"]).optional(),
|
|
1666
|
+
cursor: z.string().max(2e3).optional(),
|
|
1667
|
+
limit: z.coerce.number().int().min(1).max(50).default(20)
|
|
1668
|
+
}).strict();
|
|
1669
|
+
var createCollectionSchema = z.object({
|
|
1670
|
+
title: z.string().trim().min(1).max(160),
|
|
1671
|
+
description: z.string().max(280),
|
|
1672
|
+
idempotencyKey: uuid
|
|
1673
|
+
}).strict();
|
|
1674
|
+
var contentPageQuerySchema = contentQuerySchema.pick({ cursor: true, limit: true });
|
|
1675
|
+
var portalAssetUploadKeySchema = uuid;
|
|
1676
|
+
var updateCollectionSchema = z.object({
|
|
1677
|
+
revision: z.number().int().positive(),
|
|
1678
|
+
title: z.string().trim().min(1).max(160).optional(),
|
|
1679
|
+
description: z.string().max(280).optional(),
|
|
1680
|
+
archived: z.boolean().optional()
|
|
1681
|
+
}).strict();
|
|
1682
|
+
var reorderContentSchema = z.object({ revision: z.number().int().positive(), direction: z.enum(["up", "down"]) }).strict();
|
|
1683
|
+
var roadmapQuerySchema = z.object({
|
|
1684
|
+
status: roadmapStatusSchema,
|
|
1685
|
+
cursor: z.string().max(2e3).optional(),
|
|
1686
|
+
limit: z.coerce.number().int().min(1).max(50).default(20),
|
|
1687
|
+
archived: z.enum(["true", "false"]).transform((v) => v === "true").optional()
|
|
1688
|
+
}).strict();
|
|
1689
|
+
var roadmapFields = {
|
|
1690
|
+
title: z.string().trim().min(1).max(120),
|
|
1691
|
+
description: z.string().max(1500),
|
|
1692
|
+
requestIds: z.array(uuid).max(100).refine((v) => new Set(v).size === v.length)
|
|
1693
|
+
};
|
|
1694
|
+
var createRoadmapSchema = z.object({ ...roadmapFields, idempotencyKey: uuid, status: roadmapStatusSchema }).strict();
|
|
1695
|
+
var saveRoadmapSchema = z.object({ ...roadmapFields, revision: z.number().int().positive() }).strict();
|
|
1696
|
+
var moveRoadmapSchema = z.object({
|
|
1697
|
+
revision: z.number().int().positive(),
|
|
1698
|
+
status: roadmapStatusSchema,
|
|
1699
|
+
requests: z.array(z.object({ id: uuid, updatedAt: z.string().datetime() }).strict()).max(100).refine((v) => new Set(v.map((r) => r.id)).size === v.length)
|
|
1700
|
+
}).strict();
|
|
1701
|
+
var contentLinkQuerySchema = z.object({
|
|
1702
|
+
kind: z.enum(["request", "roadmap"]),
|
|
1703
|
+
q: z.string().trim().max(200).optional(),
|
|
1704
|
+
cursor: z.string().max(2e3).optional(),
|
|
1705
|
+
limit: z.coerce.number().int().min(1).max(100).default(20),
|
|
1706
|
+
ids: z.string().max(4e3).optional().refine((v) => !v || v.split(",").every((id2) => uuid.safeParse(id2).success), "Invalid linked IDs")
|
|
1707
|
+
}).strict();
|
|
1708
|
+
|
|
1709
|
+
// src/types/mcp.ts
|
|
1710
|
+
var MCP_DOMAINS = ["context", "users", "analytics", "requests", "roadmap", "content", "experiences", "segments", "brand", "portal", "push", "integrations", "sdk", "apps"];
|
|
1711
|
+
var MCP_ACCESS_MODES = ["read_only", "approve_changes", "automatic"];
|
|
1712
|
+
var MCP_CAPABILITIES = [
|
|
1713
|
+
"context:read",
|
|
1714
|
+
"users:read",
|
|
1715
|
+
"analytics:read",
|
|
1716
|
+
"requests:read",
|
|
1717
|
+
"requests:manage",
|
|
1718
|
+
"requests:publish",
|
|
1719
|
+
"roadmap:read",
|
|
1720
|
+
"roadmap:manage",
|
|
1721
|
+
"roadmap:publish",
|
|
1722
|
+
"content:read",
|
|
1723
|
+
"content:manage",
|
|
1724
|
+
"content:publish",
|
|
1725
|
+
"experiences:read",
|
|
1726
|
+
"experiences:manage",
|
|
1727
|
+
"experiences:publish",
|
|
1728
|
+
"segments:read",
|
|
1729
|
+
"segments:manage",
|
|
1730
|
+
"brand:read",
|
|
1731
|
+
"brand:manage",
|
|
1732
|
+
"portal:read",
|
|
1733
|
+
"portal:manage",
|
|
1734
|
+
"portal:publish",
|
|
1735
|
+
"push:read",
|
|
1736
|
+
"push:manage",
|
|
1737
|
+
"push:send",
|
|
1738
|
+
"integrations:read",
|
|
1739
|
+
"integrations:manage",
|
|
1740
|
+
"integrations:send",
|
|
1741
|
+
"sdk:read",
|
|
1742
|
+
"sdk:manage",
|
|
1743
|
+
"apps:read",
|
|
1744
|
+
"apps:manage"
|
|
1745
|
+
];
|
|
1746
|
+
var MCP_DEFAULT_CAPABILITIES = [
|
|
1747
|
+
"context:read",
|
|
1748
|
+
"users:read",
|
|
1749
|
+
"analytics:read",
|
|
1750
|
+
"requests:read",
|
|
1751
|
+
"roadmap:read",
|
|
1752
|
+
"content:read",
|
|
1753
|
+
"experiences:read",
|
|
1754
|
+
"segments:read",
|
|
1755
|
+
"brand:read",
|
|
1756
|
+
"portal:read",
|
|
1757
|
+
"push:read",
|
|
1758
|
+
"sdk:read",
|
|
1759
|
+
"apps:read",
|
|
1760
|
+
"requests:manage",
|
|
1761
|
+
"roadmap:manage",
|
|
1762
|
+
"content:manage",
|
|
1763
|
+
"experiences:manage",
|
|
1764
|
+
"segments:manage"
|
|
1765
|
+
];
|
|
1766
|
+
|
|
1767
|
+
// src/schemas/mcp.ts
|
|
1768
|
+
var mcpCapabilitySchema = z.string().refine((value) => MCP_CAPABILITIES.includes(value), "Unknown MCP capability");
|
|
1769
|
+
var mcpGrantSchema = z.object({
|
|
1770
|
+
name: z.string().trim().min(1).max(100).default("AI connection"),
|
|
1771
|
+
appIds: z.array(z.string().uuid()).min(1).max(100).refine((v) => new Set(v).size === v.length, "Duplicate app IDs"),
|
|
1772
|
+
capabilities: z.array(mcpCapabilitySchema).max(56).default([...MCP_DEFAULT_CAPABILITIES]),
|
|
1773
|
+
mode: z.enum(MCP_ACCESS_MODES).default("approve_changes")
|
|
1774
|
+
}).strict();
|
|
1775
|
+
var mcpConsentSchema = mcpGrantSchema.extend({
|
|
1776
|
+
workspaceId: z.string().uuid(),
|
|
1777
|
+
externalAuthId: z.string().min(10).max(256).regex(/^[a-zA-Z0-9_-]+$/)
|
|
1778
|
+
});
|
|
1779
|
+
var mcpUpdateConnectionSchema = mcpGrantSchema.extend({ policyVersion: z.number().int().positive() });
|
|
1780
|
+
var subjectRefSchema = z.union([
|
|
1781
|
+
z.object({ subjectId: z.string().uuid() }).strict(),
|
|
1782
|
+
z.object({ externalId: z.string().min(1).max(256) }).strict(),
|
|
1783
|
+
z.object({ anonymousId: z.string().min(1).max(256) }).strict()
|
|
1784
|
+
]);
|
|
1785
|
+
var mcpPageSchema = z.object({ cursor: z.string().max(4096).optional(), limit: z.number().int().min(1).max(100).default(25) });
|
|
1786
|
+
var mcpSearchSchema = mcpPageSchema.extend({
|
|
1787
|
+
query: z.string().trim().min(1).max(500),
|
|
1788
|
+
types: z.array(z.enum(["request", "feedback_answer", "survey_answer", "roadmap", "article", "changelog"])).max(6).optional(),
|
|
1789
|
+
variant: z.enum(["draft", "published", "record"]).optional()
|
|
1790
|
+
});
|
|
1791
|
+
|
|
1792
|
+
// src/types/web.ts
|
|
1793
|
+
var DELIVERY_PROTOCOL_VERSION = 2;
|
|
1794
|
+
function deliveryPlatformsForApp(platforms) {
|
|
1795
|
+
const values = /* @__PURE__ */ new Set();
|
|
1796
|
+
for (const platform of platforms) {
|
|
1797
|
+
if (platform === "web" || platform === "ios" || platform === "android") values.add(platform);
|
|
1798
|
+
if (platform === "expo" || platform === "react-native" || platform === "flutter") {
|
|
1799
|
+
values.add("ios");
|
|
1800
|
+
values.add("android");
|
|
1801
|
+
}
|
|
1802
|
+
}
|
|
1803
|
+
return [...values];
|
|
1804
|
+
}
|
|
1805
|
+
function resolveWebPresentation(pillar, override) {
|
|
1806
|
+
return {
|
|
1807
|
+
layout: override?.layout ?? "modal",
|
|
1808
|
+
size: override?.size ?? (pillar === "requests" ? "wide" : "standard"),
|
|
1809
|
+
position: override?.position ?? "right",
|
|
1810
|
+
backdrop: override?.backdrop ?? override?.layout !== "card"
|
|
1811
|
+
};
|
|
1812
|
+
}
|
|
1813
|
+
var id = z.string().regex(/^[A-Za-z][A-Za-z0-9_]{0,63}$/);
|
|
1814
|
+
var key = z.string().min(1).max(120).refine(
|
|
1815
|
+
(v) => !["__proto__", "prototype", "constructor"].includes(v),
|
|
1816
|
+
"Reserved property key"
|
|
1817
|
+
);
|
|
1818
|
+
var scalar = z.union([
|
|
1819
|
+
z.string().max(8192),
|
|
1820
|
+
z.number().finite(),
|
|
1821
|
+
z.boolean(),
|
|
1822
|
+
z.null()
|
|
1823
|
+
]);
|
|
1824
|
+
var baseSource = { id, label: z.string().min(1).max(120) };
|
|
1825
|
+
var filterSchema = z.object({
|
|
1826
|
+
key,
|
|
1827
|
+
op: z.enum([
|
|
1828
|
+
"eq",
|
|
1829
|
+
"neq",
|
|
1830
|
+
"gt",
|
|
1831
|
+
"gte",
|
|
1832
|
+
"lt",
|
|
1833
|
+
"lte",
|
|
1834
|
+
"in",
|
|
1835
|
+
"nin",
|
|
1836
|
+
"contains",
|
|
1837
|
+
"starts_with",
|
|
1838
|
+
"exists",
|
|
1839
|
+
"not_exists"
|
|
1840
|
+
]),
|
|
1841
|
+
value: z.union([
|
|
1842
|
+
z.string(),
|
|
1843
|
+
z.number().finite(),
|
|
1844
|
+
z.boolean(),
|
|
1845
|
+
z.array(z.union([z.string(), z.number().finite()])).max(100)
|
|
1846
|
+
]).optional()
|
|
1847
|
+
});
|
|
1848
|
+
var personalizationSpecSchema = z.object({
|
|
1849
|
+
version: z.literal(1),
|
|
1850
|
+
sources: z.array(
|
|
1851
|
+
z.discriminatedUnion("kind", [
|
|
1852
|
+
z.object({ ...baseSource, kind: z.literal("user_property") }),
|
|
1853
|
+
z.object({ ...baseSource, kind: z.literal("trigger_event") }),
|
|
1854
|
+
z.object({ ...baseSource, kind: z.literal("send_data") }),
|
|
1855
|
+
z.object({ ...baseSource, kind: z.literal("app") }),
|
|
1856
|
+
z.object({ ...baseSource, kind: z.literal("now") }),
|
|
1857
|
+
z.object({
|
|
1858
|
+
...baseSource,
|
|
1859
|
+
kind: z.literal("latest_event"),
|
|
1860
|
+
eventName: z.string().min(1).max(120),
|
|
1861
|
+
lookbackDays: z.number().int().min(1).max(90),
|
|
1862
|
+
filters: z.array(filterSchema).max(8).optional()
|
|
1863
|
+
})
|
|
1864
|
+
])
|
|
1865
|
+
).max(16),
|
|
1866
|
+
bindings: z.array(
|
|
1867
|
+
z.object({
|
|
1868
|
+
id,
|
|
1869
|
+
label: z.string().min(1).max(120),
|
|
1870
|
+
sourceId: id,
|
|
1871
|
+
key,
|
|
1872
|
+
type: z.enum(["string", "number", "boolean", "date"]),
|
|
1873
|
+
fallback: scalar.optional()
|
|
1874
|
+
})
|
|
1875
|
+
).max(64),
|
|
1876
|
+
missingData: z.literal("skip")
|
|
1877
|
+
}).superRefine((spec, ctx) => {
|
|
1878
|
+
const sources = new Set(spec.sources.map((source) => source.id));
|
|
1879
|
+
if (sources.size !== spec.sources.length)
|
|
1880
|
+
ctx.addIssue({
|
|
1881
|
+
code: "custom",
|
|
1882
|
+
path: ["sources"],
|
|
1883
|
+
message: "Source IDs must be unique"
|
|
1884
|
+
});
|
|
1885
|
+
if (spec.sources.filter((source) => source.kind === "latest_event").length > 4)
|
|
1886
|
+
ctx.addIssue({
|
|
1887
|
+
code: "custom",
|
|
1888
|
+
path: ["sources"],
|
|
1889
|
+
message: "Use at most four activity sources"
|
|
1890
|
+
});
|
|
1891
|
+
const bindings = /* @__PURE__ */ new Set();
|
|
1892
|
+
spec.bindings.forEach((binding, index) => {
|
|
1893
|
+
if (bindings.has(binding.id))
|
|
1894
|
+
ctx.addIssue({
|
|
1895
|
+
code: "custom",
|
|
1896
|
+
path: ["bindings", index, "id"],
|
|
1897
|
+
message: "Field IDs must be unique"
|
|
1898
|
+
});
|
|
1899
|
+
bindings.add(binding.id);
|
|
1900
|
+
if (!sources.has(binding.sourceId))
|
|
1901
|
+
ctx.addIssue({
|
|
1902
|
+
code: "custom",
|
|
1903
|
+
path: ["bindings", index, "sourceId"],
|
|
1904
|
+
message: "Choose an existing source"
|
|
1905
|
+
});
|
|
1906
|
+
if (binding.fallback !== void 0 && (isMissing(binding.fallback) || !matchesType(binding.fallback, binding.type)))
|
|
1907
|
+
ctx.addIssue({
|
|
1908
|
+
code: "custom",
|
|
1909
|
+
path: ["bindings", index, "fallback"],
|
|
1910
|
+
message: "Fallback must be a non-empty value of the selected type"
|
|
1911
|
+
});
|
|
1912
|
+
});
|
|
1913
|
+
});
|
|
1914
|
+
var userPropertiesUpdateSchema = z.object({
|
|
1915
|
+
mutationId: z.string().uuid(),
|
|
1916
|
+
set: z.record(key, scalar).refine(
|
|
1917
|
+
(v) => Object.keys(v).length <= 64,
|
|
1918
|
+
"Update at most 64 properties"
|
|
1919
|
+
).optional(),
|
|
1920
|
+
unset: z.array(key).max(64).optional()
|
|
1921
|
+
}).superRefine((update, ctx) => {
|
|
1922
|
+
if (!Object.keys(update.set ?? {}).length && !update.unset?.length)
|
|
1923
|
+
ctx.addIssue({
|
|
1924
|
+
code: "custom",
|
|
1925
|
+
message: "Supply properties to set or remove"
|
|
1926
|
+
});
|
|
1927
|
+
if (update.unset?.some((name) => Object.hasOwn(update.set ?? {}, name)))
|
|
1928
|
+
ctx.addIssue({
|
|
1929
|
+
code: "custom",
|
|
1930
|
+
message: "A property cannot be set and removed in the same update"
|
|
1931
|
+
});
|
|
1932
|
+
});
|
|
1933
|
+
function isMissing(value) {
|
|
1934
|
+
return value == null || typeof value === "string" && !value.trim();
|
|
1935
|
+
}
|
|
1936
|
+
function matchesType(value, type) {
|
|
1937
|
+
if (type === "date")
|
|
1938
|
+
return typeof value === "string" && !Number.isNaN(Date.parse(value));
|
|
1939
|
+
return typeof value === type && (typeof value !== "number" || Number.isFinite(value));
|
|
1940
|
+
}
|
|
1941
|
+
function resolvePersonalizationBindings(spec, sources) {
|
|
1942
|
+
const values = /* @__PURE__ */ Object.create(null);
|
|
1943
|
+
const issues = [];
|
|
1944
|
+
const fallbackBindingIds = [];
|
|
1945
|
+
for (const binding of spec.bindings) {
|
|
1946
|
+
const source = sources[binding.sourceId]?.values;
|
|
1947
|
+
const value = source && Object.hasOwn(source, binding.key) ? source[binding.key] : void 0;
|
|
1948
|
+
const missing = isMissing(value);
|
|
1949
|
+
if (!missing && matchesType(value, binding.type))
|
|
1950
|
+
values[binding.id] = value;
|
|
1951
|
+
else if (missing && binding.fallback !== void 0) {
|
|
1952
|
+
values[binding.id] = binding.fallback;
|
|
1953
|
+
fallbackBindingIds.push(binding.id);
|
|
1954
|
+
} else
|
|
1955
|
+
issues.push({
|
|
1956
|
+
code: missing ? "missing" : "type_mismatch",
|
|
1957
|
+
bindingId: binding.id,
|
|
1958
|
+
message: missing ? `${binding.label} is missing` : `${binding.label} must be ${binding.type}`
|
|
1959
|
+
});
|
|
1960
|
+
}
|
|
1961
|
+
return {
|
|
1962
|
+
status: issues.length ? "skipped" : fallbackBindingIds.length ? "using_fallback" : "ready",
|
|
1963
|
+
values,
|
|
1964
|
+
fallbackBindingIds,
|
|
1965
|
+
issues,
|
|
1966
|
+
sources
|
|
1967
|
+
};
|
|
1968
|
+
}
|
|
1969
|
+
var TOKEN = /\{\{\s*p\.([A-Za-z][A-Za-z0-9_]*)\s*\}\}/g;
|
|
1970
|
+
var WHOLE_TOKEN = /^\{\{\s*p\.([A-Za-z][A-Za-z0-9_]*)\s*\}\}$/;
|
|
1971
|
+
function personalizationToken(bindingId) {
|
|
1972
|
+
return `{{p.${bindingId}}}`;
|
|
1973
|
+
}
|
|
1974
|
+
function renderPersonalizedValue(input, values, issues, path = "", mode = "text") {
|
|
1975
|
+
if (typeof input === "string") {
|
|
1976
|
+
const whole = WHOLE_TOKEN.exec(input);
|
|
1977
|
+
const lookup = (bindingId) => {
|
|
1978
|
+
if (Object.hasOwn(values, bindingId)) return values[bindingId];
|
|
1979
|
+
issues.push({
|
|
1980
|
+
code: "unknown_binding",
|
|
1981
|
+
bindingId,
|
|
1982
|
+
path,
|
|
1983
|
+
message: `No value is available for ${bindingId}`
|
|
1984
|
+
});
|
|
1985
|
+
return null;
|
|
1986
|
+
};
|
|
1987
|
+
if (mode === "json" && whole) return lookup(whole[1]);
|
|
1988
|
+
if (/\{\{|\}\}/.test(input.replace(TOKEN, "")))
|
|
1989
|
+
issues.push({
|
|
1990
|
+
code: "invalid_template",
|
|
1991
|
+
path,
|
|
1992
|
+
message: "Insert a valid dynamic field"
|
|
1993
|
+
});
|
|
1994
|
+
const result = input.replace(TOKEN, (_, bindingId) => {
|
|
1995
|
+
const value = lookup(bindingId);
|
|
1996
|
+
return mode === "url" && !whole ? encodeURIComponent(String(value ?? "")) : String(value ?? "");
|
|
1997
|
+
});
|
|
1998
|
+
if (mode === "url" && result) {
|
|
1999
|
+
try {
|
|
2000
|
+
const url = new URL(result);
|
|
2001
|
+
if (["javascript:", "data:", "file:", "vbscript:"].includes(url.protocol))
|
|
2002
|
+
throw new Error("unsafe");
|
|
2003
|
+
if (!whole && /\{\{/.test(input.split(/:\/\//)[0] ?? ""))
|
|
2004
|
+
throw new Error("dynamic scheme");
|
|
2005
|
+
if (!whole && /\{\{/.test(
|
|
2006
|
+
input.match(/^[a-z][a-z0-9+.-]*:\/\/([^/?#]*)/i)?.[1] ?? ""
|
|
2007
|
+
))
|
|
2008
|
+
throw new Error("dynamic authority");
|
|
2009
|
+
} catch {
|
|
2010
|
+
issues.push({
|
|
2011
|
+
code: "invalid_destination",
|
|
2012
|
+
path,
|
|
2013
|
+
message: "The resolved destination must be a valid app link or web URL"
|
|
2014
|
+
});
|
|
2015
|
+
}
|
|
2016
|
+
}
|
|
2017
|
+
return result;
|
|
2018
|
+
}
|
|
2019
|
+
if (Array.isArray(input))
|
|
2020
|
+
return input.map(
|
|
2021
|
+
(item, index) => renderPersonalizedValue(item, values, issues, `${path}/${index}`, mode)
|
|
2022
|
+
);
|
|
2023
|
+
if (input && typeof input === "object") {
|
|
2024
|
+
const result = /* @__PURE__ */ Object.create(null);
|
|
2025
|
+
for (const [name, value] of Object.entries(input)) {
|
|
2026
|
+
if (["__proto__", "constructor", "prototype"].includes(name)) {
|
|
2027
|
+
issues.push({
|
|
2028
|
+
code: "invalid_template",
|
|
2029
|
+
path,
|
|
2030
|
+
message: "Reserved object key"
|
|
2031
|
+
});
|
|
2032
|
+
continue;
|
|
2033
|
+
}
|
|
2034
|
+
result[name] = renderPersonalizedValue(
|
|
2035
|
+
value,
|
|
2036
|
+
values,
|
|
2037
|
+
issues,
|
|
2038
|
+
`${path}/${name}`,
|
|
2039
|
+
mode
|
|
2040
|
+
);
|
|
2041
|
+
}
|
|
2042
|
+
return result;
|
|
2043
|
+
}
|
|
2044
|
+
return input;
|
|
2045
|
+
}
|
|
2046
|
+
var TEXT_KEYS = /* @__PURE__ */ new Set([
|
|
2047
|
+
"title",
|
|
2048
|
+
"body",
|
|
2049
|
+
"subtitle",
|
|
2050
|
+
"headline",
|
|
2051
|
+
"label",
|
|
2052
|
+
"placeholder",
|
|
2053
|
+
"followUp",
|
|
2054
|
+
"lowLabel",
|
|
2055
|
+
"highLabel",
|
|
2056
|
+
"description",
|
|
2057
|
+
"buttonText",
|
|
2058
|
+
"submitLabel",
|
|
2059
|
+
"nextLabel",
|
|
2060
|
+
"backLabel",
|
|
2061
|
+
"text"
|
|
2062
|
+
]);
|
|
2063
|
+
var URL_KEYS = /* @__PURE__ */ new Set(["imageUrl", "deepLink", "target"]);
|
|
2064
|
+
var CONTENT_KEYS = /* @__PURE__ */ new Set([
|
|
2065
|
+
"questions",
|
|
2066
|
+
"options",
|
|
2067
|
+
"ctas",
|
|
2068
|
+
"cta",
|
|
2069
|
+
"actionButtons",
|
|
2070
|
+
"flow",
|
|
2071
|
+
"welcome",
|
|
2072
|
+
"thankYou",
|
|
2073
|
+
"completion",
|
|
2074
|
+
"endScreen",
|
|
2075
|
+
"openAction"
|
|
2076
|
+
]);
|
|
2077
|
+
function personalizationUsage(content) {
|
|
2078
|
+
const usage = /* @__PURE__ */ new Map();
|
|
2079
|
+
const scan = (value, field) => {
|
|
2080
|
+
if (typeof value === "string") {
|
|
2081
|
+
for (const match of value.matchAll(TOKEN)) {
|
|
2082
|
+
const fields = usage.get(match[1]) ?? /* @__PURE__ */ new Set();
|
|
2083
|
+
fields.add(field);
|
|
2084
|
+
usage.set(match[1], fields);
|
|
2085
|
+
}
|
|
2086
|
+
} else if (Array.isArray(value)) value.forEach((item) => scan(item, field));
|
|
2087
|
+
else if (value && typeof value === "object")
|
|
2088
|
+
Object.values(value).forEach((item) => scan(item, field));
|
|
2089
|
+
};
|
|
2090
|
+
const walk = (value) => {
|
|
2091
|
+
if (Array.isArray(value)) {
|
|
2092
|
+
value.forEach(walk);
|
|
2093
|
+
return;
|
|
2094
|
+
}
|
|
2095
|
+
if (!value || typeof value !== "object") return;
|
|
2096
|
+
for (const [key2, item] of Object.entries(value)) {
|
|
2097
|
+
if (TEXT_KEYS.has(key2) || URL_KEYS.has(key2) || key2 === "actionJson" || key2 === "payloadExtras")
|
|
2098
|
+
scan(item, key2);
|
|
2099
|
+
else if (CONTENT_KEYS.has(key2)) walk(item);
|
|
2100
|
+
}
|
|
2101
|
+
};
|
|
2102
|
+
walk(content);
|
|
2103
|
+
return usage;
|
|
2104
|
+
}
|
|
2105
|
+
function renderPersonalizedContent(content, resolution) {
|
|
2106
|
+
const usage = personalizationUsage(content);
|
|
2107
|
+
const onlyOptionalImage = (id2) => usage.get(id2)?.size === 1 && usage.get(id2)?.has("imageUrl");
|
|
2108
|
+
const issues = resolution.issues.filter(
|
|
2109
|
+
(issue) => !issue.bindingId || usage.has(issue.bindingId) && !(issue.code === "missing" && onlyOptionalImage(issue.bindingId))
|
|
2110
|
+
);
|
|
2111
|
+
const fallbacks = resolution.fallbackBindingIds.filter((id2) => usage.has(id2));
|
|
2112
|
+
const walk = (value, path = "") => {
|
|
2113
|
+
if (Array.isArray(value))
|
|
2114
|
+
return value.map((item, index) => walk(item, `${path}/${index}`));
|
|
2115
|
+
if (!value || typeof value !== "object") return value;
|
|
2116
|
+
return Object.fromEntries(
|
|
2117
|
+
Object.entries(value).map(([name, item]) => {
|
|
2118
|
+
const childPath = `${path}/${name}`;
|
|
2119
|
+
if (TEXT_KEYS.has(name) && typeof item === "string")
|
|
2120
|
+
return [
|
|
2121
|
+
name,
|
|
2122
|
+
renderPersonalizedValue(item, resolution.values, issues, childPath)
|
|
2123
|
+
];
|
|
2124
|
+
if (URL_KEYS.has(name) && typeof item === "string") {
|
|
2125
|
+
if (name === "imageUrl" && [...item.matchAll(TOKEN)].some(
|
|
2126
|
+
(match) => resolution.issues.some(
|
|
2127
|
+
(issue) => issue.bindingId === match[1] && issue.code === "missing"
|
|
2128
|
+
)
|
|
2129
|
+
))
|
|
2130
|
+
return [name, null];
|
|
2131
|
+
const isEvent = name === "target" && value.action === "custom_event";
|
|
2132
|
+
const rendered2 = renderPersonalizedValue(
|
|
2133
|
+
item,
|
|
2134
|
+
resolution.values,
|
|
2135
|
+
issues,
|
|
2136
|
+
childPath,
|
|
2137
|
+
isEvent ? "text" : "url"
|
|
2138
|
+
);
|
|
2139
|
+
if (name === "imageUrl" && rendered2 && !String(rendered2).startsWith("https://"))
|
|
2140
|
+
issues.push({
|
|
2141
|
+
code: "invalid_destination",
|
|
2142
|
+
path: childPath,
|
|
2143
|
+
message: "Image URLs must use HTTPS"
|
|
2144
|
+
});
|
|
2145
|
+
return [name, rendered2];
|
|
2146
|
+
}
|
|
2147
|
+
if (name === "actionJson" || name === "payloadExtras")
|
|
2148
|
+
return [
|
|
2149
|
+
name,
|
|
2150
|
+
renderPersonalizedValue(
|
|
2151
|
+
item,
|
|
2152
|
+
resolution.values,
|
|
2153
|
+
issues,
|
|
2154
|
+
childPath,
|
|
2155
|
+
"json"
|
|
2156
|
+
)
|
|
2157
|
+
];
|
|
2158
|
+
if (CONTENT_KEYS.has(name)) return [name, walk(item, childPath)];
|
|
2159
|
+
return [name, item];
|
|
2160
|
+
})
|
|
2161
|
+
);
|
|
2162
|
+
};
|
|
2163
|
+
const rendered = walk(content);
|
|
2164
|
+
return {
|
|
2165
|
+
content: rendered,
|
|
2166
|
+
resolution: {
|
|
2167
|
+
...resolution,
|
|
2168
|
+
fallbackBindingIds: fallbacks,
|
|
2169
|
+
status: issues.length ? "skipped" : fallbacks.length ? "using_fallback" : "ready",
|
|
2170
|
+
issues
|
|
2171
|
+
}
|
|
2172
|
+
};
|
|
2173
|
+
}
|
|
2174
|
+
|
|
2175
|
+
// src/presentation.ts
|
|
2176
|
+
var PresentationGate = class {
|
|
2177
|
+
paused;
|
|
2178
|
+
identity = 0;
|
|
2179
|
+
revisions = { feedback: 0, survey: 0 };
|
|
2180
|
+
listeners = /* @__PURE__ */ new Set();
|
|
2181
|
+
constructor(paused = false) {
|
|
2182
|
+
this.paused = paused;
|
|
2183
|
+
}
|
|
2184
|
+
get isPaused() {
|
|
2185
|
+
return this.paused;
|
|
2186
|
+
}
|
|
2187
|
+
setPaused(paused) {
|
|
2188
|
+
this.paused = paused;
|
|
2189
|
+
this.notify();
|
|
2190
|
+
}
|
|
2191
|
+
invalidate(purpose) {
|
|
2192
|
+
if (purpose) this.revisions[purpose]++;
|
|
2193
|
+
else this.identity++;
|
|
2194
|
+
this.notify();
|
|
2195
|
+
}
|
|
2196
|
+
validator(purpose) {
|
|
2197
|
+
const identity = this.identity;
|
|
2198
|
+
const revision = this.revisions[purpose];
|
|
2199
|
+
return () => identity === this.identity && revision === this.revisions[purpose];
|
|
2200
|
+
}
|
|
2201
|
+
subscribe(listener) {
|
|
2202
|
+
this.listeners.add(listener);
|
|
2203
|
+
return () => {
|
|
2204
|
+
this.listeners.delete(listener);
|
|
2205
|
+
};
|
|
2206
|
+
}
|
|
2207
|
+
runWhenReady(run, valid, cancel = () => {
|
|
2208
|
+
}) {
|
|
2209
|
+
if (!valid()) {
|
|
2210
|
+
cancel();
|
|
2211
|
+
return;
|
|
2212
|
+
}
|
|
2213
|
+
if (!this.paused) {
|
|
2214
|
+
run();
|
|
2215
|
+
return;
|
|
2216
|
+
}
|
|
2217
|
+
const stop = this.subscribe(() => {
|
|
2218
|
+
if (valid() && this.paused) return;
|
|
2219
|
+
stop();
|
|
2220
|
+
if (valid()) run();
|
|
2221
|
+
else cancel();
|
|
2222
|
+
});
|
|
2223
|
+
}
|
|
2224
|
+
waitUntilReady(valid) {
|
|
2225
|
+
if (!valid()) return Promise.resolve(false);
|
|
2226
|
+
if (!this.paused) return Promise.resolve(true);
|
|
2227
|
+
return new Promise((resolve) => {
|
|
2228
|
+
const stop = this.subscribe(() => {
|
|
2229
|
+
if (valid() && this.paused) return;
|
|
2230
|
+
stop();
|
|
2231
|
+
resolve(valid());
|
|
2232
|
+
});
|
|
2233
|
+
});
|
|
2234
|
+
}
|
|
2235
|
+
notify() {
|
|
2236
|
+
for (const listener of [...this.listeners]) listener();
|
|
2237
|
+
}
|
|
2238
|
+
};
|
|
2239
|
+
|
|
2240
|
+
export { APP_OPEN_EVENT_NAME, APP_VERSION_CHANGED_EVENT_NAME, AUDIENCE_JOIN_EVENT_NAME, AUDIENCE_JOIN_TRIGGER_KIND, BILLING_GRACE_DAYS, COMMERCIAL_BILLING_PLANS, CORE_INTEGRATION_EVENTS, DELIVERY_PROTOCOL_VERSION, INAPP_AUTO_DISMISSED_EVENT_NAME, INAPP_CTA_CLICKED_EVENT_NAME, INAPP_DISMISSED_EVENT_NAME, INAPP_SHOWN_EVENT_NAME, INTEGRATION_CATEGORIES, INTEGRATION_PROVIDERS, MCP_ACCESS_MODES, MCP_CAPABILITIES, MCP_DEFAULT_CAPABILITIES, MCP_DOMAINS, PORTAL_RESERVED_SLUGS, PORTAL_SLUG_PATTERN, PUSH_EVENTS, PresentationGate, RADIUS_PRESETS, RECIPIENTS_LIMIT, REQUEST_COMMENTED_EVENT_NAME, REQUEST_COMMENT_DELETED_EVENT_NAME, REQUEST_COMMENT_EDITED_EVENT_NAME, REQUEST_FOLLOWED_EVENT_NAME, REQUEST_RESPONDED_EVENT_NAME, REQUEST_STATUS_CHANGED_EVENT_NAME, REQUEST_SUBMITTED_EVENT_NAME, REQUEST_UNFOLLOWED_EVENT_NAME, REQUEST_UNUPVOTED_EVENT_NAME, REQUEST_UPVOTED_EVENT_NAME, STARTER_FOUNDING_CUSTOMER_LIMIT, STARTER_FOUNDING_OFFER_CODE, SURVEY_END_SENTINEL, THEME_PRESETS, TRIAL_APP_LIMIT, TRIAL_DAYS, USER_IDENTIFIED_EVENT_NAME, brandTokensFromPreset, buildPortalUrl, contentAssetIds, contentSlug, contentText, defaultEventTrigger, defaultOnboardingMessage, defaultThemePreset, defaultTriggerSpec, deliveryPlatformsForApp, emptyAudienceSpec, emptyDocumentDraft, emptySegmentDsl, endpoints, err, estimateProgress, evaluateBranchCondition, evaluateSegment, evaluateSerializedSegmentRules, findQuestion, findQuestionIndex, getCoreIntegrationEvent, getThemePresetById, inAppColorsFromBrandTokens, isSilentPushPayload, isValidIanaTimeZone, nextPeriodicFire, nextQuestionId, ok, personalizationSpecSchema, personalizationToken, personalizationUsage, promptThemeFromBrandTokens, reachableQuestions, renderPersonalizedContent, renderPersonalizedValue, requestAccentFromBrandTokens, resolvePersonalizationBindings, resolveWebPresentation, safeContentHref, sanitizeCoreIntegrationProperties, schemas_exports as schemas, serializedRulesToDsl, suggestPortalSlug, tzOffsetMinutes, userPropertiesUpdateSchema, validPortalSlug, zonedDateEndToUtc, zonedDateTimeParts, zonedDateTimeToUtc };
|
|
1091
2241
|
//# sourceMappingURL=index.js.map
|
|
1092
2242
|
//# sourceMappingURL=index.js.map
|