@trylight/app 0.1.2 → 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 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 |
@@ -46,6 +46,11 @@ sign-in; a stranger to a public app only reads what members kept.
46
46
 
47
47
  ## Schema sync
48
48
 
49
+ `light-db sync` and `migrate` connect with `DATABASE_URL_UNPOOLED`, the
50
+ database's direct address, and refuse to run without it: they serialise on
51
+ Postgres advisory locks, which a transaction pooler (the app's `DATABASE_URL`
52
+ on Neon) keeps on a pooled connection after the process exits.
53
+
49
54
  `plan()` diffs `db/schema` against the newest migration snapshot with
50
55
  drizzle-kit's API, removals first and additions second, so drizzle-kit never
51
56
  has to ask whether a column was renamed (it can't without a terminal).
@@ -65,7 +70,7 @@ fails while `db/schema` has changes no migration holds.
65
70
  `src/db/audit.ts`, in Postgres itself. Every entity table has a row trigger
66
71
  that records each insert, update, delete and truncate in `light_changes` (the
67
72
  new row, `{ column: { from, to } }`, or the old row), whoever wrote it: the
68
- actor is `light.actor_kind/_id/_name`, which the entities route and Plaza's
73
+ actor is `light.actor_kind/_id/_name`, which the entities route and the platform's
69
74
  `mii db write` set per transaction, or else the database role. A migration
70
75
  that drops a table or column or changes a column's type first copies what it
71
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
- /** The rows, cached per options; refetched after any write to this table. */
27
- useList: (options?: ListOptions<Row<T>>) => 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>;
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
- /** The rows, cached per options; refetched after any write to this table. */
62
- useList: (options = {}) => useQuery({ queryKey: keys.list(options), queryFn: () => list(options) }),
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/db/cli.js CHANGED
@@ -24,7 +24,9 @@ const SYNC_LOCK_KEY = 4_815_162_343;
24
24
  /** Exit code of a migrate whose migrations committed but whose change triggers didn't go on. */
25
25
  const APPLIED_UNTRACKED = 2;
26
26
  // pg_advisory_lock waits for the one before it; a process that dies releases it.
27
- const connect = (onnotice = () => undefined) => postgres(databaseUrl(), { max: 1, onnotice });
27
+ // Only on a direct connection: behind a transaction pooler the lock stays on a
28
+ // pooled server connection after the process exits, and every later sync waits.
29
+ const connect = (onnotice = () => undefined) => postgres(unpooledDatabaseUrl(), { max: 1, onnotice });
28
30
  const root = process.cwd();
29
31
  const envFile = join(root, ".env.local");
30
32
  if (existsSync(envFile))
@@ -33,11 +35,12 @@ const [command] = process.argv.slice(2);
33
35
  async function loadSchema() {
34
36
  return import(pathToFileURL(join(root, SCHEMA_ENTRY)).href);
35
37
  }
36
- function databaseUrl() {
37
- const { databaseUrl } = env();
38
- if (databaseUrl === undefined)
39
- throw new Error("DATABASE_URL is not set: there is no database to sync db/schema into.");
40
- return databaseUrl;
38
+ function unpooledDatabaseUrl() {
39
+ const { unpooledDatabaseUrl } = env();
40
+ if (unpooledDatabaseUrl === undefined) {
41
+ throw new Error("DATABASE_URL_UNPOOLED is not set: light-db sync and migrate need the database's direct (unpooled) connection.");
42
+ }
43
+ return unpooledDatabaseUrl;
41
44
  }
42
45
  /**
43
46
  * Makes the database match `db/schema`, whatever the change does to the rows
package/dist/platform.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // The platform contract a page and the global middleware need: framing by
2
- // Plaza's App pane, the server-function door, analytics and error policies,
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";
@@ -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 (*.miis.run): only this app's pages may use it.
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 Plaza to sign in with: only a bare local dev server serves (as the
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) {
@@ -1,10 +1,11 @@
1
1
  /** Read when asked, not at import: a build imports this without a runtime env. */
2
2
  export declare function env(): {
3
3
  databaseUrl: string | undefined;
4
- /** Sign-in needs Plaza, this app's id in it, and a database for sessions. */
4
+ unpooledDatabaseUrl: string | undefined;
5
+ /** Sign-in needs the platform, this app's id in it, and a database for sessions. */
5
6
  configured: boolean;
6
7
  /**
7
- * A bare local dev server: no Plaza and no app id at all (the platform
8
+ * A bare local dev server: no platform and no app id at all (the platform
8
9
  * gives every sandbox and deployment its MII_APP_ID), and not production.
9
10
  */
10
11
  local: boolean;
@@ -4,16 +4,19 @@ import { z } from "zod/v4";
4
4
  const serverEnv = z
5
5
  .object({
6
6
  DATABASE_URL: z.string().min(1).optional(),
7
+ /** The same database without a pooler: schema changes and their locks need a session. */
8
+ DATABASE_URL_UNPOOLED: z.string().min(1).optional(),
7
9
  MII_PLAZA_URL: z.url().optional(),
8
10
  MII_APP_ID: z.string().min(1).optional(),
9
11
  NODE_ENV: z.string().optional()
10
12
  })
11
13
  .transform((env) => ({
12
14
  databaseUrl: env.DATABASE_URL,
13
- /** Sign-in needs Plaza, this app's id in it, and a database for sessions. */
15
+ unpooledDatabaseUrl: env.DATABASE_URL_UNPOOLED,
16
+ /** Sign-in needs the platform, this app's id in it, and a database for sessions. */
14
17
  configured: Boolean(env.MII_PLAZA_URL && env.MII_APP_ID && env.DATABASE_URL),
15
18
  /**
16
- * A bare local dev server: no Plaza and no app id at all (the platform
19
+ * A bare local dev server: no platform and no app id at all (the platform
17
20
  * gives every sandbox and deployment its MII_APP_ID), and not production.
18
21
  */
19
22
  local: !env.MII_PLAZA_URL && !env.MII_APP_ID && env.NODE_ENV !== "production"
@@ -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 Plaza; the session to set as a cookie, or why not. */
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 Plaza as the person. */
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>;
@@ -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 Plaza), and the routes the SDK serves (entities,
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 Plaza; the session to set as a cookie, or why not. */
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 Plaza as the person. */
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 Plaza as the person: nothing to forward without sign-in. */
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)
@@ -14,5 +14,5 @@ export type SignIn = {
14
14
  ok: false;
15
15
  message: string;
16
16
  };
17
- /** The login form's whole job: Plaza checks the password, the app starts a session. */
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>;
@@ -1,10 +1,10 @@
1
- // Who is asking, as the app's door sees it, and signing in through Plaza.
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 Plaza to sign in with: a bare local dev server has no door; any other
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
- // Plaza unreachable on a cold start throws: an error page, not a door that guesses.
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: Plaza checks the password, the app starts a session. */
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.2",
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",