@trylight/app 0.1.3 → 0.1.4
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 +2 -2
- package/dist/client.d.ts +169 -2
- package/dist/client.js +15 -2
- package/dist/platform.js +1 -1
- package/dist/server/entities.js +2 -2
- package/dist/server/env.d.ts +2 -2
- package/dist/server/env.js +2 -2
- package/dist/server/index.d.ts +2 -2
- package/dist/server/index.js +4 -4
- package/dist/server/session.d.ts +1 -1
- package/dist/server/session.js +4 -4
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ unchanged; entities and the schema sync are new here.
|
|
|
8
8
|
| Import | What |
|
|
9
9
|
|---|---|
|
|
10
10
|
| `@trylight/app/schema` | `entity(name, columns, access)`: a Drizzle table plus `id`, `created_at`, `updated_at`, `created_by`, `field_sources`; `lightChanges` (the audit table); `lightImages` (the app's copy of its logos and photos); `fieldSource` (the `{url, evidence}` schema) |
|
|
11
|
-
| `@trylight/app` | `createApp<typeof contracts>()` → `app.table(entity)` (`list/get/create/update/delete`, `useList/useGet/useCreate/useUpdate/useDelete`, typed by the entity passed), `app.contracts` (`useInvocation`), and `imageSrc(url)` (where a logo or photo loads from) |
|
|
11
|
+
| `@trylight/app` | `createApp<typeof contracts>()` → `app.table(entity)` (`list/get/create/update/delete`, `useList/useGet/useCreate/useUpdate/useDelete`, typed by the entity passed; `useList(...).rows` is never undefined), `app.contracts` (`useInvocation`), and `imageSrc(url)` (where a logo or photo loads from) |
|
|
12
12
|
| `@trylight/app/server` | `createLightServer({ schema, contracts })` → `light.db` (Drizzle), `light.session(request)`, `light.signIn(email, password)`, and the route handlers `light.entities`, `light.image`, `light.invoke.handler`, `light.chat` |
|
|
13
13
|
| `@trylight/app/platform` | the door for server functions, framing, analytics/error policies, the pane bridge |
|
|
14
14
|
| `@trylight/app/vite` | `lightDb()`: the dev server syncs `db/schema` into the database on every change |
|
|
@@ -70,7 +70,7 @@ fails while `db/schema` has changes no migration holds.
|
|
|
70
70
|
`src/db/audit.ts`, in Postgres itself. Every entity table has a row trigger
|
|
71
71
|
that records each insert, update, delete and truncate in `light_changes` (the
|
|
72
72
|
new row, `{ column: { from, to } }`, or the old row), whoever wrote it: the
|
|
73
|
-
actor is `light.actor_kind/_id/_name`, which the entities route and
|
|
73
|
+
actor is `light.actor_kind/_id/_name`, which the entities route and the platform's
|
|
74
74
|
`mii db write` set per transaction, or else the database role. A migration
|
|
75
75
|
that drops a table or column or changes a column's type first copies what it
|
|
76
76
|
destroys into `light_archive` (`light_archive_values`, called at the top of
|
package/dist/client.d.ts
CHANGED
|
@@ -23,8 +23,175 @@ declare function tableClient<T extends Entity>(entity: T): {
|
|
|
23
23
|
create: (data: NewRow<T>) => Promise<Row<T>>;
|
|
24
24
|
update: (id: string, patch: Patch<T>) => Promise<Row<T>>;
|
|
25
25
|
delete: (id: string) => Promise<void>;
|
|
26
|
-
/**
|
|
27
|
-
|
|
26
|
+
/**
|
|
27
|
+
* The query (`isPending`, `isError`, `error`, `refetch`) and its `rows`,
|
|
28
|
+
* cached per options and refetched after any write to this table. `rows`
|
|
29
|
+
* is never undefined: empty until the first answer, and the same array
|
|
30
|
+
* until the rows change, so it can sit in a hook's dependencies.
|
|
31
|
+
*/
|
|
32
|
+
useList: (options?: ListOptions<Row<T>>) => {
|
|
33
|
+
rows: (T["$inferSelect"] extends infer T_1 ? { [K in keyof T_1]: Date extends T_1[K] ? string | Exclude<T_1[K], T_1[K] & Date> : T_1[K]; } : never)[];
|
|
34
|
+
data: (T["$inferSelect"] extends infer T_2 ? { [K in keyof T_2]: Date extends T_2[K] ? string | Exclude<T_2[K], T_2[K] & Date> : T_2[K]; } : never)[];
|
|
35
|
+
error: EntityError;
|
|
36
|
+
isError: true;
|
|
37
|
+
isPending: false;
|
|
38
|
+
isLoading: false;
|
|
39
|
+
isLoadingError: false;
|
|
40
|
+
isRefetchError: true;
|
|
41
|
+
isSuccess: false;
|
|
42
|
+
isPlaceholderData: false;
|
|
43
|
+
status: "error";
|
|
44
|
+
dataUpdatedAt: number;
|
|
45
|
+
errorUpdatedAt: number;
|
|
46
|
+
failureCount: number;
|
|
47
|
+
failureReason: EntityError | null;
|
|
48
|
+
errorUpdateCount: number;
|
|
49
|
+
isFetched: boolean;
|
|
50
|
+
isFetchedAfterMount: boolean;
|
|
51
|
+
isFetching: boolean;
|
|
52
|
+
isInitialLoading: boolean;
|
|
53
|
+
isPaused: boolean;
|
|
54
|
+
isRefetching: boolean;
|
|
55
|
+
isStale: boolean;
|
|
56
|
+
isEnabled: boolean;
|
|
57
|
+
refetch: (options?: import("@tanstack/react-query").RefetchOptions) => Promise<import("@tanstack/react-query").QueryObserverResult<(T["$inferSelect"] extends infer T_3 ? { [K in keyof T_3]: Date extends T_3[K] ? string | Exclude<T_3[K], T_3[K] & Date> : T_3[K]; } : never)[], EntityError>>;
|
|
58
|
+
fetchStatus: import("@tanstack/react-query").FetchStatus;
|
|
59
|
+
} | {
|
|
60
|
+
rows: (T["$inferSelect"] extends infer T_3 ? { [K in keyof T_3]: Date extends T_3[K] ? string | Exclude<T_3[K], T_3[K] & Date> : T_3[K]; } : never)[];
|
|
61
|
+
data: (T["$inferSelect"] extends infer T_4 ? { [K in keyof T_4]: Date extends T_4[K] ? string | Exclude<T_4[K], T_4[K] & Date> : T_4[K]; } : never)[];
|
|
62
|
+
error: null;
|
|
63
|
+
isError: false;
|
|
64
|
+
isPending: false;
|
|
65
|
+
isLoading: false;
|
|
66
|
+
isLoadingError: false;
|
|
67
|
+
isRefetchError: false;
|
|
68
|
+
isSuccess: true;
|
|
69
|
+
isPlaceholderData: false;
|
|
70
|
+
status: "success";
|
|
71
|
+
dataUpdatedAt: number;
|
|
72
|
+
errorUpdatedAt: number;
|
|
73
|
+
failureCount: number;
|
|
74
|
+
failureReason: EntityError | null;
|
|
75
|
+
errorUpdateCount: number;
|
|
76
|
+
isFetched: boolean;
|
|
77
|
+
isFetchedAfterMount: boolean;
|
|
78
|
+
isFetching: boolean;
|
|
79
|
+
isInitialLoading: boolean;
|
|
80
|
+
isPaused: boolean;
|
|
81
|
+
isRefetching: boolean;
|
|
82
|
+
isStale: boolean;
|
|
83
|
+
isEnabled: boolean;
|
|
84
|
+
refetch: (options?: import("@tanstack/react-query").RefetchOptions) => Promise<import("@tanstack/react-query").QueryObserverResult<(T["$inferSelect"] extends infer T_5 ? { [K in keyof T_5]: Date extends T_5[K] ? string | Exclude<T_5[K], T_5[K] & Date> : T_5[K]; } : never)[], EntityError>>;
|
|
85
|
+
fetchStatus: import("@tanstack/react-query").FetchStatus;
|
|
86
|
+
} | {
|
|
87
|
+
rows: (T["$inferSelect"] extends infer T_5 ? { [K in keyof T_5]: Date extends T_5[K] ? string | Exclude<T_5[K], T_5[K] & Date> : T_5[K]; } : never)[];
|
|
88
|
+
data: undefined;
|
|
89
|
+
error: EntityError;
|
|
90
|
+
isError: true;
|
|
91
|
+
isPending: false;
|
|
92
|
+
isLoading: false;
|
|
93
|
+
isLoadingError: true;
|
|
94
|
+
isRefetchError: false;
|
|
95
|
+
isSuccess: false;
|
|
96
|
+
isPlaceholderData: false;
|
|
97
|
+
status: "error";
|
|
98
|
+
dataUpdatedAt: number;
|
|
99
|
+
errorUpdatedAt: number;
|
|
100
|
+
failureCount: number;
|
|
101
|
+
failureReason: EntityError | null;
|
|
102
|
+
errorUpdateCount: number;
|
|
103
|
+
isFetched: boolean;
|
|
104
|
+
isFetchedAfterMount: boolean;
|
|
105
|
+
isFetching: boolean;
|
|
106
|
+
isInitialLoading: boolean;
|
|
107
|
+
isPaused: boolean;
|
|
108
|
+
isRefetching: boolean;
|
|
109
|
+
isStale: boolean;
|
|
110
|
+
isEnabled: boolean;
|
|
111
|
+
refetch: (options?: import("@tanstack/react-query").RefetchOptions) => Promise<import("@tanstack/react-query").QueryObserverResult<(T["$inferSelect"] extends infer T_6 ? { [K in keyof T_6]: Date extends T_6[K] ? string | Exclude<T_6[K], T_6[K] & Date> : T_6[K]; } : never)[], EntityError>>;
|
|
112
|
+
fetchStatus: import("@tanstack/react-query").FetchStatus;
|
|
113
|
+
} | {
|
|
114
|
+
rows: (T["$inferSelect"] extends infer T_6 ? { [K in keyof T_6]: Date extends T_6[K] ? string | Exclude<T_6[K], T_6[K] & Date> : T_6[K]; } : never)[];
|
|
115
|
+
data: undefined;
|
|
116
|
+
error: null;
|
|
117
|
+
isError: false;
|
|
118
|
+
isPending: true;
|
|
119
|
+
isLoading: true;
|
|
120
|
+
isLoadingError: false;
|
|
121
|
+
isRefetchError: false;
|
|
122
|
+
isSuccess: false;
|
|
123
|
+
isPlaceholderData: false;
|
|
124
|
+
status: "pending";
|
|
125
|
+
dataUpdatedAt: number;
|
|
126
|
+
errorUpdatedAt: number;
|
|
127
|
+
failureCount: number;
|
|
128
|
+
failureReason: EntityError | null;
|
|
129
|
+
errorUpdateCount: number;
|
|
130
|
+
isFetched: boolean;
|
|
131
|
+
isFetchedAfterMount: boolean;
|
|
132
|
+
isFetching: boolean;
|
|
133
|
+
isInitialLoading: boolean;
|
|
134
|
+
isPaused: boolean;
|
|
135
|
+
isRefetching: boolean;
|
|
136
|
+
isStale: boolean;
|
|
137
|
+
isEnabled: boolean;
|
|
138
|
+
refetch: (options?: import("@tanstack/react-query").RefetchOptions) => Promise<import("@tanstack/react-query").QueryObserverResult<(T["$inferSelect"] extends infer T_7 ? { [K in keyof T_7]: Date extends T_7[K] ? string | Exclude<T_7[K], T_7[K] & Date> : T_7[K]; } : never)[], EntityError>>;
|
|
139
|
+
fetchStatus: import("@tanstack/react-query").FetchStatus;
|
|
140
|
+
} | {
|
|
141
|
+
rows: (T["$inferSelect"] extends infer T_7 ? { [K in keyof T_7]: Date extends T_7[K] ? string | Exclude<T_7[K], T_7[K] & Date> : T_7[K]; } : never)[];
|
|
142
|
+
data: undefined;
|
|
143
|
+
error: null;
|
|
144
|
+
isError: false;
|
|
145
|
+
isPending: true;
|
|
146
|
+
isLoadingError: false;
|
|
147
|
+
isRefetchError: false;
|
|
148
|
+
isSuccess: false;
|
|
149
|
+
isPlaceholderData: false;
|
|
150
|
+
status: "pending";
|
|
151
|
+
dataUpdatedAt: number;
|
|
152
|
+
errorUpdatedAt: number;
|
|
153
|
+
failureCount: number;
|
|
154
|
+
failureReason: EntityError | null;
|
|
155
|
+
errorUpdateCount: number;
|
|
156
|
+
isFetched: boolean;
|
|
157
|
+
isFetchedAfterMount: boolean;
|
|
158
|
+
isFetching: boolean;
|
|
159
|
+
isLoading: boolean;
|
|
160
|
+
isInitialLoading: boolean;
|
|
161
|
+
isPaused: boolean;
|
|
162
|
+
isRefetching: boolean;
|
|
163
|
+
isStale: boolean;
|
|
164
|
+
isEnabled: boolean;
|
|
165
|
+
refetch: (options?: import("@tanstack/react-query").RefetchOptions) => Promise<import("@tanstack/react-query").QueryObserverResult<(T["$inferSelect"] extends infer T_8 ? { [K in keyof T_8]: Date extends T_8[K] ? string | Exclude<T_8[K], T_8[K] & Date> : T_8[K]; } : never)[], EntityError>>;
|
|
166
|
+
fetchStatus: import("@tanstack/react-query").FetchStatus;
|
|
167
|
+
} | {
|
|
168
|
+
rows: (T["$inferSelect"] extends infer T_8 ? { [K in keyof T_8]: Date extends T_8[K] ? string | Exclude<T_8[K], T_8[K] & Date> : T_8[K]; } : never)[];
|
|
169
|
+
data: (T["$inferSelect"] extends infer T_9 ? { [K in keyof T_9]: Date extends T_9[K] ? string | Exclude<T_9[K], T_9[K] & Date> : T_9[K]; } : never)[];
|
|
170
|
+
isError: false;
|
|
171
|
+
error: null;
|
|
172
|
+
isPending: false;
|
|
173
|
+
isLoading: false;
|
|
174
|
+
isLoadingError: false;
|
|
175
|
+
isRefetchError: false;
|
|
176
|
+
isSuccess: true;
|
|
177
|
+
isPlaceholderData: true;
|
|
178
|
+
status: "success";
|
|
179
|
+
dataUpdatedAt: number;
|
|
180
|
+
errorUpdatedAt: number;
|
|
181
|
+
failureCount: number;
|
|
182
|
+
failureReason: EntityError | null;
|
|
183
|
+
errorUpdateCount: number;
|
|
184
|
+
isFetched: boolean;
|
|
185
|
+
isFetchedAfterMount: boolean;
|
|
186
|
+
isFetching: boolean;
|
|
187
|
+
isInitialLoading: boolean;
|
|
188
|
+
isPaused: boolean;
|
|
189
|
+
isRefetching: boolean;
|
|
190
|
+
isStale: boolean;
|
|
191
|
+
isEnabled: boolean;
|
|
192
|
+
refetch: (options?: import("@tanstack/react-query").RefetchOptions) => Promise<import("@tanstack/react-query").QueryObserverResult<(T["$inferSelect"] extends infer T_10 ? { [K in keyof T_10]: Date extends T_10[K] ? string | Exclude<T_10[K], T_10[K] & Date> : T_10[K]; } : never)[], EntityError>>;
|
|
193
|
+
fetchStatus: import("@tanstack/react-query").FetchStatus;
|
|
194
|
+
};
|
|
28
195
|
/** One row; `null` waits (nothing open yet). */
|
|
29
196
|
useGet: (id: string | null) => import("@tanstack/react-query").UseQueryResult<T["$inferSelect"] extends infer T_1 ? { [K in keyof T_1]: Date extends T_1[K] ? string | Exclude<T_1[K], T_1[K] & Date> : T_1[K]; } : never, EntityError>;
|
|
30
197
|
useCreate: () => import("@tanstack/react-query").UseMutationResult<T["$inferSelect"] extends infer T_1 ? { [K in keyof T_1]: Date extends T_1[K] ? string | Exclude<T_1[K], T_1[K] & Date> : T_1[K]; } : never, EntityError, NewRow<T>, unknown>;
|
package/dist/client.js
CHANGED
|
@@ -17,6 +17,8 @@ export { InvocationError } from "@shubh90/app-runtime/invoke";
|
|
|
17
17
|
export function createApp() {
|
|
18
18
|
return { table: tableClient, contracts: createInvoker() };
|
|
19
19
|
}
|
|
20
|
+
/** Every list's rows before its first answer: one array, so they never change identity while pending. */
|
|
21
|
+
const NO_ROWS = [];
|
|
20
22
|
/** One entity table's rows: plain calls, and TanStack Query hooks over them. */
|
|
21
23
|
function tableClient(entity) {
|
|
22
24
|
const base = `${ENTITIES_PATH}/${getTableName(entity)}`;
|
|
@@ -58,8 +60,19 @@ function tableClient(entity) {
|
|
|
58
60
|
create,
|
|
59
61
|
update,
|
|
60
62
|
delete: remove,
|
|
61
|
-
/**
|
|
62
|
-
|
|
63
|
+
/**
|
|
64
|
+
* The query (`isPending`, `isError`, `error`, `refetch`) and its `rows`,
|
|
65
|
+
* cached per options and refetched after any write to this table. `rows`
|
|
66
|
+
* is never undefined: empty until the first answer, and the same array
|
|
67
|
+
* until the rows change, so it can sit in a hook's dependencies.
|
|
68
|
+
*/
|
|
69
|
+
useList: (options = {}) => {
|
|
70
|
+
const query = useQuery({ queryKey: keys.list(options), queryFn: () => list(options) });
|
|
71
|
+
const { data: rows = NO_ROWS } = query;
|
|
72
|
+
// A spread reads every field, so the page re-renders on any of them (isFetching
|
|
73
|
+
// too); a list page shows those states anyway, and it keeps the result plain.
|
|
74
|
+
return { ...query, rows };
|
|
75
|
+
},
|
|
63
76
|
/** One row; `null` waits (nothing open yet). */
|
|
64
77
|
useGet: (id) => useQuery({
|
|
65
78
|
queryKey: keys.get(id),
|
package/dist/platform.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// The platform contract a page and the global middleware need: framing by
|
|
2
|
-
//
|
|
2
|
+
// the platform's App pane, the server-function door, analytics and error policies,
|
|
3
3
|
// and the pane bridge. Behaviour lives in @shubh90/app-runtime.
|
|
4
4
|
export { FRAME_ANCESTORS, isAgentBrowser, mayCallServerFn, miiAnalyticsPolicy, miiErrorPolicy, PLAZA_FRAME_ORIGINS } from "@shubh90/app-runtime/platform";
|
|
5
5
|
export { MiiPaneBridge } from "@shubh90/app-runtime/react";
|
package/dist/server/entities.js
CHANGED
|
@@ -37,11 +37,11 @@ export function createEntitiesHandler(options) {
|
|
|
37
37
|
.map((entity) => [getTableName(entity), tableRules(entity)]));
|
|
38
38
|
return async (request) => {
|
|
39
39
|
const url = new URL(request.url);
|
|
40
|
-
// Every org app shares one site (
|
|
40
|
+
// Every org app shares one site (*.<domain>): only this app's pages may use it.
|
|
41
41
|
if (!isSameOrigin(request, url)) {
|
|
42
42
|
return refuse(403, "cross_site", "Only this app's own pages can use its data.");
|
|
43
43
|
}
|
|
44
|
-
// No
|
|
44
|
+
// No platform to sign in with: only a bare local dev server serves (as the
|
|
45
45
|
// local developer). A deployment or a sandbox missing its sign-in config serves nothing.
|
|
46
46
|
const { configured, local } = env();
|
|
47
47
|
if (!configured && !local) {
|
package/dist/server/env.d.ts
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
export declare function env(): {
|
|
3
3
|
databaseUrl: string | undefined;
|
|
4
4
|
unpooledDatabaseUrl: string | undefined;
|
|
5
|
-
/** Sign-in needs
|
|
5
|
+
/** Sign-in needs the platform, this app's id in it, and a database for sessions. */
|
|
6
6
|
configured: boolean;
|
|
7
7
|
/**
|
|
8
|
-
* A bare local dev server: no
|
|
8
|
+
* A bare local dev server: no platform and no app id at all (the platform
|
|
9
9
|
* gives every sandbox and deployment its MII_APP_ID), and not production.
|
|
10
10
|
*/
|
|
11
11
|
local: boolean;
|
package/dist/server/env.js
CHANGED
|
@@ -13,10 +13,10 @@ const serverEnv = z
|
|
|
13
13
|
.transform((env) => ({
|
|
14
14
|
databaseUrl: env.DATABASE_URL,
|
|
15
15
|
unpooledDatabaseUrl: env.DATABASE_URL_UNPOOLED,
|
|
16
|
-
/** Sign-in needs
|
|
16
|
+
/** Sign-in needs the platform, this app's id in it, and a database for sessions. */
|
|
17
17
|
configured: Boolean(env.MII_PLAZA_URL && env.MII_APP_ID && env.DATABASE_URL),
|
|
18
18
|
/**
|
|
19
|
-
* A bare local dev server: no
|
|
19
|
+
* A bare local dev server: no platform and no app id at all (the platform
|
|
20
20
|
* gives every sandbox and deployment its MII_APP_ID), and not production.
|
|
21
21
|
*/
|
|
22
22
|
local: !env.MII_PLAZA_URL && !env.MII_APP_ID && env.NODE_ENV !== "production"
|
package/dist/server/index.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export declare function createLightServer<S extends Record<string, unknown>, C e
|
|
|
12
12
|
session: (request: Request) => Promise<import("./session.js").SessionState>;
|
|
13
13
|
/** Whether sign-in is configured (a bare local dev server has none). */
|
|
14
14
|
configured: () => boolean;
|
|
15
|
-
/** Email and password, checked by
|
|
15
|
+
/** Email and password, checked by the platform; the session to set as a cookie, or why not. */
|
|
16
16
|
signIn: (email: string, password: string) => Promise<import("./session.js").SignIn>;
|
|
17
17
|
/** The app's database, typed by its schema: `light.db.select().from(companies)`. */
|
|
18
18
|
readonly db: PostgresJsDatabase<S>;
|
|
@@ -22,7 +22,7 @@ export declare function createLightServer<S extends Record<string, unknown>, C e
|
|
|
22
22
|
image: (request: Request) => Promise<Response>;
|
|
23
23
|
/** Server-side starts and reads of the app's contracts. */
|
|
24
24
|
invoke: import("@shubh90/app-runtime/invoke/server").MiiInvoke<C>;
|
|
25
|
-
/** `/api/mii/invoke/*`: the contract buttons, forwarded to
|
|
25
|
+
/** `/api/mii/invoke/*`: the contract buttons, forwarded to the platform as the person. */
|
|
26
26
|
invokeRoute: (request: Request) => Promise<Response>;
|
|
27
27
|
/** `/api/mii/chat/*`: the chat bubble's calls to the org's agents. */
|
|
28
28
|
chatRoute: (request: Request) => Promise<Response>;
|
package/dist/server/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// The app's server half, bound once in `src/server/light.ts`: its database,
|
|
2
|
-
// its door (sign-in through
|
|
2
|
+
// its door (sign-in through the platform), and the routes the SDK serves (entities,
|
|
3
3
|
// images, contracts, chat). Server-only.
|
|
4
4
|
import { drizzle } from "drizzle-orm/postgres-js";
|
|
5
5
|
import postgres from "postgres";
|
|
@@ -35,7 +35,7 @@ export function createLightServer(options) {
|
|
|
35
35
|
session,
|
|
36
36
|
/** Whether sign-in is configured (a bare local dev server has none). */
|
|
37
37
|
configured: () => env().configured,
|
|
38
|
-
/** Email and password, checked by
|
|
38
|
+
/** Email and password, checked by the platform; the session to set as a cookie, or why not. */
|
|
39
39
|
signIn: (email, password) => signIn(auth, email, password),
|
|
40
40
|
/** The app's database, typed by its schema: `light.db.select().from(companies)`. */
|
|
41
41
|
get db() {
|
|
@@ -47,13 +47,13 @@ export function createLightServer(options) {
|
|
|
47
47
|
image: createImageHandler({ db, session }),
|
|
48
48
|
/** Server-side starts and reads of the app's contracts. */
|
|
49
49
|
invoke,
|
|
50
|
-
/** `/api/mii/invoke/*`: the contract buttons, forwarded to
|
|
50
|
+
/** `/api/mii/invoke/*`: the contract buttons, forwarded to the platform as the person. */
|
|
51
51
|
invokeRoute: needsSignIn(invoke.handler),
|
|
52
52
|
/** `/api/mii/chat/*`: the chat bubble's calls to the org's agents. */
|
|
53
53
|
chatRoute: needsSignIn(chat)
|
|
54
54
|
};
|
|
55
55
|
}
|
|
56
|
-
/** A route that forwards to
|
|
56
|
+
/** A route that forwards to the platform as the person: nothing to forward without sign-in. */
|
|
57
57
|
function needsSignIn(handler) {
|
|
58
58
|
return (request) => env().configured
|
|
59
59
|
? handler(request)
|
package/dist/server/session.d.ts
CHANGED
|
@@ -14,5 +14,5 @@ export type SignIn = {
|
|
|
14
14
|
ok: false;
|
|
15
15
|
message: string;
|
|
16
16
|
};
|
|
17
|
-
/** The login form's whole job:
|
|
17
|
+
/** The login form's whole job: the platform checks the password, the app starts a session. */
|
|
18
18
|
export declare function signIn(auth: MiiAuthCore, email: string, password: string): Promise<SignIn>;
|
package/dist/server/session.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
// Who is asking, as the app's door sees it, and signing in through
|
|
1
|
+
// Who is asking, as the app's door sees it, and signing in through the platform.
|
|
2
2
|
import { signInWithPlaza } from "@shubh90/app-runtime/auth/core";
|
|
3
3
|
import { readCookie } from "@shubh90/app-runtime/chat/server";
|
|
4
4
|
import { env } from "./env.js";
|
|
5
5
|
/** The session a request's cookie names, and whether the app is members-only. */
|
|
6
6
|
export async function readSession(auth, request) {
|
|
7
|
-
// No
|
|
7
|
+
// No platform to sign in with: a bare local dev server has no door; any other
|
|
8
8
|
// server without its sign-in config keeps the door shut.
|
|
9
9
|
const { configured, local } = env();
|
|
10
10
|
if (!configured)
|
|
@@ -13,14 +13,14 @@ export async function readSession(auth, request) {
|
|
|
13
13
|
let found = null;
|
|
14
14
|
if (token !== null)
|
|
15
15
|
found = await auth.resolveSession(token);
|
|
16
|
-
//
|
|
16
|
+
// The platform unreachable on a cold start throws: an error page, not a door that guesses.
|
|
17
17
|
const mode = await auth.getAuthMode();
|
|
18
18
|
if (found === null)
|
|
19
19
|
return { user: null, mode };
|
|
20
20
|
const { id, name, email, avatarUrl, role } = found;
|
|
21
21
|
return { user: { id, name, email, avatarUrl, role }, mode };
|
|
22
22
|
}
|
|
23
|
-
/** The login form's whole job:
|
|
23
|
+
/** The login form's whole job: the platform checks the password, the app starts a session. */
|
|
24
24
|
export async function signIn(auth, email, password) {
|
|
25
25
|
if (!env().configured)
|
|
26
26
|
return { ok: false, message: "Sign-in is not configured." };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trylight/app",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "The Light SDK every v2 org app depends on: entities (Drizzle tables with a typed client), sign-in, contracts and in-app chat.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
|
@@ -63,6 +63,7 @@
|
|
|
63
63
|
"@tanstack/react-query": "^5.104.0",
|
|
64
64
|
"@types/node": "^22.20.4",
|
|
65
65
|
"@types/react": "^19.3.0",
|
|
66
|
+
"@types/react-dom": "^19.3.0",
|
|
66
67
|
"drizzle-orm": "^0.45.3",
|
|
67
68
|
"eslint": "^10.11.0",
|
|
68
69
|
"postgres": "^3.4.9",
|