@segmentstream/app 0.0.0-stage → 0.38.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/LICENSE +201 -0
- package/NOTICE +2 -0
- package/README.md +1236 -2
- package/dist/agent.d.ts +56 -0
- package/dist/agent.js +203 -0
- package/dist/app.d.ts +82 -0
- package/dist/app.js +434 -0
- package/dist/browser-auth-capability.d.ts +2 -0
- package/dist/browser-auth-capability.js +2 -0
- package/dist/browser-revision.d.ts +19 -0
- package/dist/browser-revision.js +140 -0
- package/dist/channel-protocol.d.ts +16 -0
- package/dist/channel-protocol.js +17 -0
- package/dist/channel-session.d.ts +22 -0
- package/dist/channel-session.js +89 -0
- package/dist/config.d.ts +53 -0
- package/dist/config.js +112 -0
- package/dist/context.d.ts +10 -0
- package/dist/context.js +1 -0
- package/dist/cookies.d.ts +56 -0
- package/dist/cookies.js +131 -0
- package/dist/data.d.ts +233 -0
- package/dist/data.js +206 -0
- package/dist/decision-pages.d.ts +14 -0
- package/dist/decision-pages.js +87 -0
- package/dist/deployment.d.ts +30 -0
- package/dist/deployment.js +72 -0
- package/dist/errors.d.ts +60 -0
- package/dist/errors.js +123 -0
- package/dist/federation.d.ts +24 -0
- package/dist/federation.js +613 -0
- package/dist/groups.d.ts +95 -0
- package/dist/groups.js +280 -0
- package/dist/handlers.d.ts +10 -0
- package/dist/handlers.js +161 -0
- package/dist/index.d.ts +46 -0
- package/dist/index.js +29 -0
- package/dist/jobs.d.ts +49 -0
- package/dist/jobs.js +40 -0
- package/dist/legacy.d.ts +77 -0
- package/dist/legacy.js +57 -0
- package/dist/logo.d.ts +1 -0
- package/dist/logo.js +25 -0
- package/dist/me.d.ts +42 -0
- package/dist/me.js +54 -0
- package/dist/navigation/build.d.ts +68 -0
- package/dist/navigation/build.js +290 -0
- package/dist/navigation/cli.d.ts +19 -0
- package/dist/navigation/cli.js +37 -0
- package/dist/navigation/generate.d.ts +43 -0
- package/dist/navigation/generate.js +138 -0
- package/dist/navigation/next.d.ts +10 -0
- package/dist/navigation/next.js +18 -0
- package/dist/navigation-protocol.d.ts +55 -0
- package/dist/navigation-protocol.js +298 -0
- package/dist/neon.d.ts +101 -0
- package/dist/neon.js +539 -0
- package/dist/oauth-completion.d.ts +6 -0
- package/dist/oauth-completion.js +106 -0
- package/dist/oauth.d.ts +59 -0
- package/dist/oauth.js +129 -0
- package/dist/ontology-names.d.ts +10 -0
- package/dist/ontology-names.js +38 -0
- package/dist/ontology-transport.d.ts +34 -0
- package/dist/ontology-transport.js +130 -0
- package/dist/ontology-wire.d.ts +66 -0
- package/dist/ontology-wire.js +355 -0
- package/dist/ontology.d.ts +381 -0
- package/dist/ontology.js +367 -0
- package/dist/pages.d.ts +6 -0
- package/dist/pages.js +6 -0
- package/dist/parameters.d.ts +55 -0
- package/dist/parameters.js +118 -0
- package/dist/permissions.d.ts +19 -0
- package/dist/permissions.js +55 -0
- package/dist/pkce.d.ts +8 -0
- package/dist/pkce.js +40 -0
- package/dist/proxy.d.ts +25 -0
- package/dist/proxy.js +135 -0
- package/dist/request-timing.d.ts +6 -0
- package/dist/request-timing.js +53 -0
- package/dist/return-to.d.ts +2 -0
- package/dist/return-to.js +24 -0
- package/dist/server.d.ts +11 -0
- package/dist/server.js +80 -0
- package/dist/sessions.d.ts +27 -0
- package/dist/sessions.js +72 -0
- package/dist/sign-in.d.ts +37 -0
- package/dist/sign-in.js +95 -0
- package/dist/storage.d.ts +79 -0
- package/dist/storage.js +180 -0
- package/dist/telegram-launch.d.ts +7 -0
- package/dist/telegram-launch.js +134 -0
- package/dist/telegram.d.ts +2 -0
- package/dist/telegram.js +6 -0
- package/dist/tokens.d.ts +51 -0
- package/dist/tokens.js +70 -0
- package/dist/ui/agent-connect.d.ts +18 -0
- package/dist/ui/agent-connect.js +23 -0
- package/dist/ui/components.d.ts +27 -0
- package/dist/ui/components.js +71 -0
- package/dist/ui/hooks.d.ts +19 -0
- package/dist/ui/hooks.js +56 -0
- package/dist/ui/index.d.ts +22 -0
- package/dist/ui/index.js +15 -0
- package/dist/ui/navigation.d.ts +5 -0
- package/dist/ui/navigation.js +8 -0
- package/dist/ui/segmentstream-logo.d.ts +2 -0
- package/dist/ui/segmentstream-logo.js +7 -0
- package/dist/ui/sign-in-check.d.ts +14 -0
- package/dist/ui/sign-in-check.js +41 -0
- package/dist/ui/sign-in.d.ts +17 -0
- package/dist/ui/sign-in.js +65 -0
- package/dist/ui/store.d.ts +80 -0
- package/dist/ui/store.js +280 -0
- package/dist/ui/styles.css +249 -0
- package/dist/ui/telegram-sign-in.d.ts +16 -0
- package/dist/ui/telegram-sign-in.js +21 -0
- package/dist/ui/use-browser-sign-in.d.ts +52 -0
- package/dist/ui/use-browser-sign-in.js +169 -0
- package/dist/ui/use-channel-sign-in.d.ts +10 -0
- package/dist/ui/use-channel-sign-in.js +118 -0
- package/dist/ui/use-sign-in.d.ts +109 -0
- package/dist/ui/use-sign-in.js +304 -0
- package/package.json +95 -4
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
import { toCamelCase, toSnakeCase } from "./ontology-names.js";
|
|
2
|
+
const FILTER_OPERATORS = [
|
|
3
|
+
"eq",
|
|
4
|
+
"ne",
|
|
5
|
+
"in",
|
|
6
|
+
"isNull",
|
|
7
|
+
"contains",
|
|
8
|
+
"startsWith",
|
|
9
|
+
"gt",
|
|
10
|
+
"gte",
|
|
11
|
+
"lt",
|
|
12
|
+
"lte",
|
|
13
|
+
"between",
|
|
14
|
+
"has",
|
|
15
|
+
"hasAny",
|
|
16
|
+
];
|
|
17
|
+
const SYSTEM_FIELDS = ["id", "created_at", "updated_at", "created_by", "updated_by"];
|
|
18
|
+
/** The built-in entity of members and services, whose `roles` field lists role names. */
|
|
19
|
+
const USER = "user";
|
|
20
|
+
/** The generated `user` actions that take role names; each, with the others below, returns the record. */
|
|
21
|
+
const USER_ROLE_ACTIONS = ["invite", "grant_roles", "revoke_roles"];
|
|
22
|
+
const USER_RECORD_ACTIONS = [...USER_ROLE_ACTIONS, "deactivate", "reactivate"];
|
|
23
|
+
/** Whether a generated action of `user` answers with the updated user record. */
|
|
24
|
+
export function returnsUserRecord(entity, action) {
|
|
25
|
+
return entity === USER && USER_RECORD_ACTIONS.includes(action);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Role names are snake_case on the wire and camelCase in the app, as `requireUser().roles` has
|
|
29
|
+
* them; `convert` turns one, or each of a list, into the other.
|
|
30
|
+
*/
|
|
31
|
+
function roleNames(value, convert) {
|
|
32
|
+
if (typeof value === "string") {
|
|
33
|
+
return convert(value);
|
|
34
|
+
}
|
|
35
|
+
return Array.isArray(value)
|
|
36
|
+
? value.map((name) => (typeof name === "string" ? convert(name) : name))
|
|
37
|
+
: value;
|
|
38
|
+
}
|
|
39
|
+
function isRecord(value) {
|
|
40
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* A relationship with a warehouse-backed end is matched by value through `on`: it has no
|
|
44
|
+
* `<name>_id` field, and the ontology service cannot traverse or filter through it.
|
|
45
|
+
*/
|
|
46
|
+
function isValueMatched(relationship) {
|
|
47
|
+
return relationship.on !== undefined && relationship.on !== null;
|
|
48
|
+
}
|
|
49
|
+
/** Catalog metadata distinguishes relationship filters from opaque JSON property values. */
|
|
50
|
+
export async function encodeWhere(input) {
|
|
51
|
+
const { where, entity, getCatalog } = input;
|
|
52
|
+
if (where === undefined) {
|
|
53
|
+
return where;
|
|
54
|
+
}
|
|
55
|
+
const catalog = await getCatalog();
|
|
56
|
+
return whereFromCatalog({ where, entity, catalog });
|
|
57
|
+
}
|
|
58
|
+
function whereFromCatalog(input) {
|
|
59
|
+
const { where, entity, catalog } = input;
|
|
60
|
+
const definition = catalog.entities.find((candidate) => candidate.name === entity);
|
|
61
|
+
const encodedWhere = Object.create(null);
|
|
62
|
+
for (const [field, condition] of Object.entries(where)) {
|
|
63
|
+
if (field === "and" || field === "or") {
|
|
64
|
+
const conditions = condition;
|
|
65
|
+
encodedWhere[field] = conditions.map((child) => whereFromCatalog({ where: child, entity, catalog }));
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (field === "not") {
|
|
69
|
+
encodedWhere[field] = whereFromCatalog({ where: condition, entity, catalog });
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
const wireField = toSnakeCase(field);
|
|
73
|
+
const relationship = relationshipTarget({ field: wireField, entity, definition, catalog });
|
|
74
|
+
if (relationship !== undefined && isRecord(condition)) {
|
|
75
|
+
encodedWhere[wireField] = relationshipFilterToWire({ condition, relationship, catalog });
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
encodedWhere[wireField] =
|
|
79
|
+
entity === USER && wireField === "roles"
|
|
80
|
+
? rolesFilterToWire(condition)
|
|
81
|
+
: fieldFilterToWire({ field: wireField, condition, definition });
|
|
82
|
+
}
|
|
83
|
+
return encodedWhere;
|
|
84
|
+
}
|
|
85
|
+
function relationshipTarget(input) {
|
|
86
|
+
const { field, entity, definition, catalog } = input;
|
|
87
|
+
const forward = definition?.relationships.find((candidate) => candidate.name === field && !isValueMatched(candidate));
|
|
88
|
+
if (forward !== undefined) {
|
|
89
|
+
return { entity: forward.to, many: forward.cardinality === "many" };
|
|
90
|
+
}
|
|
91
|
+
for (const candidate of catalog.entities) {
|
|
92
|
+
const inverse = candidate.relationships.find((relationship) => relationship.to === entity &&
|
|
93
|
+
relationship.inverse === field &&
|
|
94
|
+
!isValueMatched(relationship));
|
|
95
|
+
if (inverse !== undefined) {
|
|
96
|
+
return { entity: candidate.name, many: true };
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
function relationshipFilterToWire(input) {
|
|
102
|
+
const { condition, relationship, catalog } = input;
|
|
103
|
+
const entity = relationship.entity;
|
|
104
|
+
if (!relationship.many) {
|
|
105
|
+
return whereFromCatalog({ where: condition, entity, catalog });
|
|
106
|
+
}
|
|
107
|
+
const encodedQuantifiers = Object.create(null);
|
|
108
|
+
for (const [quantifier, child] of Object.entries(condition)) {
|
|
109
|
+
encodedQuantifiers[quantifier] = whereFromCatalog({ where: child, entity, catalog });
|
|
110
|
+
}
|
|
111
|
+
return encodedQuantifiers;
|
|
112
|
+
}
|
|
113
|
+
function isGeneratedField(field, definition) {
|
|
114
|
+
if (SYSTEM_FIELDS.includes(field)) {
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
return (definition?.relationships.some((relationship) => relationship.cardinality === "one" &&
|
|
118
|
+
!isValueMatched(relationship) &&
|
|
119
|
+
`${relationship.name}_id` === field) ?? false);
|
|
120
|
+
}
|
|
121
|
+
function fieldFilterToWire(input) {
|
|
122
|
+
const { field, condition, definition } = input;
|
|
123
|
+
const property = definition?.properties.find((candidate) => candidate.name === field);
|
|
124
|
+
if (!isRecord(condition)) {
|
|
125
|
+
return selectToWire(condition, property);
|
|
126
|
+
}
|
|
127
|
+
if (property === undefined && !isGeneratedField(field, definition)) {
|
|
128
|
+
return condition;
|
|
129
|
+
}
|
|
130
|
+
const operatorFilter = Object.keys(condition).every((operator) => FILTER_OPERATORS.includes(operator));
|
|
131
|
+
if (property?.type === "json" && !operatorFilter) {
|
|
132
|
+
return condition;
|
|
133
|
+
}
|
|
134
|
+
const encodedFilter = Object.create(null);
|
|
135
|
+
for (const [operator, operand] of Object.entries(condition)) {
|
|
136
|
+
encodedFilter[operatorToWire(operator)] = selectToWire(operand, property);
|
|
137
|
+
}
|
|
138
|
+
return encodedFilter;
|
|
139
|
+
}
|
|
140
|
+
function rolesFilterToWire(condition) {
|
|
141
|
+
if (!isRecord(condition)) {
|
|
142
|
+
return roleNames(condition, toSnakeCase);
|
|
143
|
+
}
|
|
144
|
+
return Object.fromEntries(Object.entries(condition).map(([operator, operand]) => [
|
|
145
|
+
operatorToWire(operator),
|
|
146
|
+
operator === "isNull" ? operand : roleNames(operand, toSnakeCase),
|
|
147
|
+
]));
|
|
148
|
+
}
|
|
149
|
+
function operatorToWire(operator) {
|
|
150
|
+
if (operator === "isNull") {
|
|
151
|
+
return "is_null";
|
|
152
|
+
}
|
|
153
|
+
if (operator === "startsWith") {
|
|
154
|
+
return "starts_with";
|
|
155
|
+
}
|
|
156
|
+
if (operator === "hasAny") {
|
|
157
|
+
return "has_any";
|
|
158
|
+
}
|
|
159
|
+
return operator;
|
|
160
|
+
}
|
|
161
|
+
export function queryToWire(query) {
|
|
162
|
+
const { orderBy, where, ...rest } = query;
|
|
163
|
+
return {
|
|
164
|
+
...rest,
|
|
165
|
+
...(query.select === undefined ? {} : { select: query.select.map(toSnakeCase) }),
|
|
166
|
+
...(orderBy === undefined
|
|
167
|
+
? {}
|
|
168
|
+
: { order_by: orderBy.map((order) => ({ ...order, field: toSnakeCase(order.field) })) }),
|
|
169
|
+
...(query.include === undefined ? {} : { include: includeToWire(query.include) }),
|
|
170
|
+
...(where === undefined ? {} : { where }),
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* `include` with snake_case names, as an object: a list of names, `["assignedOwner"]`, becomes
|
|
175
|
+
* `{ assigned_owner: true }`, which every version of the ontology service accepts.
|
|
176
|
+
*/
|
|
177
|
+
export function includeToWire(include) {
|
|
178
|
+
const entries = isIncludeList(include)
|
|
179
|
+
? include.map((name) => [name, true])
|
|
180
|
+
: Object.entries(include);
|
|
181
|
+
return Object.fromEntries(entries.map(([name, value]) => [toSnakeCase(name), value]));
|
|
182
|
+
}
|
|
183
|
+
function isIncludeList(include) {
|
|
184
|
+
return Array.isArray(include);
|
|
185
|
+
}
|
|
186
|
+
function selectToWire(value, property) {
|
|
187
|
+
if (property?.type !== "select") {
|
|
188
|
+
return value;
|
|
189
|
+
}
|
|
190
|
+
const convert = (child) => typeof child === "string" &&
|
|
191
|
+
property.options?.some((option) => option.value === toSnakeCase(child))
|
|
192
|
+
? toSnakeCase(child)
|
|
193
|
+
: child;
|
|
194
|
+
return Array.isArray(value) ? value.map(convert) : convert(value);
|
|
195
|
+
}
|
|
196
|
+
/** Decode record fields and included relationships without descending into JSON properties. */
|
|
197
|
+
export function recordFromWire(value, entity, catalog) {
|
|
198
|
+
if (!isRecord(value)) {
|
|
199
|
+
return value;
|
|
200
|
+
}
|
|
201
|
+
const definition = catalog.entities.find((candidate) => candidate.name === entity);
|
|
202
|
+
return Object.fromEntries(Object.entries(value).map(([field, child]) => {
|
|
203
|
+
const property = definition?.properties.find((candidate) => candidate.name === field);
|
|
204
|
+
if (property?.type === "select" && typeof child === "string") {
|
|
205
|
+
return [toCamelCase(field), toCamelCase(child)];
|
|
206
|
+
}
|
|
207
|
+
if (entity === USER && field === "roles") {
|
|
208
|
+
return [field, roleNames(child, toCamelCase)];
|
|
209
|
+
}
|
|
210
|
+
const relationship = relationshipTarget({ field, entity, definition, catalog });
|
|
211
|
+
if (relationship !== undefined) {
|
|
212
|
+
const decoded = Array.isArray(child)
|
|
213
|
+
? child.map((record) => recordFromWire(record, relationship.entity, catalog))
|
|
214
|
+
: recordFromWire(child, relationship.entity, catalog);
|
|
215
|
+
return [toCamelCase(field), decoded];
|
|
216
|
+
}
|
|
217
|
+
return [toCamelCase(field), child];
|
|
218
|
+
}));
|
|
219
|
+
}
|
|
220
|
+
function schemaInputToWire(value, schema, definitions) {
|
|
221
|
+
if (schema.$ref?.startsWith("#/$defs/")) {
|
|
222
|
+
const target = definitions[schema.$ref.slice("#/$defs/".length)];
|
|
223
|
+
return target === undefined ? value : schemaInputToWire(value, target, definitions);
|
|
224
|
+
}
|
|
225
|
+
const alternatives = schema.anyOf ?? schema.oneOf;
|
|
226
|
+
if (alternatives !== undefined) {
|
|
227
|
+
const object = alternatives.find((candidate) => matchesObjectSchema(value, candidate, definitions));
|
|
228
|
+
const array = alternatives.find((candidate) => candidate.items !== undefined && Array.isArray(value));
|
|
229
|
+
const literals = alternatives.flatMap((candidate) => candidate.enum ?? (candidate.const === undefined ? [] : [candidate.const]));
|
|
230
|
+
if (typeof value === "string" && literals.includes(toSnakeCase(value))) {
|
|
231
|
+
return toSnakeCase(value);
|
|
232
|
+
}
|
|
233
|
+
const target = object ?? array;
|
|
234
|
+
return target === undefined ? value : schemaInputToWire(value, target, definitions);
|
|
235
|
+
}
|
|
236
|
+
if (typeof value === "string" &&
|
|
237
|
+
(schema.enum?.includes(toSnakeCase(value)) || schema.const === toSnakeCase(value))) {
|
|
238
|
+
return toSnakeCase(value);
|
|
239
|
+
}
|
|
240
|
+
if (Array.isArray(value) && schema.items !== undefined) {
|
|
241
|
+
return value.map((child) => schemaInputToWire(child, schema.items, definitions));
|
|
242
|
+
}
|
|
243
|
+
if (!isRecord(value)) {
|
|
244
|
+
return value;
|
|
245
|
+
}
|
|
246
|
+
if (schema.properties === undefined) {
|
|
247
|
+
const childSchema = schema.additionalProperties;
|
|
248
|
+
return typeof childSchema === "object"
|
|
249
|
+
? Object.fromEntries(Object.entries(value).map(([key, child]) => [
|
|
250
|
+
key,
|
|
251
|
+
schemaInputToWire(child, childSchema, definitions),
|
|
252
|
+
]))
|
|
253
|
+
: value;
|
|
254
|
+
}
|
|
255
|
+
return Object.fromEntries(Object.entries(value).map(([field, child]) => {
|
|
256
|
+
const wireField = toSnakeCase(field);
|
|
257
|
+
const childSchema = schema.properties?.[wireField];
|
|
258
|
+
return [
|
|
259
|
+
wireField,
|
|
260
|
+
childSchema === undefined ? child : schemaInputToWire(child, childSchema, definitions),
|
|
261
|
+
];
|
|
262
|
+
}));
|
|
263
|
+
}
|
|
264
|
+
function matchesObjectSchema(value, schema, definitions) {
|
|
265
|
+
if (schema.$ref?.startsWith("#/$defs/")) {
|
|
266
|
+
const target = definitions[schema.$ref.slice("#/$defs/".length)];
|
|
267
|
+
return (target !== undefined && target !== schema && matchesObjectSchema(value, target, definitions));
|
|
268
|
+
}
|
|
269
|
+
if (!isRecord(value) || schema.properties === undefined) {
|
|
270
|
+
return false;
|
|
271
|
+
}
|
|
272
|
+
if (schema.required?.some((key) => !Object.hasOwn(value, key) && !Object.hasOwn(value, toCamelCase(key)))) {
|
|
273
|
+
return false;
|
|
274
|
+
}
|
|
275
|
+
return Object.entries(schema.properties).every(([key, childSchema]) => {
|
|
276
|
+
if (childSchema.const === undefined) {
|
|
277
|
+
return true;
|
|
278
|
+
}
|
|
279
|
+
const child = value[key] ?? value[toCamelCase(key)];
|
|
280
|
+
return (child === childSchema.const ||
|
|
281
|
+
(typeof child === "string" && toSnakeCase(child) === childSchema.const));
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
export function actionInputToWire(given, entity, action, catalog) {
|
|
285
|
+
const definition = catalog.entities.find((candidate) => candidate.name === entity);
|
|
286
|
+
const authored = definition?.actions?.find((candidate) => candidate.name === action);
|
|
287
|
+
const input = entity === USER && USER_ROLE_ACTIONS.includes(action) && isRecord(given) && "roles" in given
|
|
288
|
+
? { ...given, roles: roleNames(given.roles, toSnakeCase) }
|
|
289
|
+
: given;
|
|
290
|
+
if (authored !== undefined) {
|
|
291
|
+
return schemaInputToWire(input, authored.input, authored.input.$defs ?? {});
|
|
292
|
+
}
|
|
293
|
+
if (!isRecord(input)) {
|
|
294
|
+
return input;
|
|
295
|
+
}
|
|
296
|
+
return Object.fromEntries(Object.entries(input).map(([field, child]) => {
|
|
297
|
+
const wireField = toSnakeCase(field);
|
|
298
|
+
const property = definition?.properties.find((candidate) => candidate.name === wireField);
|
|
299
|
+
return [
|
|
300
|
+
wireField,
|
|
301
|
+
property?.type === "blob" && isRecord(child)
|
|
302
|
+
? Object.fromEntries(Object.entries(child).map(([key, value]) => [toSnakeCase(key), value]))
|
|
303
|
+
: (action === "link" || action === "unlink") &&
|
|
304
|
+
field === "relationship" &&
|
|
305
|
+
typeof child === "string"
|
|
306
|
+
? toSnakeCase(child)
|
|
307
|
+
: selectToWire(child, property),
|
|
308
|
+
];
|
|
309
|
+
}));
|
|
310
|
+
}
|
|
311
|
+
function isPagedRoute(method, route) {
|
|
312
|
+
if (route.endsWith("/history")) {
|
|
313
|
+
return true;
|
|
314
|
+
}
|
|
315
|
+
if (method === "POST") {
|
|
316
|
+
return route.endsWith("/query");
|
|
317
|
+
}
|
|
318
|
+
const segments = route.split("/").filter(Boolean);
|
|
319
|
+
return segments.length === 1;
|
|
320
|
+
}
|
|
321
|
+
function historyEntryFromWire(item) {
|
|
322
|
+
const { record_id: recordId, action_id: actionId, principal_id: principalId, ...entry } = item;
|
|
323
|
+
return { ...entry, recordId, actionId, principalId };
|
|
324
|
+
}
|
|
325
|
+
export function responseFromWire(input) {
|
|
326
|
+
const { method, path, payload } = input;
|
|
327
|
+
if (!isRecord(payload)) {
|
|
328
|
+
return payload;
|
|
329
|
+
}
|
|
330
|
+
const route = path.split("?")[0] ?? "";
|
|
331
|
+
if (!isPagedRoute(method, route) || !Object.hasOwn(payload, "next_cursor")) {
|
|
332
|
+
return payload;
|
|
333
|
+
}
|
|
334
|
+
const { next_cursor: nextCursor, ...page } = payload;
|
|
335
|
+
if (!route.endsWith("/history") || !Array.isArray(page.items)) {
|
|
336
|
+
return { ...page, nextCursor };
|
|
337
|
+
}
|
|
338
|
+
const items = page.items.map(historyEntryFromWire);
|
|
339
|
+
return { ...page, nextCursor, items };
|
|
340
|
+
}
|
|
341
|
+
/** Decode current edits using the current catalog; stored historical changes retain their names. */
|
|
342
|
+
export function editsFromWire(edits, catalog) {
|
|
343
|
+
return edits.map((edit) => {
|
|
344
|
+
const before = recordFromWire(Object.fromEntries(Object.entries(edit.changes).map(([field, change]) => [field, change.before])), edit.entity, catalog);
|
|
345
|
+
const after = recordFromWire(Object.fromEntries(Object.entries(edit.changes).map(([field, change]) => [field, change.after])), edit.entity, catalog);
|
|
346
|
+
return {
|
|
347
|
+
...edit,
|
|
348
|
+
entity: toCamelCase(edit.entity),
|
|
349
|
+
changes: Object.fromEntries(Object.keys(before).map((field) => [
|
|
350
|
+
field,
|
|
351
|
+
{ before: before[field], after: after[field] },
|
|
352
|
+
])),
|
|
353
|
+
};
|
|
354
|
+
});
|
|
355
|
+
}
|