@segmentstream-cloud/app 0.4.0 → 0.5.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/README.md +8 -5
- package/dist/app.d.ts +1 -3
- package/dist/app.js +3 -5
- package/dist/data.d.ts +14 -23
- package/dist/data.js +7 -5
- package/dist/index.d.ts +2 -2
- package/dist/ontology.d.ts +19 -6
- package/dist/ontology.js +9 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -325,10 +325,9 @@ export default async function Page() {
|
|
|
325
325
|
- Values arrive as JSON: safe integers as numbers, larger integers and decimals as strings,
|
|
326
326
|
timestamps as ISO strings, records and arrays as JSON strings.
|
|
327
327
|
|
|
328
|
-
`segmentstream.
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
`app_credential_missing`.
|
|
328
|
+
`segmentstream.ontology.<entity>.aggregate(request)` uses the server-held workspace app
|
|
329
|
+
credential and supports Neon Auth apps. Stored record operations use the ontology user token.
|
|
330
|
+
`segmentstream.fetch(path)` still requires the legacy platform session.
|
|
332
331
|
|
|
333
332
|
## How requests are handled
|
|
334
333
|
|
|
@@ -387,7 +386,7 @@ Without `NEON_AUTH_BASE_URL`, 0.3 behaves exactly as 0.2, so existing apps keep
|
|
|
387
386
|
working: members of the workspace's organization sign in with their SegmentStream
|
|
388
387
|
account through `/api/segmentstream/[...path]/route.ts`
|
|
389
388
|
(`export const { GET, POST } = segmentstream.handlers();`), the proxy redirects to
|
|
390
|
-
SegmentStream, and `fetch
|
|
389
|
+
SegmentStream, and `fetch` acts as the member; warehouse queries and aggregates prefer the workspace app credential. In that mode
|
|
391
390
|
`roles` and `permissions` are empty, `can()` is `false`, `authHandlers()` answers
|
|
392
391
|
`404 neon_auth_not_configured`, and `segmentstream.ontology` sends the member's app
|
|
393
392
|
token, which the ontology service accepts while workspaces move to Neon Auth. Keep the
|
|
@@ -410,3 +409,7 @@ app word messages in its own language without matching the English sentences.
|
|
|
410
409
|
## License
|
|
411
410
|
|
|
412
411
|
Apache-2.0
|
|
412
|
+
|
|
413
|
+
## Upgrading to 0.5
|
|
414
|
+
|
|
415
|
+
Replace segmentstream.semantic with typed warehouse-backed entity aggregates. See [ontology aggregates](../../docs/ontology-aggregates.md). Warehouse-backed clients expose no record CRUD or actions.
|
package/dist/app.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Environment, SegmentStreamAppConfig } from "./config.js";
|
|
2
|
-
import type {
|
|
2
|
+
import type { WarehouseClient } from "./data.js";
|
|
3
3
|
import type { SegmentStreamRouteHandlers } from "./handlers.js";
|
|
4
4
|
import type { AuthRouteHandlers, CreateNeonAuth, SignOutOptions } from "./neon.js";
|
|
5
5
|
import type { OntologyClient } from "./ontology.js";
|
|
@@ -44,8 +44,6 @@ export interface SegmentStreamApp<O = unknown> {
|
|
|
44
44
|
* (`SEGMENTSTREAM_APP_CREDENTIAL`) when it is set, else the signed-in member's session.
|
|
45
45
|
*/
|
|
46
46
|
readonly warehouse: WarehouseClient;
|
|
47
|
-
/** Governed metrics from the workspace's deployed semantic models. Server-only. Legacy sign-in only. */
|
|
48
|
-
readonly semantic: SemanticClient;
|
|
49
47
|
/** The app's ontology service, `/api/ontology`, as the signed-in user. Server-only. */
|
|
50
48
|
readonly ontology: OntologyClient<O>;
|
|
51
49
|
}
|
package/dist/app.js
CHANGED
|
@@ -48,13 +48,12 @@ export function createApp(config, dependencies = {}) {
|
|
|
48
48
|
getAccessToken: legacy.getAccessToken,
|
|
49
49
|
fetch: data.fetch,
|
|
50
50
|
warehouse: data.warehouse,
|
|
51
|
-
semantic: data.semantic,
|
|
52
51
|
// The ontology service accepts SegmentStream app tokens while workspaces move to Neon Auth.
|
|
53
52
|
ontology: createOntologyClient(createOntologyTransport({
|
|
54
53
|
fetch: fetchImplementation,
|
|
55
54
|
ontologyUrl: ontologyUrl === undefined ? undefined : new URL(ontologyUrl).origin,
|
|
56
55
|
credentials: { token: legacy.getAccessToken },
|
|
57
|
-
})),
|
|
56
|
+
}), data.aggregateTransport),
|
|
58
57
|
};
|
|
59
58
|
}
|
|
60
59
|
const neon = createNeonSession({
|
|
@@ -82,8 +81,7 @@ export function createApp(config, dependencies = {}) {
|
|
|
82
81
|
getAccessToken: async () => null,
|
|
83
82
|
fetch: data.fetch,
|
|
84
83
|
warehouse: data.warehouse,
|
|
85
|
-
|
|
86
|
-
ontology: createOntologyClient(neon.ontologyTransport),
|
|
84
|
+
ontology: createOntologyClient(neon.ontologyTransport, data.aggregateTransport),
|
|
87
85
|
};
|
|
88
86
|
}
|
|
89
87
|
/** Under Neon Auth the app has no SegmentStream session to call the API with. */
|
|
@@ -91,7 +89,7 @@ async function noSegmentStreamSession() {
|
|
|
91
89
|
throw new SegmentStreamApiError({
|
|
92
90
|
code: "app_credential_missing",
|
|
93
91
|
status: 401,
|
|
94
|
-
detail: "Apps that sign in with Neon Auth have no SegmentStream session. Warehouse queries use the workspace app credential, SEGMENTSTREAM_APP_CREDENTIAL, which is not set; SegmentStream sets it on deploy. Other SegmentStream API calls are not available to these apps yet.",
|
|
92
|
+
detail: "Apps that sign in with Neon Auth have no SegmentStream session. Warehouse queries and ontology aggregates use the workspace app credential, SEGMENTSTREAM_APP_CREDENTIAL, which is not set; SegmentStream sets it on deploy. Other SegmentStream API calls are not available to these apps yet.",
|
|
95
93
|
});
|
|
96
94
|
}
|
|
97
95
|
function notConfiguredHandlers() {
|
package/dist/data.d.ts
CHANGED
|
@@ -43,38 +43,39 @@ export interface WarehouseDryRun {
|
|
|
43
43
|
readonly bytesProcessed: string | null;
|
|
44
44
|
readonly maximumBytesBilled: string;
|
|
45
45
|
}
|
|
46
|
-
export interface
|
|
47
|
-
readonly dimensions: readonly
|
|
48
|
-
readonly measures: readonly
|
|
49
|
-
readonly dateRange
|
|
46
|
+
export interface AggregateRequest<Property extends string = string, Measure extends string = string, Temporal extends Property = Property> {
|
|
47
|
+
readonly dimensions: readonly Property[];
|
|
48
|
+
readonly measures: readonly Measure[];
|
|
49
|
+
readonly dateRange?: {
|
|
50
50
|
readonly from: string;
|
|
51
51
|
readonly to: string;
|
|
52
52
|
};
|
|
53
|
+
readonly granularityOf?: Temporal;
|
|
53
54
|
readonly granularity?: "day" | "week" | "month";
|
|
54
55
|
readonly filters?: ReadonlyArray<{
|
|
55
|
-
readonly dimension:
|
|
56
|
+
readonly dimension: Property;
|
|
56
57
|
readonly operator: "equals" | "in" | "isNull" | "isNotNull";
|
|
57
58
|
readonly value?: string | number | boolean;
|
|
58
59
|
readonly values?: ReadonlyArray<string | number | boolean>;
|
|
59
60
|
}>;
|
|
60
61
|
readonly orderBy?: ReadonlyArray<{
|
|
61
|
-
readonly measure?:
|
|
62
|
-
readonly dimension?:
|
|
62
|
+
readonly measure?: Measure;
|
|
63
|
+
readonly dimension?: Property;
|
|
63
64
|
readonly direction: "asc" | "desc";
|
|
64
65
|
}>;
|
|
65
66
|
readonly limit?: number;
|
|
66
67
|
}
|
|
67
|
-
export interface
|
|
68
|
+
export interface AggregateField {
|
|
68
69
|
readonly name: string;
|
|
69
70
|
readonly label: string;
|
|
70
71
|
readonly type: string;
|
|
71
72
|
readonly unit?: string;
|
|
72
73
|
readonly format?: string;
|
|
73
74
|
}
|
|
74
|
-
export interface
|
|
75
|
+
export interface AggregateResult<Row = Record<string, string | boolean | null>> {
|
|
75
76
|
readonly deploymentId: string | null;
|
|
76
77
|
readonly definitionDigest: string;
|
|
77
|
-
readonly fields: readonly
|
|
78
|
+
readonly fields: readonly AggregateField[];
|
|
78
79
|
readonly rows: Row[];
|
|
79
80
|
readonly truncated: boolean;
|
|
80
81
|
readonly execution: {
|
|
@@ -94,18 +95,6 @@ export interface WarehouseClient {
|
|
|
94
95
|
/** Validates SQL and estimates bytes processed without running it. */
|
|
95
96
|
readonly dryRun: (sql: string, options?: Pick<WarehouseQueryOptions, "params" | "maximumBytesBilled" | "signal">) => Promise<WarehouseDryRun>;
|
|
96
97
|
}
|
|
97
|
-
export interface SemanticClient {
|
|
98
|
-
readonly list: () => Promise<{
|
|
99
|
-
readonly deploymentId: string | null;
|
|
100
|
-
readonly models: readonly unknown[];
|
|
101
|
-
}>;
|
|
102
|
-
readonly get: (model: string) => Promise<{
|
|
103
|
-
readonly deploymentId: string | null;
|
|
104
|
-
readonly definitionDigest: string;
|
|
105
|
-
readonly model: unknown;
|
|
106
|
-
}>;
|
|
107
|
-
readonly query: <Row = Record<string, string | boolean | null>>(model: string, request: SemanticQueryRequest) => Promise<SemanticQueryResult<Row>>;
|
|
108
|
-
}
|
|
109
98
|
export interface DataClientDependencies {
|
|
110
99
|
readonly config: ResolvedConfig;
|
|
111
100
|
readonly fetch: Fetch;
|
|
@@ -122,5 +111,7 @@ export interface DataClientDependencies {
|
|
|
122
111
|
export declare function createDataClient(dependencies: DataClientDependencies): {
|
|
123
112
|
fetch: (path: string, init?: RequestInit) => Promise<Response>;
|
|
124
113
|
warehouse: WarehouseClient;
|
|
125
|
-
|
|
114
|
+
aggregateTransport: {
|
|
115
|
+
fetch: (path: string, init: RequestInit) => Promise<Response>;
|
|
116
|
+
};
|
|
126
117
|
};
|
package/dist/data.js
CHANGED
|
@@ -154,12 +154,14 @@ export function createDataClient(dependencies) {
|
|
|
154
154
|
};
|
|
155
155
|
},
|
|
156
156
|
};
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
157
|
+
const aggregateTransport = {
|
|
158
|
+
fetch: (path, init) => {
|
|
159
|
+
if (!/^\/[a-z][a-z0-9_]*\/aggregate$/.test(path))
|
|
160
|
+
throw new TypeError("Invalid aggregate path.");
|
|
161
|
+
return authorizedFetch(`${workspace}/entities${path}`, init, warehouseToken);
|
|
162
|
+
},
|
|
161
163
|
};
|
|
162
|
-
return { fetch: apiFetch, warehouse,
|
|
164
|
+
return { fetch: apiFetch, warehouse, aggregateTransport };
|
|
163
165
|
}
|
|
164
166
|
function bytesLimit(value) {
|
|
165
167
|
if (value === undefined) {
|
package/dist/index.d.ts
CHANGED
|
@@ -22,9 +22,9 @@ export type { AuthenticationMethod, SegmentStreamApp } from "./app.js";
|
|
|
22
22
|
export type { ApiIssue } from "./errors.js";
|
|
23
23
|
export type { CurrentUser } from "./me.js";
|
|
24
24
|
export type { AuthRouteHandler, AuthRouteHandlers, SignOutOptions } from "./neon.js";
|
|
25
|
-
export type { ActionCall, ActionOptions, ActionResponse, Change, Edit, EditKind, EntityClient, FieldFilter, GeneratedActions, HistoryEntry, HistoryOptions, HistoryPage, JsonRecord, JsonValue, ManyFilter, OntologyClient, OntologyPage, OntologyQuery, OrderBy, RequestOptions, UntypedOntologyClient, ValidationIssue, Where, } from "./ontology.js";
|
|
25
|
+
export type { ActionCall, ActionOptions, ActionResponse, Change, Edit, EditKind, EntityClient, FieldFilter, GeneratedActions, HistoryEntry, HistoryOptions, HistoryPage, JsonRecord, JsonValue, ManyFilter, OntologyClient, AggregateEntityClient, OntologyPage, OntologyQuery, OrderBy, RequestOptions, UntypedOntologyClient, ValidationIssue, Where, } from "./ontology.js";
|
|
26
26
|
export type { PermissionHolder } from "./permissions.js";
|
|
27
|
-
export type {
|
|
27
|
+
export type { AggregateField, AggregateRequest, AggregateResult, WarehouseClient, WarehouseColumn, WarehouseDryRun, WarehouseQueryOptions, WarehouseReadOptions, WarehouseQueryResult, WarehouseRow, WarehouseValue, } from "./data.js";
|
|
28
28
|
export type { ParameterType, QueryParameterValue, QueryParameters, TypedParameter, } from "./parameters.js";
|
|
29
29
|
export type { SegmentStreamAppConfig } from "./config.js";
|
|
30
30
|
export type { SegmentStreamRouteHandlers, RouteHandler } from "./handlers.js";
|
package/dist/ontology.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { AggregateRequest, AggregateResult } from "./data.js";
|
|
1
2
|
/**
|
|
2
3
|
* The typed client for the app's ontology service, `/api/ontology`. The app imports its ontology as
|
|
3
4
|
* types only (`import type ontology from "../../ontology"`) and passes `typeof ontology` to
|
|
@@ -185,18 +186,30 @@ type ResultOf<A> = A extends {
|
|
|
185
186
|
type ActionClients<A> = {
|
|
186
187
|
readonly [K in keyof A & string]: ActionCall<InputOf<A[K]>, ResultOf<A[K]>>;
|
|
187
188
|
};
|
|
189
|
+
type PropertiesOf<E> = E extends {
|
|
190
|
+
readonly properties: infer P;
|
|
191
|
+
} ? P : never;
|
|
192
|
+
type MeasuresOf<E> = E extends {
|
|
193
|
+
readonly measures: infer M;
|
|
194
|
+
} ? keyof M & string : never;
|
|
195
|
+
export interface AggregateEntityClient<Property extends string = string, Measure extends string = string, Temporal extends Property = Property> {
|
|
196
|
+
readonly aggregate: (request: AggregateRequest<Property, Measure, Temporal>, options?: RequestOptions) => Promise<AggregateResult>;
|
|
197
|
+
}
|
|
198
|
+
type ClientOf<E> = E extends {
|
|
199
|
+
readonly definition: {
|
|
200
|
+
readonly source: object;
|
|
201
|
+
};
|
|
202
|
+
} ? AggregateEntityClient<keyof PropertiesOf<E> & string, MeasuresOf<E>> : EntityClient<RecordOf<E>, ActionClients<ActionsOf<E>>>;
|
|
188
203
|
type TypedOntologyClient<O> = {
|
|
189
|
-
readonly [N in NameOf<EntitiesOf<O>>]:
|
|
190
|
-
readonly name: N;
|
|
191
|
-
}>>, ActionClients<ActionsOf<Extract<EntitiesOf<O>, {
|
|
204
|
+
readonly [N in NameOf<EntitiesOf<O>>]: ClientOf<Extract<EntitiesOf<O>, {
|
|
192
205
|
readonly name: N;
|
|
193
|
-
}
|
|
206
|
+
}>>;
|
|
194
207
|
};
|
|
195
208
|
/** A client for any entity, when the app passes no ontology type. */
|
|
196
209
|
export type UntypedOntologyClient = {
|
|
197
210
|
readonly [entity: string]: EntityClient<JsonRecord, {
|
|
198
211
|
readonly [action: string]: ActionCall<JsonRecord, JsonValue>;
|
|
199
|
-
}
|
|
212
|
+
}> & AggregateEntityClient;
|
|
200
213
|
};
|
|
201
214
|
/**
|
|
202
215
|
* `segmentstream.ontology`: one client per entity of `O`, the type of the app's ontology
|
|
@@ -211,7 +224,7 @@ export interface OntologyTransport {
|
|
|
211
224
|
* Entities and actions exist only in the app's types, so the client resolves them by name when
|
|
212
225
|
* they are used: `ontology.lead.actions.changeStatus(…)` posts to `lead/actions/changeStatus`.
|
|
213
226
|
*/
|
|
214
|
-
export declare function createOntologyClient<O>(transport: OntologyTransport): OntologyClient<O>;
|
|
227
|
+
export declare function createOntologyClient<O>(transport: OntologyTransport, aggregateTransport?: OntologyTransport): OntologyClient<O>;
|
|
215
228
|
/** `find` sends the query as URL parameters: `where` as JSON, `orderBy` as `field:direction`. */
|
|
216
229
|
export declare function findParameters(query: OntologyQuery<JsonRecord>): URLSearchParams;
|
|
217
230
|
export {};
|
package/dist/ontology.js
CHANGED
|
@@ -19,7 +19,7 @@ const RESERVED = new Set([
|
|
|
19
19
|
* Entities and actions exist only in the app's types, so the client resolves them by name when
|
|
20
20
|
* they are used: `ontology.lead.actions.changeStatus(…)` posts to `lead/actions/changeStatus`.
|
|
21
21
|
*/
|
|
22
|
-
export function createOntologyClient(transport) {
|
|
22
|
+
export function createOntologyClient(transport, aggregateTransport) {
|
|
23
23
|
const entities = new Map();
|
|
24
24
|
return new Proxy(Object.create(null), {
|
|
25
25
|
get(_target, property) {
|
|
@@ -28,7 +28,14 @@ export function createOntologyClient(transport) {
|
|
|
28
28
|
}
|
|
29
29
|
let client = entities.get(property);
|
|
30
30
|
if (client === undefined) {
|
|
31
|
-
client =
|
|
31
|
+
client = {
|
|
32
|
+
...entityClient(property, transport),
|
|
33
|
+
aggregate: (request, options = {}) => {
|
|
34
|
+
if (!aggregateTransport)
|
|
35
|
+
throw new TypeError("An aggregate transport is required.");
|
|
36
|
+
return send(aggregateTransport, "POST", `/${property}/aggregate`, request, options.signal);
|
|
37
|
+
},
|
|
38
|
+
};
|
|
32
39
|
entities.set(property, client);
|
|
33
40
|
}
|
|
34
41
|
return client;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@segmentstream-cloud/app",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Sign-in, users, permissions, the ontology client and warehouse data access for SegmentStream workspace apps built with Next.js.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "SegmentStream",
|