@adventurelabs/scout-core 1.4.75 → 1.4.78
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/dist/helpers/cache.d.ts +10 -0
- package/dist/helpers/cache.js +86 -2
- package/dist/helpers/client_abilities_token.d.ts +0 -1
- package/dist/helpers/client_abilities_token.js +0 -1
- package/dist/helpers/herd_modules_equal.d.ts +2 -0
- package/dist/helpers/herd_modules_equal.js +28 -0
- package/dist/helpers/pubsub_token.d.ts +3 -1
- package/dist/helpers/pubsub_token.js +41 -0
- package/dist/helpers/versions_software.d.ts +0 -1
- package/dist/helpers/versions_software.js +0 -14
- package/dist/helpers/versions_software_server.d.ts +0 -1
- package/dist/helpers/versions_software_server.js +0 -16
- package/dist/hooks/useScoutRefresh.js +13 -11
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/providers/ScoutRefreshProvider.d.ts +12 -11
- package/dist/providers/ScoutRefreshProvider.js +85 -31
- package/dist/store/api.d.ts +722 -130
- package/dist/store/scout.d.ts +0 -106
- package/dist/store/scout.js +6 -2
- package/dist/types/client_abilities_token.d.ts +1 -6
- package/dist/types/client_abilities_token.js +5 -3
- package/dist/types/jwt_mint.d.ts +10 -0
- package/dist/types/jwt_mint.js +26 -0
- package/dist/types/pubsub_token.d.ts +5 -0
- package/dist/types/pubsub_token.js +1 -1
- package/package.json +3 -1
package/dist/helpers/cache.d.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { IHerdModule } from "../types/herd_module";
|
|
2
|
+
export type JwtMintCacheKey = "client_abilities" | "pubsub";
|
|
3
|
+
type CachedJwtMint = {
|
|
4
|
+
token: string;
|
|
5
|
+
iat: number;
|
|
6
|
+
exp: number;
|
|
7
|
+
};
|
|
2
8
|
export interface CacheMetadata {
|
|
3
9
|
key: string;
|
|
4
10
|
timestamp: number;
|
|
@@ -43,6 +49,9 @@ export declare class ScoutCache {
|
|
|
43
49
|
private validateDatabaseSchema;
|
|
44
50
|
setHerdModules(herdModules: IHerdModule[], ttlMs?: number, etag?: string): Promise<void>;
|
|
45
51
|
getHerdModules(): Promise<CacheResult<IHerdModule[]>>;
|
|
52
|
+
setJwtMint<T extends CachedJwtMint>(key: JwtMintCacheKey, mint: T): Promise<void>;
|
|
53
|
+
getJwtMint<T extends CachedJwtMint>(key: JwtMintCacheKey): Promise<CacheResult<T>>;
|
|
54
|
+
clearJwtMint(key: JwtMintCacheKey): Promise<void>;
|
|
46
55
|
clearHerdModules(): Promise<void>;
|
|
47
56
|
invalidateHerdModules(): Promise<void>;
|
|
48
57
|
getCacheStats(): Promise<CacheStats>;
|
|
@@ -60,3 +69,4 @@ export declare class ScoutCache {
|
|
|
60
69
|
checkDatabaseHealth(): Promise<DatabaseHealth>;
|
|
61
70
|
}
|
|
62
71
|
export declare const scoutCache: ScoutCache;
|
|
72
|
+
export {};
|
package/dist/helpers/cache.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const DB_NAME = "ScoutCache";
|
|
2
|
-
const DB_VERSION =
|
|
2
|
+
const DB_VERSION = 8;
|
|
3
3
|
const HERD_MODULES_STORE = "herd_modules";
|
|
4
|
+
const JWT_MINTS_STORE = "jwt_mints";
|
|
4
5
|
const CACHE_METADATA_STORE = "cache_metadata";
|
|
5
6
|
// Default TTL: 24 hours (1 day)
|
|
6
7
|
const DEFAULT_TTL_MS = 24 * 60 * 60 * 1000;
|
|
@@ -56,6 +57,13 @@ export class ScoutCache {
|
|
|
56
57
|
unique: false,
|
|
57
58
|
});
|
|
58
59
|
console.log("[ScoutCache] Created herd_modules object store");
|
|
60
|
+
const jwtMintsStore = db.createObjectStore(JWT_MINTS_STORE, {
|
|
61
|
+
keyPath: "key",
|
|
62
|
+
});
|
|
63
|
+
jwtMintsStore.createIndex("timestamp", "timestamp", {
|
|
64
|
+
unique: false,
|
|
65
|
+
});
|
|
66
|
+
console.log("[ScoutCache] Created jwt_mints object store");
|
|
59
67
|
// Create cache metadata store
|
|
60
68
|
const metadataStore = db.createObjectStore(CACHE_METADATA_STORE, {
|
|
61
69
|
keyPath: "key",
|
|
@@ -78,14 +86,18 @@ export class ScoutCache {
|
|
|
78
86
|
if (!this.db)
|
|
79
87
|
return false;
|
|
80
88
|
const hasHerdModulesStore = this.db.objectStoreNames.contains(HERD_MODULES_STORE);
|
|
89
|
+
const hasJwtMintsStore = this.db.objectStoreNames.contains(JWT_MINTS_STORE);
|
|
81
90
|
const hasMetadataStore = this.db.objectStoreNames.contains(CACHE_METADATA_STORE);
|
|
82
91
|
if (!hasHerdModulesStore) {
|
|
83
92
|
console.error("[ScoutCache] Missing herd_modules object store");
|
|
84
93
|
}
|
|
94
|
+
if (!hasJwtMintsStore) {
|
|
95
|
+
console.error("[ScoutCache] Missing jwt_mints object store");
|
|
96
|
+
}
|
|
85
97
|
if (!hasMetadataStore) {
|
|
86
98
|
console.error("[ScoutCache] Missing cache_metadata object store");
|
|
87
99
|
}
|
|
88
|
-
return hasHerdModulesStore && hasMetadataStore;
|
|
100
|
+
return hasHerdModulesStore && hasJwtMintsStore && hasMetadataStore;
|
|
89
101
|
}
|
|
90
102
|
async setHerdModules(herdModules, ttlMs = DEFAULT_TTL_MS, etag) {
|
|
91
103
|
await this.init();
|
|
@@ -190,6 +202,78 @@ export class ScoutCache {
|
|
|
190
202
|
};
|
|
191
203
|
});
|
|
192
204
|
}
|
|
205
|
+
async setJwtMint(key, mint) {
|
|
206
|
+
await this.init();
|
|
207
|
+
if (!this.db)
|
|
208
|
+
throw new Error("Database not initialized");
|
|
209
|
+
if (!this.validateDatabaseSchema()) {
|
|
210
|
+
throw new Error("Database schema validation failed - required object stores not found");
|
|
211
|
+
}
|
|
212
|
+
const transaction = this.db.transaction([JWT_MINTS_STORE], "readwrite");
|
|
213
|
+
return new Promise((resolve, reject) => {
|
|
214
|
+
transaction.onerror = () => reject(transaction.error);
|
|
215
|
+
transaction.oncomplete = () => resolve();
|
|
216
|
+
transaction.objectStore(JWT_MINTS_STORE).put({
|
|
217
|
+
key,
|
|
218
|
+
data: mint,
|
|
219
|
+
timestamp: Date.now(),
|
|
220
|
+
dbVersion: DB_VERSION,
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
async getJwtMint(key) {
|
|
225
|
+
await this.init();
|
|
226
|
+
if (!this.db)
|
|
227
|
+
throw new Error("Database not initialized");
|
|
228
|
+
if (!this.validateDatabaseSchema()) {
|
|
229
|
+
throw new Error("Database schema validation failed - required object stores not found");
|
|
230
|
+
}
|
|
231
|
+
const transaction = this.db.transaction([JWT_MINTS_STORE], "readonly");
|
|
232
|
+
return new Promise((resolve, reject) => {
|
|
233
|
+
transaction.onerror = () => reject(transaction.error);
|
|
234
|
+
const request = transaction.objectStore(JWT_MINTS_STORE).get(key);
|
|
235
|
+
request.onsuccess = () => {
|
|
236
|
+
const entry = request.result;
|
|
237
|
+
if (!entry?.data?.token ||
|
|
238
|
+
entry.dbVersion !== DB_VERSION ||
|
|
239
|
+
!entry.timestamp) {
|
|
240
|
+
this.stats.misses++;
|
|
241
|
+
resolve({ data: null, isStale: true, age: 0, metadata: null });
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
const now = Date.now();
|
|
245
|
+
const age = now - entry.timestamp;
|
|
246
|
+
const isStale = entry.data.exp <= Math.floor(now / 1000);
|
|
247
|
+
this.stats.hits++;
|
|
248
|
+
resolve({
|
|
249
|
+
data: entry.data,
|
|
250
|
+
isStale,
|
|
251
|
+
age,
|
|
252
|
+
metadata: {
|
|
253
|
+
key,
|
|
254
|
+
timestamp: entry.timestamp,
|
|
255
|
+
ttl: Math.max((entry.data.exp - entry.data.iat) * 1000, 0),
|
|
256
|
+
version: "1.0.0",
|
|
257
|
+
dbVersion: DB_VERSION,
|
|
258
|
+
},
|
|
259
|
+
});
|
|
260
|
+
};
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
async clearJwtMint(key) {
|
|
264
|
+
await this.init();
|
|
265
|
+
if (!this.db)
|
|
266
|
+
throw new Error("Database not initialized");
|
|
267
|
+
if (!this.validateDatabaseSchema()) {
|
|
268
|
+
throw new Error("Database schema validation failed - required object stores not found");
|
|
269
|
+
}
|
|
270
|
+
const transaction = this.db.transaction([JWT_MINTS_STORE], "readwrite");
|
|
271
|
+
return new Promise((resolve, reject) => {
|
|
272
|
+
transaction.onerror = () => reject(transaction.error);
|
|
273
|
+
transaction.oncomplete = () => resolve();
|
|
274
|
+
transaction.objectStore(JWT_MINTS_STORE).delete(key);
|
|
275
|
+
});
|
|
276
|
+
}
|
|
193
277
|
async clearHerdModules() {
|
|
194
278
|
await this.init();
|
|
195
279
|
if (!this.db)
|
|
@@ -4,5 +4,4 @@ import { IClientAbilitiesJwtPublicKeyEntry, IClientAbilitiesTokenMint } from "..
|
|
|
4
4
|
import { IWebResponseCompatible } from "../types/requests";
|
|
5
5
|
export declare function mint_client_abilities_token(client: SupabaseClient<Database>): Promise<IWebResponseCompatible<IClientAbilitiesTokenMint>>;
|
|
6
6
|
export declare function get_client_abilities_jwt_public_keys(client: SupabaseClient<Database>): Promise<IWebResponseCompatible<IClientAbilitiesJwtPublicKeyEntry[]>>;
|
|
7
|
-
/** Cryptographic check only; returns the same mint envelope from the edge. */
|
|
8
7
|
export declare function verify_client_abilities_token(mint: IClientAbilitiesTokenMint, publicKeys: IClientAbilitiesJwtPublicKeyEntry[]): Promise<IClientAbilitiesTokenMint>;
|
|
@@ -32,7 +32,6 @@ export async function get_client_abilities_jwt_public_keys(client) {
|
|
|
32
32
|
}
|
|
33
33
|
return IWebResponse.success(keys).to_compatible();
|
|
34
34
|
}
|
|
35
|
-
/** Cryptographic check only; returns the same mint envelope from the edge. */
|
|
36
35
|
export async function verify_client_abilities_token(mint, publicKeys) {
|
|
37
36
|
if (publicKeys.length === 0) {
|
|
38
37
|
throw new Error("no client abilities JWT public keys configured");
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { dequal } from "dequal";
|
|
2
|
+
function withoutFetchTimestamp({ timestamp_last_refreshed: _timestamp, ...module }) {
|
|
3
|
+
return module;
|
|
4
|
+
}
|
|
5
|
+
export function herdModulesSemanticallyEqual(current, incoming) {
|
|
6
|
+
if (current === incoming) {
|
|
7
|
+
return true;
|
|
8
|
+
}
|
|
9
|
+
if (current.length !== incoming.length) {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
if (current.length === 0) {
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
const currentByHerdId = new Map(current.map((module) => [
|
|
16
|
+
String(module.herd.id),
|
|
17
|
+
withoutFetchTimestamp(module),
|
|
18
|
+
]));
|
|
19
|
+
for (const incomingModule of incoming) {
|
|
20
|
+
const herdId = String(incomingModule.herd.id);
|
|
21
|
+
const existing = currentByHerdId.get(herdId);
|
|
22
|
+
if (!existing ||
|
|
23
|
+
!dequal(existing, withoutFetchTimestamp(incomingModule))) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { SupabaseClient } from "@supabase/supabase-js";
|
|
2
2
|
import { Database } from "../types/supabase";
|
|
3
|
-
import { IPubsubTokenMint } from "../types/pubsub_token";
|
|
3
|
+
import { IPubsubJwtPublicKeyEntry, IPubsubTokenMint } from "../types/pubsub_token";
|
|
4
4
|
import { IWebResponseCompatible } from "../types/requests";
|
|
5
5
|
export declare function mint_pubsub_token(client: SupabaseClient<Database>): Promise<IWebResponseCompatible<IPubsubTokenMint>>;
|
|
6
|
+
export declare function get_pubsub_jwt_public_keys(client: SupabaseClient<Database>): Promise<IWebResponseCompatible<IPubsubJwtPublicKeyEntry[]>>;
|
|
7
|
+
export declare function verify_pubsub_token(mint: IPubsubTokenMint, publicKeys: IPubsubJwtPublicKeyEntry[]): Promise<IPubsubTokenMint>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as jose from "jose";
|
|
1
2
|
import { IWebResponse } from "../types/requests";
|
|
2
3
|
export async function mint_pubsub_token(client) {
|
|
3
4
|
const { data, error } = await client.functions.invoke("mint-pubsub-token", {
|
|
@@ -12,3 +13,43 @@ export async function mint_pubsub_token(client) {
|
|
|
12
13
|
}
|
|
13
14
|
return IWebResponse.success(mint).to_compatible();
|
|
14
15
|
}
|
|
16
|
+
export async function get_pubsub_jwt_public_keys(client) {
|
|
17
|
+
const { data, error } = await client.rpc("get_pubsub_jwt_public_keys");
|
|
18
|
+
if (error) {
|
|
19
|
+
return IWebResponse.error(error.message).to_compatible();
|
|
20
|
+
}
|
|
21
|
+
const entries = Array.isArray(data) ? data : [];
|
|
22
|
+
const keys = [];
|
|
23
|
+
for (const row of entries) {
|
|
24
|
+
if (row &&
|
|
25
|
+
typeof row === "object" &&
|
|
26
|
+
"kid" in row &&
|
|
27
|
+
"jwk" in row &&
|
|
28
|
+
typeof row.kid === "string" &&
|
|
29
|
+
row.jwk &&
|
|
30
|
+
typeof row.jwk === "object") {
|
|
31
|
+
keys.push({ kid: row.kid, jwk: row.jwk });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return IWebResponse.success(keys).to_compatible();
|
|
35
|
+
}
|
|
36
|
+
export async function verify_pubsub_token(mint, publicKeys) {
|
|
37
|
+
if (publicKeys.length === 0) {
|
|
38
|
+
throw new Error("no pubsub JWT public keys configured");
|
|
39
|
+
}
|
|
40
|
+
const header = jose.decodeProtectedHeader(mint.token);
|
|
41
|
+
const kid = header.kid;
|
|
42
|
+
if (!kid || typeof kid !== "string") {
|
|
43
|
+
throw new Error("pubsub JWT missing kid");
|
|
44
|
+
}
|
|
45
|
+
const entry = publicKeys.find((k) => k.kid === kid);
|
|
46
|
+
if (!entry) {
|
|
47
|
+
throw new Error(`unknown pubsub JWT kid: ${kid}`);
|
|
48
|
+
}
|
|
49
|
+
const alg = typeof entry.jwk.alg === "string" && entry.jwk.alg
|
|
50
|
+
? entry.jwk.alg
|
|
51
|
+
: header.alg ?? "ES256";
|
|
52
|
+
const verifyKey = await jose.importJWK(entry.jwk, alg);
|
|
53
|
+
await jose.jwtVerify(mint.token, verifyKey, { algorithms: [alg] });
|
|
54
|
+
return mint;
|
|
55
|
+
}
|
|
@@ -7,4 +7,3 @@ export declare function get_versions_software_by_system(client: SupabaseClient<D
|
|
|
7
7
|
export declare function create_version_software(client: SupabaseClient<Database>, newVersionSoftware: VersionsSoftwareInsert): Promise<IWebResponseCompatible<IVersionsSoftware | null>>;
|
|
8
8
|
export declare function update_version_software(client: SupabaseClient<Database>, version_id: number, updatedVersionSoftware: Partial<VersionsSoftwareInsert>): Promise<IWebResponseCompatible<IVersionsSoftware | null>>;
|
|
9
9
|
export declare function delete_version_software(client: SupabaseClient<Database>, version_id: number): Promise<IWebResponseCompatible<IVersionsSoftware | null>>;
|
|
10
|
-
export declare function get_versions_software_by_created_by(client: SupabaseClient<Database>, user_id: string): Promise<IWebResponseCompatible<IVersionsSoftware[]>>;
|
|
@@ -75,17 +75,3 @@ export async function delete_version_software(client, version_id) {
|
|
|
75
75
|
}
|
|
76
76
|
return IWebResponse.success(data).to_compatible();
|
|
77
77
|
}
|
|
78
|
-
export async function get_versions_software_by_created_by(client, user_id) {
|
|
79
|
-
const { data, error } = await client
|
|
80
|
-
.from("versions_software")
|
|
81
|
-
.select("*")
|
|
82
|
-
.eq("created_by", user_id)
|
|
83
|
-
.order("created_at", { ascending: false });
|
|
84
|
-
if (error) {
|
|
85
|
-
return IWebResponse.error(error.message).to_compatible();
|
|
86
|
-
}
|
|
87
|
-
if (!data) {
|
|
88
|
-
return IWebResponse.error("No software versions found for user").to_compatible();
|
|
89
|
-
}
|
|
90
|
-
return IWebResponse.success(data).to_compatible();
|
|
91
|
-
}
|
|
@@ -3,4 +3,3 @@ import { IWebResponseCompatible } from "../types/requests";
|
|
|
3
3
|
import { SupabaseClient } from "@supabase/supabase-js";
|
|
4
4
|
export declare function server_get_versions_software(client?: SupabaseClient): Promise<IWebResponseCompatible<IVersionsSoftwareWithBuildUrl[]>>;
|
|
5
5
|
export declare function server_get_versions_software_by_system(system: string, client?: SupabaseClient): Promise<IWebResponseCompatible<IVersionsSoftwareWithBuildUrl[]>>;
|
|
6
|
-
export declare function server_get_versions_software_by_created_by(user_id: string, client?: SupabaseClient): Promise<IWebResponseCompatible<IVersionsSoftwareWithBuildUrl[]>>;
|
|
@@ -53,19 +53,3 @@ export async function server_get_versions_software_by_system(system, client) {
|
|
|
53
53
|
const withUrls = await attachBuildArtifactUrls(data, client);
|
|
54
54
|
return IWebResponse.success(withUrls).to_compatible();
|
|
55
55
|
}
|
|
56
|
-
export async function server_get_versions_software_by_created_by(user_id, client) {
|
|
57
|
-
const supabase = client || (await newServerClient());
|
|
58
|
-
const { data, error } = await supabase
|
|
59
|
-
.from("versions_software")
|
|
60
|
-
.select("*")
|
|
61
|
-
.eq("created_by", user_id)
|
|
62
|
-
.order("created_at", { ascending: false });
|
|
63
|
-
if (error) {
|
|
64
|
-
return IWebResponse.error(error.message).to_compatible();
|
|
65
|
-
}
|
|
66
|
-
if (!data) {
|
|
67
|
-
return IWebResponse.error("No software versions found for user").to_compatible();
|
|
68
|
-
}
|
|
69
|
-
const withUrls = await attachBuildArtifactUrls(data, client);
|
|
70
|
-
return IWebResponse.success(withUrls).to_compatible();
|
|
71
|
-
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { useEffect, useCallback, useRef, useMemo } from "react";
|
|
1
|
+
import { useEffect, useCallback, useRef, useMemo, startTransition } from "react";
|
|
2
2
|
import { useAppDispatch } from "../store/hooks";
|
|
3
|
-
import { useStore } from "react-redux";
|
|
4
3
|
import { EnumScoutStateStatus, setHerdModules, setStatus, setHerdModulesLoadingState, setHerdModulesLoadedInMs, setHerdModulesApiServerProcessingDuration, setHerdModulesApiTotalRequestDuration, setUserApiDuration, setDataProcessingDuration, setCacheLoadDuration, setUser, setDataSource, setDataSourceInfo, } from "../store/scout";
|
|
5
4
|
import { EnumHerdModulesLoadingState } from "../types/herd_module";
|
|
6
5
|
import { server_load_herd_modules } from "../helpers/herds";
|
|
@@ -8,6 +7,11 @@ import { scoutCache } from "../helpers/cache";
|
|
|
8
7
|
import { EnumDataSource } from "../types/data_source";
|
|
9
8
|
import { EnumWebResponse } from "../types/requests";
|
|
10
9
|
import { createBrowserClient } from "@supabase/ssr";
|
|
10
|
+
function dispatchInTransition(dispatch, action) {
|
|
11
|
+
startTransition(() => {
|
|
12
|
+
dispatch(action);
|
|
13
|
+
});
|
|
14
|
+
}
|
|
11
15
|
/**
|
|
12
16
|
* Hook for refreshing scout data with detailed timing measurements and cache-first loading
|
|
13
17
|
*
|
|
@@ -38,7 +42,6 @@ export function useScoutRefresh(options = {}) {
|
|
|
38
42
|
onlineRefetchMinIntervalMs = 15 * 1000, // 15 seconds
|
|
39
43
|
} = options;
|
|
40
44
|
const dispatch = useAppDispatch();
|
|
41
|
-
const store = useStore();
|
|
42
45
|
const refreshInProgressRef = useRef(false);
|
|
43
46
|
const lastQueryAtRef = useRef(0);
|
|
44
47
|
const supabase = useMemo(() => {
|
|
@@ -98,7 +101,7 @@ export function useScoutRefresh(options = {}) {
|
|
|
98
101
|
const totalMs = Date.now() - startTotal;
|
|
99
102
|
timingRefs.current.userApiDuration = totalMs;
|
|
100
103
|
dispatch(setUserApiDuration(totalMs));
|
|
101
|
-
dispatch
|
|
104
|
+
dispatchInTransition(dispatch, setUser(data.user));
|
|
102
105
|
return data.user;
|
|
103
106
|
}
|
|
104
107
|
catch (e) {
|
|
@@ -116,7 +119,7 @@ export function useScoutRefresh(options = {}) {
|
|
|
116
119
|
const totalMs = Date.now() - startTotal;
|
|
117
120
|
timingRefs.current.userApiDuration = totalMs;
|
|
118
121
|
dispatch(setUserApiDuration(totalMs));
|
|
119
|
-
dispatch
|
|
122
|
+
dispatchInTransition(dispatch, setUser(data.user));
|
|
120
123
|
return data.user;
|
|
121
124
|
}
|
|
122
125
|
}
|
|
@@ -155,7 +158,7 @@ export function useScoutRefresh(options = {}) {
|
|
|
155
158
|
}));
|
|
156
159
|
// Update the store with cached data
|
|
157
160
|
console.log(`[useScoutRefresh] Updating store with cached herd modules`);
|
|
158
|
-
dispatch
|
|
161
|
+
dispatchInTransition(dispatch, setHerdModules(cachedHerdModules));
|
|
159
162
|
dispatch(setHerdModulesLoadingState(EnumHerdModulesLoadingState.SUCCESSFULLY_LOADED));
|
|
160
163
|
// If cache is fresh, we still background fetch but don't wait (only when online)
|
|
161
164
|
if (!cacheResult.isStale) {
|
|
@@ -204,7 +207,7 @@ export function useScoutRefresh(options = {}) {
|
|
|
204
207
|
}
|
|
205
208
|
// Update store with fresh data from background request
|
|
206
209
|
console.log(`[useScoutRefresh] Updating store with background herd modules`);
|
|
207
|
-
dispatch
|
|
210
|
+
dispatchInTransition(dispatch, setHerdModules(backgroundHerdModulesResult.data));
|
|
208
211
|
// Update data source to DATABASE
|
|
209
212
|
dispatch(setDataSource(EnumDataSource.DATABASE));
|
|
210
213
|
dispatch(setDataSourceInfo({
|
|
@@ -245,7 +248,7 @@ export function useScoutRefresh(options = {}) {
|
|
|
245
248
|
source: EnumDataSource.CACHE,
|
|
246
249
|
timestamp: Date.now(),
|
|
247
250
|
}));
|
|
248
|
-
dispatch
|
|
251
|
+
dispatchInTransition(dispatch, setHerdModules(cachedHerdModules));
|
|
249
252
|
dispatch(setHerdModulesLoadingState(EnumHerdModulesLoadingState.SUCCESSFULLY_LOADED));
|
|
250
253
|
}
|
|
251
254
|
else {
|
|
@@ -327,11 +330,11 @@ export function useScoutRefresh(options = {}) {
|
|
|
327
330
|
await scoutCache.setHerdModules(compatible_new_herd_modules, cacheTtlMs);
|
|
328
331
|
});
|
|
329
332
|
}
|
|
330
|
-
// Step 4:
|
|
333
|
+
// Step 4: Update store with fresh data (reducer skips no-op updates)
|
|
331
334
|
const dataProcessingStartTime = Date.now();
|
|
332
335
|
// Update store with new data
|
|
333
336
|
console.log(`[useScoutRefresh] Updating store with fresh herd modules`);
|
|
334
|
-
dispatch
|
|
337
|
+
dispatchInTransition(dispatch, setHerdModules(compatible_new_herd_modules));
|
|
335
338
|
dispatch(setHerdModulesLoadingState(EnumHerdModulesLoadingState.SUCCESSFULLY_LOADED));
|
|
336
339
|
const dataProcessingDuration = Date.now() - dataProcessingStartTime;
|
|
337
340
|
timingRefs.current.dataProcessingDuration = dataProcessingDuration;
|
|
@@ -367,7 +370,6 @@ export function useScoutRefresh(options = {}) {
|
|
|
367
370
|
}
|
|
368
371
|
}, [
|
|
369
372
|
dispatch,
|
|
370
|
-
store,
|
|
371
373
|
supabase,
|
|
372
374
|
onRefreshComplete,
|
|
373
375
|
cacheFirst,
|
package/dist/index.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export * from "./types/events";
|
|
|
12
12
|
export * from "./types/connectivity";
|
|
13
13
|
export * from "./types/pubsub_token";
|
|
14
14
|
export * from "./types/client_abilities_token";
|
|
15
|
+
export * from "./types/jwt_mint";
|
|
15
16
|
export * from "./helpers/analysis_usage";
|
|
16
17
|
export * from "./helpers/artifacts";
|
|
17
18
|
export * from "./helpers/auth";
|
package/dist/index.js
CHANGED
|
@@ -14,6 +14,7 @@ export * from "./types/events";
|
|
|
14
14
|
export * from "./types/connectivity";
|
|
15
15
|
export * from "./types/pubsub_token";
|
|
16
16
|
export * from "./types/client_abilities_token";
|
|
17
|
+
export * from "./types/jwt_mint";
|
|
17
18
|
// Helpers
|
|
18
19
|
export * from "./helpers/analysis_usage";
|
|
19
20
|
export * from "./helpers/artifacts";
|
|
@@ -3,30 +3,31 @@ import { type ReactNode } from "react";
|
|
|
3
3
|
import { SupabaseClient } from "@supabase/supabase-js";
|
|
4
4
|
import { Database } from "../types/supabase";
|
|
5
5
|
import { IClientAbilitiesTokenMint } from "../types/client_abilities_token";
|
|
6
|
+
import { IPubsubTokenMint } from "../types/pubsub_token";
|
|
7
|
+
import { EnumJwtMintStatus } from "../types/jwt_mint";
|
|
6
8
|
export interface ClientAbilitiesMintState {
|
|
7
|
-
/** Set only after jose verification against DB pubkeys. */
|
|
8
9
|
mint: IClientAbilitiesTokenMint | null;
|
|
9
|
-
|
|
10
|
+
status: EnumJwtMintStatus;
|
|
10
11
|
error: string | null;
|
|
11
12
|
refreshMint: () => Promise<void>;
|
|
12
13
|
}
|
|
13
14
|
export interface ScoutRefreshAbilityParams {
|
|
14
|
-
/** Mint and verify a client abilities JWT. Default: `true`. */
|
|
15
15
|
mintClientAbilitiesToken?: boolean;
|
|
16
|
-
|
|
17
|
-
* Expected JWT lifetime in seconds (default 1 hour).
|
|
18
|
-
* Align with edge `CLIENT_ABILITIES_TOKEN_TTL_SEC`; used to schedule re-mint
|
|
19
|
-
* (falls back when `exp` is missing; otherwise uses `exp` from the mint).
|
|
20
|
-
*/
|
|
16
|
+
mintPubsubToken?: boolean;
|
|
21
17
|
clientAbilitiesTokenTtlSec?: number;
|
|
22
|
-
|
|
18
|
+
pubsubTokenTtlSec?: number;
|
|
23
19
|
refreshBeforeExpirySec?: number;
|
|
24
|
-
/** Ms to cache pubkey RPC results (default 10 min). */
|
|
25
20
|
publicKeysCacheTtlMs?: number;
|
|
26
21
|
}
|
|
22
|
+
export interface PubsubTokenMintState {
|
|
23
|
+
mint: IPubsubTokenMint | null;
|
|
24
|
+
status: EnumJwtMintStatus;
|
|
25
|
+
error: string | null;
|
|
26
|
+
refreshMint: () => Promise<void>;
|
|
27
|
+
}
|
|
27
28
|
export declare function useSupabase(): SupabaseClient<Database>;
|
|
28
|
-
/** Verified client abilities JWT (minted on a schedule by ScoutRefreshProvider). */
|
|
29
29
|
export declare function useClientAbilitiesMint(): ClientAbilitiesMintState;
|
|
30
|
+
export declare function usePubsubTokenMint(): PubsubTokenMintState;
|
|
30
31
|
export interface ScoutRefreshProviderProps extends UseScoutRefreshOptions {
|
|
31
32
|
children: ReactNode;
|
|
32
33
|
abilityParams?: ScoutRefreshAbilityParams;
|