@trylight/app 0.3.8 → 0.3.9
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 +4 -2
- package/dist/auth/lookup.d.ts +1 -1
- package/dist/auth/lookup.js +1 -1
- package/dist/brand.generated.d.ts +2 -0
- package/dist/brand.generated.js +2 -0
- package/dist/client.d.ts +10 -1
- package/dist/client.js +7 -4
- package/dist/platform/contract.d.ts +0 -1
- package/dist/platform/contract.js +1 -2
- package/dist/platform/live-data.d.ts +7 -4
- package/dist/platform/live-data.js +14 -9
- package/package.json +12 -10
package/README.md
CHANGED
|
@@ -60,8 +60,10 @@ every `POLL_MS` while the tab is visible, at once on focus and reconnect. The
|
|
|
60
60
|
route answers from `light_changes` with the tables changed since the cursor
|
|
61
61
|
(looking `CHANGES_OVERLAP` behind it, for transactions that committed after
|
|
62
62
|
the last poll read) and the next cursor; never rows. Each changed table's
|
|
63
|
-
queries are invalidated
|
|
64
|
-
|
|
63
|
+
queries are invalidated: those under its key, and those that list its table in
|
|
64
|
+
`meta.tables` (a query that reads several tables, e.g. a server function's
|
|
65
|
+
join), except a table this tab is writing (the write's own refetch follows
|
|
66
|
+
it); the route loaders are reloaded. `light_changes`' `created_at` index is made
|
|
65
67
|
after every migrate (`src/db/audit.ts`). The poll is one `ChangeFeed`; a pushed
|
|
66
68
|
transport can replace it.
|
|
67
69
|
|
package/dist/auth/lookup.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { AuthContext } from "./context.js";
|
|
2
2
|
import { type PlazaSession } from "./plaza.js";
|
|
3
3
|
/** The cookie holding this app's own session. Nothing of the platform's is read here. */
|
|
4
|
-
export { SESSION_COOKIE } from "../
|
|
4
|
+
export { APP_SESSION_COOKIE as SESSION_COOKIE } from "../brand.generated.js";
|
|
5
5
|
/**
|
|
6
6
|
* How often a session asks the platform whether it still stands. Someone removed from
|
|
7
7
|
* the org is signed out of the app within this long; their chat stops at once,
|
package/dist/auth/lookup.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash, randomBytes } from "node:crypto";
|
|
2
2
|
import { checkPlazaSession, revokePlazaSession } from "./plaza.js";
|
|
3
3
|
/** The cookie holding this app's own session. Nothing of the platform's is read here. */
|
|
4
|
-
export { SESSION_COOKIE } from "../
|
|
4
|
+
export { APP_SESSION_COOKIE as SESSION_COOKIE } from "../brand.generated.js";
|
|
5
5
|
/**
|
|
6
6
|
* How often a session asks the platform whether it still stands. Someone removed from
|
|
7
7
|
* the org is signed out of the app within this long; their chat stops at once,
|
package/dist/brand.generated.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
// Generated from config/brand.json by scripts/write-brand.ts. Do not edit.
|
|
2
2
|
/** The platform app's origins, current domain first. */
|
|
3
3
|
export const APP_ORIGINS = ["https://trylight.ai", "https://plaza.miis.run"];
|
|
4
|
+
/** The cookie holding an app's own session. */
|
|
5
|
+
export const APP_SESSION_COOKIE = "mii_session";
|
package/dist/client.d.ts
CHANGED
|
@@ -2,6 +2,14 @@ import { type QueryKey } from "@tanstack/react-query";
|
|
|
2
2
|
import type { Contracts } from "./invoke/server.js";
|
|
3
3
|
import { EntityError, type ListOptions, type NewRow, type Patch, type Row } from "./entities.js";
|
|
4
4
|
import type { Entity } from "./schema.js";
|
|
5
|
+
declare module "@tanstack/react-query" {
|
|
6
|
+
interface Register {
|
|
7
|
+
/** `tables`: the tables a query reads when it isn't keyed under one (a server function's join); a write to any refetches it. */
|
|
8
|
+
queryMeta: {
|
|
9
|
+
tables: readonly Entity[];
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
}
|
|
5
13
|
export { EntityError, LIST_LIMIT, type ListOptions, type NewRow, type Sort, type Patch, type Row, type Where } from "./entities.js";
|
|
6
14
|
export type { FieldSource, FieldSources } from "./schema.js";
|
|
7
15
|
export { siteOf } from "./schema.js";
|
|
@@ -68,7 +76,8 @@ declare function tableClient<T extends Entity>(entity: T): {
|
|
|
68
76
|
/**
|
|
69
77
|
* The query key everything this table caches sits under. A page's own
|
|
70
78
|
* query over the table (a server function's totals) keyed under it is
|
|
71
|
-
* refetched after every write too
|
|
79
|
+
* refetched after every write too; one over several tables names them in
|
|
80
|
+
* `meta.tables` instead.
|
|
72
81
|
*/
|
|
73
82
|
key: string[];
|
|
74
83
|
list: (options?: ListOptions<Row<T>>) => Promise<Row<T>[]>;
|
package/dist/client.js
CHANGED
|
@@ -13,6 +13,7 @@ import { z } from "zod/v4";
|
|
|
13
13
|
import { ENTITIES_PATH } from "./platform/contract.js";
|
|
14
14
|
import { EntityError, LIST_LIMIT, tableQueryKey } from "./entities.js";
|
|
15
15
|
import { fetchThroughRestart } from "./platform/restart.js";
|
|
16
|
+
import { reads } from "./platform/live-data.js";
|
|
16
17
|
export { EntityError, LIST_LIMIT } from "./entities.js";
|
|
17
18
|
export { siteOf } from "./schema.js";
|
|
18
19
|
export { imageSrc } from "./images.js";
|
|
@@ -124,8 +125,9 @@ function putBack(client, id, before) {
|
|
|
124
125
|
}
|
|
125
126
|
/** One entity table's rows: plain calls, and TanStack Query hooks over them. */
|
|
126
127
|
function tableClient(entity) {
|
|
127
|
-
const
|
|
128
|
-
const
|
|
128
|
+
const table = getTableName(entity);
|
|
129
|
+
const base = `${ENTITIES_PATH}/${table}`;
|
|
130
|
+
const all = tableQueryKey(table);
|
|
129
131
|
const keys = {
|
|
130
132
|
all,
|
|
131
133
|
list: (options) => [...all, "list", options],
|
|
@@ -168,7 +170,7 @@ function tableClient(entity) {
|
|
|
168
170
|
const client = useQueryClient();
|
|
169
171
|
const refetch = async () => {
|
|
170
172
|
if (client.isMutating({ mutationKey: keys.write }) === 1)
|
|
171
|
-
await client.invalidateQueries({
|
|
173
|
+
await client.invalidateQueries({ predicate: (query) => reads(query, table) });
|
|
172
174
|
};
|
|
173
175
|
return (_data, error) => {
|
|
174
176
|
if (error === null)
|
|
@@ -180,7 +182,8 @@ function tableClient(entity) {
|
|
|
180
182
|
/**
|
|
181
183
|
* The query key everything this table caches sits under. A page's own
|
|
182
184
|
* query over the table (a server function's totals) keyed under it is
|
|
183
|
-
* refetched after every write too
|
|
185
|
+
* refetched after every write too; one over several tables names them in
|
|
186
|
+
* `meta.tables` instead.
|
|
184
187
|
*/
|
|
185
188
|
key: keys.all,
|
|
186
189
|
list,
|
|
@@ -31,7 +31,6 @@ export declare const CHAT_FEATURES_HEADER = "X-Mii-Chat-Features";
|
|
|
31
31
|
export declare const STARTING_HEADER = "x-mii-app-starting";
|
|
32
32
|
/** Every TanStack query the SDK owns sits under this key. */
|
|
33
33
|
export declare const QUERY_ROOT = "light";
|
|
34
|
-
export declare const SESSION_COOKIE = "mii_session";
|
|
35
34
|
export declare const STORAGE_KEYS: {
|
|
36
35
|
/** sessionStorage: the build this tab already reloaded for after a missing chunk, so a second miss on it can't loop. */
|
|
37
36
|
chunkRetryBuildId: string;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// apps and the platform's pane depend on every value here: rename freely, never
|
|
3
3
|
// change a value without both sides moving.
|
|
4
4
|
const NAMESPACE = "light";
|
|
5
|
-
/** The platform's former name: the pane protocol
|
|
5
|
+
/** The platform's former name: the pane protocol and two routes still carry it. */
|
|
6
6
|
const LEGACY_NAMESPACE = "mii";
|
|
7
7
|
export const ENTITIES_PATH = `/api/${NAMESPACE}/entities`;
|
|
8
8
|
export const CHANGES_PATH = `${ENTITIES_PATH}/_changes`;
|
|
@@ -34,7 +34,6 @@ export const CHAT_FEATURES_HEADER = "X-Mii-Chat-Features";
|
|
|
34
34
|
export const STARTING_HEADER = `x-${LEGACY_NAMESPACE}-app-starting`;
|
|
35
35
|
/** Every TanStack query the SDK owns sits under this key. */
|
|
36
36
|
export const QUERY_ROOT = NAMESPACE;
|
|
37
|
-
export const SESSION_COOKIE = `${LEGACY_NAMESPACE}_session`;
|
|
38
37
|
export const STORAGE_KEYS = {
|
|
39
38
|
/** sessionStorage: the build this tab already reloaded for after a missing chunk, so a second miss on it can't loop. */
|
|
40
39
|
chunkRetryBuildId: `${NAMESPACE}:chunk-retry-build-id`,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type QueryClient } from "@tanstack/react-query";
|
|
1
|
+
import { type Query, type QueryClient } from "@tanstack/react-query";
|
|
2
2
|
import { z } from "zod/v4";
|
|
3
3
|
declare const changes: z.ZodObject<{
|
|
4
4
|
cursor: z.ZodString;
|
|
@@ -20,9 +20,12 @@ export type ChangeFeed = (onChanged: (tables: string[]) => void) => () => void;
|
|
|
20
20
|
export declare function pollChanges(queries: QueryClient, fetchChanges: (since: string | null) => Promise<Poll>): ChangeFeed;
|
|
21
21
|
export declare function fetchChanges(since: string | null): Promise<Poll>;
|
|
22
22
|
/**
|
|
23
|
-
* Each changed table's on-screen queries refetch,
|
|
24
|
-
*
|
|
25
|
-
*
|
|
23
|
+
* Each changed table's on-screen queries refetch, in one go: those keyed under
|
|
24
|
+
* it, and those that read it among others and say so in `meta.tables`. A table
|
|
25
|
+
* this tab is writing is left to its write, whose own refetch follows it
|
|
26
|
+
* (client.ts's settle) and brings these changes too.
|
|
26
27
|
*/
|
|
27
28
|
export declare function refreshTables(queries: QueryClient, tables: string[]): void;
|
|
29
|
+
/** Whether a query shows `table`'s rows: keyed under it, or listing it in `meta.tables` (typed in client.ts). */
|
|
30
|
+
export declare function reads(query: Query, table: string): boolean;
|
|
28
31
|
export {};
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
// of a change is a ChangeFeed; today a poll of `_changes` that TanStack Query
|
|
5
5
|
// itself runs (refetchInterval while the tab is visible, at once on focus and
|
|
6
6
|
// reconnect), later a pushed one (SSE) behind the same type.
|
|
7
|
-
import { QueryObserver } from "@tanstack/react-query";
|
|
7
|
+
import { QueryObserver, partialMatchKey } from "@tanstack/react-query";
|
|
8
|
+
import { getTableName } from "drizzle-orm";
|
|
8
9
|
import { z } from "zod/v4";
|
|
9
10
|
import { EntityError, tableQueryKey } from "../entities.js";
|
|
10
11
|
import { CHANGES_PATH, QUERY_ROOT } from "./contract.js";
|
|
@@ -73,14 +74,18 @@ export async function fetchChanges(since) {
|
|
|
73
74
|
return changes.parse(await response.json());
|
|
74
75
|
}
|
|
75
76
|
/**
|
|
76
|
-
* Each changed table's on-screen queries refetch,
|
|
77
|
-
*
|
|
78
|
-
*
|
|
77
|
+
* Each changed table's on-screen queries refetch, in one go: those keyed under
|
|
78
|
+
* it, and those that read it among others and say so in `meta.tables`. A table
|
|
79
|
+
* this tab is writing is left to its write, whose own refetch follows it
|
|
80
|
+
* (client.ts's settle) and brings these changes too.
|
|
79
81
|
*/
|
|
80
82
|
export function refreshTables(queries, tables) {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
83
|
+
const quiet = tables.filter((table) => queries.isMutating({ mutationKey: [...tableQueryKey(table), "write"] }) === 0);
|
|
84
|
+
if (quiet.length > 0)
|
|
85
|
+
void queries.invalidateQueries({ predicate: (query) => quiet.some((table) => reads(query, table)) });
|
|
86
|
+
}
|
|
87
|
+
/** Whether a query shows `table`'s rows: keyed under it, or listing it in `meta.tables` (typed in client.ts). */
|
|
88
|
+
export function reads(query, table) {
|
|
89
|
+
// eslint-disable-next-line no-restricted-syntax -- TanStack's meta is optional: most queries name no tables
|
|
90
|
+
return partialMatchKey(query.queryKey, tableQueryKey(table)) || query.meta?.tables.some((entity) => getTableName(entity) === table) === true;
|
|
86
91
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trylight/app",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.9",
|
|
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",
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
"url": "https://github.com/shubh9/mii.git",
|
|
13
13
|
"directory": "packages/app-sdk"
|
|
14
14
|
},
|
|
15
|
+
"packageManager": "pnpm@11.0.9",
|
|
15
16
|
"files": [
|
|
16
17
|
"dist",
|
|
17
18
|
"bin"
|
|
@@ -41,6 +42,15 @@
|
|
|
41
42
|
"default": "./dist/vite.js"
|
|
42
43
|
}
|
|
43
44
|
},
|
|
45
|
+
"scripts": {
|
|
46
|
+
"build": "rm -rf dist && tsc -p tsconfig.build.json",
|
|
47
|
+
"typecheck": "tsc --noEmit",
|
|
48
|
+
"lint": "eslint src test --max-warnings 0",
|
|
49
|
+
"test": "node --test --import tsx \"test/**/*.test.ts\"",
|
|
50
|
+
"check": "tsx scripts/write-brand.ts --check && pnpm run typecheck && pnpm run lint && pnpm run test && pnpm run build",
|
|
51
|
+
"prepack": "pnpm run build",
|
|
52
|
+
"brand": "tsx scripts/write-brand.ts"
|
|
53
|
+
},
|
|
44
54
|
"peerDependencies": {
|
|
45
55
|
"@tanstack/react-query": ">=5.69",
|
|
46
56
|
"@tanstack/react-router": ">=1.170",
|
|
@@ -74,13 +84,5 @@
|
|
|
74
84
|
"typescript-eslint": "^8.70.1",
|
|
75
85
|
"vite": "^8.2.0",
|
|
76
86
|
"zod": "^4.6.5"
|
|
77
|
-
},
|
|
78
|
-
"scripts": {
|
|
79
|
-
"build": "rm -rf dist && tsc -p tsconfig.build.json",
|
|
80
|
-
"typecheck": "tsc --noEmit",
|
|
81
|
-
"lint": "eslint src test --max-warnings 0",
|
|
82
|
-
"test": "node --test --import tsx \"test/**/*.test.ts\"",
|
|
83
|
-
"check": "tsx scripts/write-brand.ts --check && pnpm run typecheck && pnpm run lint && pnpm run test && pnpm run build",
|
|
84
|
-
"brand": "tsx scripts/write-brand.ts"
|
|
85
87
|
}
|
|
86
|
-
}
|
|
88
|
+
}
|