@promptbook/cli 0.112.0-110 → 0.112.0-112
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/apps/agents-server/src/app/admin/servers/CreateServerDialog.tsx +16 -0
- package/apps/agents-server/src/app/admin/servers/useCreateServerWizard.ts +31 -5
- package/apps/agents-server/src/app/agents/[agentName]/_utils.ts +7 -5
- package/apps/agents-server/src/app/agents/[agentName]/api/user-chats/[chatId]/stream/route.ts +4 -86
- package/apps/agents-server/src/app/api/admin/servers/route.ts +5 -0
- package/apps/agents-server/src/app/api/metadata/route.ts +4 -0
- package/apps/agents-server/src/components/ApplicationErrorPage/ApplicationErrorPage.tsx +118 -12
- package/apps/agents-server/src/database/customJavascript.ts +62 -1
- package/apps/agents-server/src/database/customStylesheet.ts +60 -1
- package/apps/agents-server/src/database/getMetadata.ts +84 -3
- package/apps/agents-server/src/instrumentation.ts +3 -0
- package/apps/agents-server/src/utils/agentRouting/resolveAgentRouteTarget.ts +27 -62
- package/apps/agents-server/src/utils/errorReporting/applicationErrorHandling.ts +45 -0
- package/apps/agents-server/src/utils/errorReporting/refreshApplicationDocument.ts +10 -0
- package/apps/agents-server/src/utils/errorReporting/registerServerErrorSentryLogging.ts +331 -0
- package/apps/agents-server/src/utils/errorReporting/sendApplicationErrorReportToSentry.ts +8 -153
- package/apps/agents-server/src/utils/errorReporting/sentryStore.ts +177 -0
- package/apps/agents-server/src/utils/importAgent.ts +1 -57
- package/apps/agents-server/src/utils/importAgentWithFallback.ts +0 -10
- package/apps/agents-server/src/utils/serverManagement/createManagedServer/bootstrapManagedServer.ts +3 -1
- package/apps/agents-server/src/utils/serverManagement/createManagedServer/normalizeCreateServerInput.ts +6 -0
- package/apps/agents-server/src/utils/serverManagement/createManagedServer/seedServerDefaultAgents.ts +7 -3
- package/apps/agents-server/src/utils/serverManagement/createManagedServer.ts +5 -0
- package/apps/agents-server/src/utils/userChat/listUserChats.ts +109 -0
- package/apps/agents-server/src/utils/userChat.ts +0 -1
- package/esm/index.es.js +39 -13
- package/esm/index.es.js.map +1 -1
- package/esm/src/cli/cli-commands/agents-server/startAgentsServer.d.ts +2 -1
- package/esm/src/cli/cli-commands/agents-server/startAgentsServer.test.d.ts +1 -0
- package/esm/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.d.ts +22 -0
- package/esm/src/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/cli/cli-commands/agents-server/startAgentsServer.ts +23 -2
- package/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.ts +91 -14
- package/src/other/templates/getTemplatesPipelineCollection.ts +801 -652
- package/src/version.ts +2 -2
- package/src/versions.txt +2 -0
- package/umd/index.umd.js +39 -13
- package/umd/index.umd.js.map +1 -1
- package/umd/src/cli/cli-commands/agents-server/startAgentsServer.d.ts +2 -1
- package/umd/src/cli/cli-commands/agents-server/startAgentsServer.test.d.ts +1 -0
- package/umd/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.d.ts +22 -0
- package/umd/src/version.d.ts +1 -1
- package/apps/agents-server/src/utils/userChat/getUserChatRevision.ts +0 -145
|
@@ -7,6 +7,11 @@ import { $provideSupabase } from './$provideSupabase';
|
|
|
7
7
|
*/
|
|
8
8
|
const CUSTOM_STYLESHEET_TABLE_BASENAME = 'CustomStylesheet';
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Process-level cache lifetime for custom stylesheet rows.
|
|
12
|
+
*/
|
|
13
|
+
const CUSTOM_STYLESHEET_CACHE_TTL_MS = 30_000;
|
|
14
|
+
|
|
10
15
|
/**
|
|
11
16
|
* Stored CustomStylesheet row shape.
|
|
12
17
|
*
|
|
@@ -82,6 +87,28 @@ type DynamicSupabaseClient = {
|
|
|
82
87
|
from: (tableName: string) => DynamicCustomStylesheetTableQuery;
|
|
83
88
|
};
|
|
84
89
|
|
|
90
|
+
/**
|
|
91
|
+
* Cached custom stylesheet rows keyed by the resolved table name.
|
|
92
|
+
*
|
|
93
|
+
* @private
|
|
94
|
+
*/
|
|
95
|
+
const cachedCustomStylesheetsByTableName = new Map<
|
|
96
|
+
string,
|
|
97
|
+
{
|
|
98
|
+
readonly loadedAt: number;
|
|
99
|
+
readonly rowsPromise: Promise<CustomStylesheetRow[]>;
|
|
100
|
+
}
|
|
101
|
+
>();
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Clears process-level stylesheet cache after admin writes.
|
|
105
|
+
*
|
|
106
|
+
* @public
|
|
107
|
+
*/
|
|
108
|
+
export function invalidateCustomStylesheetCache(): void {
|
|
109
|
+
cachedCustomStylesheetsByTableName.clear();
|
|
110
|
+
}
|
|
111
|
+
|
|
85
112
|
/**
|
|
86
113
|
* Resolves the prefixed table name for CustomStylesheet.
|
|
87
114
|
*
|
|
@@ -127,8 +154,37 @@ function getCustomStylesheetClient(): DynamicSupabaseClient {
|
|
|
127
154
|
*/
|
|
128
155
|
export async function listCustomStylesheets(): Promise<CustomStylesheetRow[]> {
|
|
129
156
|
const table = await getCustomStylesheetTableName();
|
|
130
|
-
const
|
|
157
|
+
const cachedStylesheets = cachedCustomStylesheetsByTableName.get(table);
|
|
158
|
+
if (cachedStylesheets && Date.now() - cachedStylesheets.loadedAt < CUSTOM_STYLESHEET_CACHE_TTL_MS) {
|
|
159
|
+
return cachedStylesheets.rowsPromise;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const rowsPromise = loadCustomStylesheetsFromDatabase(table);
|
|
163
|
+
cachedCustomStylesheetsByTableName.set(table, {
|
|
164
|
+
loadedAt: Date.now(),
|
|
165
|
+
rowsPromise,
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
try {
|
|
169
|
+
return await rowsPromise;
|
|
170
|
+
} catch (error) {
|
|
171
|
+
if (cachedCustomStylesheetsByTableName.get(table)?.rowsPromise === rowsPromise) {
|
|
172
|
+
cachedCustomStylesheetsByTableName.delete(table);
|
|
173
|
+
}
|
|
174
|
+
throw error;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
131
177
|
|
|
178
|
+
/**
|
|
179
|
+
* Reads custom stylesheet rows from the database.
|
|
180
|
+
*
|
|
181
|
+
* @param table - Resolved CustomStylesheet table name.
|
|
182
|
+
* @returns Stored stylesheet rows.
|
|
183
|
+
*
|
|
184
|
+
* @private
|
|
185
|
+
*/
|
|
186
|
+
async function loadCustomStylesheetsFromDatabase(table: string): Promise<CustomStylesheetRow[]> {
|
|
187
|
+
const supabase = getCustomStylesheetClient();
|
|
132
188
|
const { data, error } = await supabase.from(table).select('*').order('createdAt', { ascending: true });
|
|
133
189
|
|
|
134
190
|
if (error) {
|
|
@@ -206,6 +262,7 @@ export async function saveCustomStylesheetFile({
|
|
|
206
262
|
throw new Error(`Failed to save custom stylesheet: ${error.message || String(error)}`);
|
|
207
263
|
}
|
|
208
264
|
|
|
265
|
+
invalidateCustomStylesheetCache();
|
|
209
266
|
return data as CustomStylesheetRow;
|
|
210
267
|
}
|
|
211
268
|
|
|
@@ -229,4 +286,6 @@ export async function deleteCustomStylesheetFile(id: number): Promise<void> {
|
|
|
229
286
|
|
|
230
287
|
throw new Error(`Failed to delete custom stylesheet: ${error.message || String(error)}`);
|
|
231
288
|
}
|
|
289
|
+
|
|
290
|
+
invalidateCustomStylesheetCache();
|
|
232
291
|
}
|
|
@@ -10,6 +10,45 @@ import { cache } from 'react';
|
|
|
10
10
|
*/
|
|
11
11
|
const metadataDefaultsMap = new Map<string, string>(metadataDefaults.map((metadata) => [metadata.key, metadata.value]));
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Process-level cache lifetime for metadata reads.
|
|
15
|
+
*
|
|
16
|
+
* @private Internal helper for metadata lookups in `apps/agents-server`.
|
|
17
|
+
*/
|
|
18
|
+
const METADATA_CACHE_TTL_MS = 30_000;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Cached metadata table payload keyed by the resolved table name.
|
|
22
|
+
*
|
|
23
|
+
* @private Internal helper for metadata lookups in `apps/agents-server`.
|
|
24
|
+
*/
|
|
25
|
+
const cachedMetadataValuesByTableName = new Map<
|
|
26
|
+
string,
|
|
27
|
+
{
|
|
28
|
+
readonly loadedAt: number;
|
|
29
|
+
readonly valuesPromise: Promise<Map<string, string | null>>;
|
|
30
|
+
}
|
|
31
|
+
>();
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Metadata row shape loaded by the lightweight metadata select.
|
|
35
|
+
*
|
|
36
|
+
* @private Internal helper for metadata lookups in `apps/agents-server`.
|
|
37
|
+
*/
|
|
38
|
+
type MetadataValueRow = {
|
|
39
|
+
readonly key: string;
|
|
40
|
+
readonly value: string | null;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Clears process-level metadata cache after admin metadata writes.
|
|
45
|
+
*
|
|
46
|
+
* @public exported from `apps/agents-server`
|
|
47
|
+
*/
|
|
48
|
+
export function invalidateMetadataCache(): void {
|
|
49
|
+
cachedMetadataValuesByTableName.clear();
|
|
50
|
+
}
|
|
51
|
+
|
|
13
52
|
/**
|
|
14
53
|
* Loads the full metadata table once per request so callers can cheaply project subsets.
|
|
15
54
|
*
|
|
@@ -18,17 +57,59 @@ const metadataDefaultsMap = new Map<string, string>(metadataDefaults.map((metada
|
|
|
18
57
|
* @private Internal helper for batched metadata lookups in `apps/agents-server`.
|
|
19
58
|
*/
|
|
20
59
|
const loadAllMetadataValues = cache(async (): Promise<Map<string, string | null>> => {
|
|
21
|
-
const supabase = $provideSupabase();
|
|
22
60
|
const table = await $getTableName('Metadata');
|
|
61
|
+
return loadCachedMetadataValues(table);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Loads metadata values using a short process-level cache shared by consecutive requests.
|
|
66
|
+
*
|
|
67
|
+
* @param table - Resolved metadata table name.
|
|
68
|
+
* @returns Metadata values keyed by metadata key.
|
|
69
|
+
*
|
|
70
|
+
* @private Internal helper for metadata lookups in `apps/agents-server`.
|
|
71
|
+
*/
|
|
72
|
+
async function loadCachedMetadataValues(table: string): Promise<Map<string, string | null>> {
|
|
73
|
+
const cachedMetadataValues = cachedMetadataValuesByTableName.get(table);
|
|
74
|
+
if (cachedMetadataValues && Date.now() - cachedMetadataValues.loadedAt < METADATA_CACHE_TTL_MS) {
|
|
75
|
+
return cachedMetadataValues.valuesPromise;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const valuesPromise = loadMetadataValuesFromDatabase(table);
|
|
79
|
+
cachedMetadataValuesByTableName.set(table, {
|
|
80
|
+
loadedAt: Date.now(),
|
|
81
|
+
valuesPromise,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
return await valuesPromise;
|
|
86
|
+
} catch (error) {
|
|
87
|
+
if (cachedMetadataValuesByTableName.get(table)?.valuesPromise === valuesPromise) {
|
|
88
|
+
cachedMetadataValuesByTableName.delete(table);
|
|
89
|
+
}
|
|
90
|
+
throw error;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Reads all persisted metadata values from the database.
|
|
96
|
+
*
|
|
97
|
+
* @param table - Resolved metadata table name.
|
|
98
|
+
* @returns Metadata values keyed by metadata key.
|
|
99
|
+
*
|
|
100
|
+
* @private Internal helper for metadata lookups in `apps/agents-server`.
|
|
101
|
+
*/
|
|
102
|
+
async function loadMetadataValuesFromDatabase(table: string): Promise<Map<string, string | null>> {
|
|
103
|
+
const supabase = $provideSupabase();
|
|
23
104
|
const { data } = await supabase.from(table).select('key, value');
|
|
24
105
|
|
|
25
106
|
const loadedMap = new Map<string, string | null>();
|
|
26
|
-
for (const row of data ?? []) {
|
|
107
|
+
for (const row of (data ?? []) as Array<MetadataValueRow>) {
|
|
27
108
|
loadedMap.set(row.key, row.value);
|
|
28
109
|
}
|
|
29
110
|
|
|
30
111
|
return loadedMap;
|
|
31
|
-
}
|
|
112
|
+
}
|
|
32
113
|
|
|
33
114
|
/**
|
|
34
115
|
* Get metadata value by key
|
|
@@ -10,6 +10,9 @@ export async function register(): Promise<void> {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
try {
|
|
13
|
+
const { registerServerErrorSentryLogging } = await import('./utils/errorReporting/registerServerErrorSentryLogging');
|
|
14
|
+
registerServerErrorSentryLogging();
|
|
15
|
+
|
|
13
16
|
const { registerNodeRuntimeInstrumentation } = await import('./instrumentation-node');
|
|
14
17
|
await registerNodeRuntimeInstrumentation();
|
|
15
18
|
} catch (error) {
|
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
import { $provideServer } from '../../tools/$provideServer';
|
|
2
2
|
import { $provideAgentCollectionForServer } from '../../tools/$provideAgentCollectionForServer';
|
|
3
|
-
import { $getTableName } from '../../database/$getTableName';
|
|
4
|
-
import { $provideSupabaseForServer } from '../../database/$provideSupabaseForServer';
|
|
5
3
|
import { $provideAgentReferenceResolver } from '../agentReferenceResolver/$provideAgentReferenceResolver';
|
|
6
4
|
import { consumeAgentReferenceResolutionIssues } from '../agentReferenceResolver/AgentReferenceResolutionIssue';
|
|
7
5
|
import { parseBookScopedAgentIdentifier } from '../agentReferenceResolver/bookScopedAgentReferences';
|
|
8
|
-
import { buildAgentNameOrIdFilter } from '../agentIdentifier';
|
|
9
6
|
import { resolvePseudoAgentDescriptor } from '../pseudoAgents';
|
|
10
7
|
import { normalizeAgentName } from '../../../../../src/_packages/core.index';
|
|
11
8
|
import type { PseudoAgentKind } from '../../../../../src/book-2.0/agent-source/pseudoAgentReferences';
|
|
12
|
-
import type { AgentsServerDatabase } from '../../database/schema';
|
|
13
9
|
import { cache } from 'react';
|
|
14
10
|
|
|
15
11
|
/**
|
|
@@ -54,14 +50,6 @@ type PseudoAgentRouteTarget = {
|
|
|
54
50
|
canonicalUrl: string;
|
|
55
51
|
};
|
|
56
52
|
|
|
57
|
-
/**
|
|
58
|
-
* Minimal database row needed to resolve one local route target.
|
|
59
|
-
*/
|
|
60
|
-
type LocalAgentRouteRow = Pick<
|
|
61
|
-
AgentsServerDatabase['public']['Tables']['Agent']['Row'],
|
|
62
|
-
'agentName' | 'permanentId'
|
|
63
|
-
>;
|
|
64
|
-
|
|
65
53
|
/**
|
|
66
54
|
* Target returned for an incoming `/agents/:agentId` route value.
|
|
67
55
|
*/
|
|
@@ -114,11 +102,6 @@ async function resolveAgentRouteTargetUncached(
|
|
|
114
102
|
};
|
|
115
103
|
}
|
|
116
104
|
|
|
117
|
-
const directLocalRouteTarget = await resolveLocalAgentRouteTargetFromDatabase(normalizedReference, localServerUrl);
|
|
118
|
-
if (directLocalRouteTarget) {
|
|
119
|
-
return directLocalRouteTarget;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
105
|
const resolver = await $provideAgentReferenceResolver({ forceRefresh: options?.forceRefresh });
|
|
123
106
|
let resolvedUrlValue: string;
|
|
124
107
|
|
|
@@ -288,21 +271,7 @@ async function resolveLocalAgentRouteTarget(
|
|
|
288
271
|
localServerUrl: string,
|
|
289
272
|
): Promise<AgentRouteTarget | null> {
|
|
290
273
|
const collection = await $provideAgentCollectionForServer();
|
|
291
|
-
const
|
|
292
|
-
const normalizedReference = normalizeAgentName(reference);
|
|
293
|
-
|
|
294
|
-
const agentMatch = agents.find((agent: { agentName: string; permanentId?: string }) => {
|
|
295
|
-
if (agent.agentName === reference || agent.permanentId === reference) {
|
|
296
|
-
return true;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
const normalizedAgentName = normalizeAgentName(agent.agentName);
|
|
300
|
-
if (normalizedAgentName === normalizedReference) {
|
|
301
|
-
return true;
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
return false;
|
|
305
|
-
});
|
|
274
|
+
const agentMatch = await findLocalAgentRouteMatch(collection, reference);
|
|
306
275
|
|
|
307
276
|
if (!agentMatch) {
|
|
308
277
|
return null;
|
|
@@ -318,40 +287,36 @@ async function resolveLocalAgentRouteTarget(
|
|
|
318
287
|
}
|
|
319
288
|
|
|
320
289
|
/**
|
|
321
|
-
*
|
|
290
|
+
* Finds a local agent route match without loading the whole collection when possible.
|
|
322
291
|
*
|
|
323
|
-
*
|
|
324
|
-
*
|
|
325
|
-
*
|
|
326
|
-
*
|
|
327
|
-
* @param reference - Normalized route reference.
|
|
328
|
-
* @param localServerUrl - Normalized URL of the current Agents Server instance.
|
|
329
|
-
* @returns Local route target or `null` when the reference is not a local agent.
|
|
292
|
+
* @param collection - Agents collection backing local routes.
|
|
293
|
+
* @param reference - Normalized route/reference text.
|
|
294
|
+
* @returns Matching local agent profile or `null`.
|
|
330
295
|
*/
|
|
331
|
-
async function
|
|
296
|
+
async function findLocalAgentRouteMatch(
|
|
297
|
+
collection: Awaited<ReturnType<typeof $provideAgentCollectionForServer>>,
|
|
332
298
|
reference: string,
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
const result = await supabase
|
|
338
|
-
.from(agentTable)
|
|
339
|
-
.select('agentName, permanentId')
|
|
340
|
-
.or(buildAgentNameOrIdFilter(reference))
|
|
341
|
-
.is('deletedAt', null)
|
|
342
|
-
.order('createdAt', { ascending: true })
|
|
343
|
-
.limit(1);
|
|
344
|
-
|
|
345
|
-
if (result.error || !result.data || result.data.length === 0) {
|
|
346
|
-
return null;
|
|
299
|
+
): Promise<{ agentName: string; permanentId?: string | null } | null> {
|
|
300
|
+
const directMatch = await collection.findAgentBasicInformation(reference);
|
|
301
|
+
if (directMatch) {
|
|
302
|
+
return directMatch;
|
|
347
303
|
}
|
|
348
304
|
|
|
349
|
-
const
|
|
350
|
-
const
|
|
305
|
+
const agents = await collection.listAgents();
|
|
306
|
+
const normalizedReference = normalizeAgentName(reference);
|
|
351
307
|
|
|
352
|
-
return
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
308
|
+
return (
|
|
309
|
+
agents.find((agent: { agentName: string; permanentId?: string | null }) => {
|
|
310
|
+
if (agent.agentName === reference || agent.permanentId === reference) {
|
|
311
|
+
return true;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const normalizedAgentName = normalizeAgentName(agent.agentName);
|
|
315
|
+
if (normalizedAgentName === normalizedReference) {
|
|
316
|
+
return true;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
return false;
|
|
320
|
+
}) || null
|
|
321
|
+
);
|
|
357
322
|
}
|
|
@@ -80,6 +80,21 @@ const EDGE_DASH_PATTERN = /^-+|-+$/g;
|
|
|
80
80
|
*/
|
|
81
81
|
const CODE_FENCE_PATTERN = /```/g;
|
|
82
82
|
|
|
83
|
+
/**
|
|
84
|
+
* Regex matching webpack/Next.js chunk-load failures caused by stale or missing build assets.
|
|
85
|
+
*/
|
|
86
|
+
const CHUNK_LOAD_ERROR_MESSAGE_PATTERN = /loading (css )?chunk .* failed/i;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Regex matching browsers that fail while fetching one dynamically imported module.
|
|
90
|
+
*/
|
|
91
|
+
const DYNAMIC_IMPORT_FETCH_ERROR_MESSAGE_PATTERN = /failed to fetch dynamically imported module/i;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Regex matching Next.js-managed build asset URLs.
|
|
95
|
+
*/
|
|
96
|
+
const NEXT_BUILD_ASSET_PATH_PATTERN = /\/_next\/static\/(?:chunks|css)\//i;
|
|
97
|
+
|
|
83
98
|
/**
|
|
84
99
|
* Shape sent from the app error boundary to server-side Sentry forwarding.
|
|
85
100
|
*/
|
|
@@ -162,6 +177,32 @@ export function createApplicationErrorDigest(error: ApplicationBoundaryError | n
|
|
|
162
177
|
return (hash >>> 0).toString(DECIMAL_BASE).padStart(DIGEST_LENGTH, '0');
|
|
163
178
|
}
|
|
164
179
|
|
|
180
|
+
/**
|
|
181
|
+
* Detects client-side build-asset errors that are commonly fixed by reloading the document once.
|
|
182
|
+
*
|
|
183
|
+
* @param error - Captured boundary exception.
|
|
184
|
+
* @returns True when the current document should be hard-refreshed instead of only retrying React navigation.
|
|
185
|
+
*/
|
|
186
|
+
export function isApplicationErrorRecoverableByHardRefresh(error: ApplicationBoundaryError | null): boolean {
|
|
187
|
+
const errorName = error?.name?.trim() || '';
|
|
188
|
+
const errorMessage = error?.message?.trim() || '';
|
|
189
|
+
const errorStack = error?.stack?.trim() || '';
|
|
190
|
+
const errorDetails = `${errorName}\n${errorMessage}\n${errorStack}`;
|
|
191
|
+
|
|
192
|
+
if (errorName === 'ChunkLoadError') {
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (CHUNK_LOAD_ERROR_MESSAGE_PATTERN.test(errorDetails)) {
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return (
|
|
201
|
+
DYNAMIC_IMPORT_FETCH_ERROR_MESSAGE_PATTERN.test(errorDetails) &&
|
|
202
|
+
NEXT_BUILD_ASSET_PATH_PATTERN.test(errorDetails)
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
165
206
|
/**
|
|
166
207
|
* Formats descriptive text shown beneath the application error headline.
|
|
167
208
|
*
|
|
@@ -170,6 +211,10 @@ export function createApplicationErrorDigest(error: ApplicationBoundaryError | n
|
|
|
170
211
|
* @returns Friendly summary of what happened.
|
|
171
212
|
*/
|
|
172
213
|
export function describeApplicationError(error: ApplicationBoundaryError | null, serverName: string): string {
|
|
214
|
+
if (isApplicationErrorRecoverableByHardRefresh(error)) {
|
|
215
|
+
return `The browser could not load the latest application files for ${serverName}. Refreshing the page usually resolves this after a deployment or stale cached shell.`;
|
|
216
|
+
}
|
|
217
|
+
|
|
173
218
|
if (error?.message) {
|
|
174
219
|
return `${error.message.trim()} - the server for ${serverName} logged this failure.`;
|
|
175
220
|
}
|