@lunora/do 1.0.0-alpha.29 → 1.0.0-alpha.30
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/index.d.mts +45 -1
- package/dist/index.d.ts +45 -1
- package/dist/index.mjs +3 -3
- package/dist/packem_shared/{ADMIN_FUNCTIONS-Bxdijn66.mjs → ADMIN_FUNCTIONS-CAHLZMj8.mjs} +1 -0
- package/dist/packem_shared/{ROOT_DO_SIZE_WARN_BYTES-CJLKsAHN.mjs → ROOT_DO_SIZE_WARN_BYTES-BA8QOChj.mjs} +129 -4
- package/dist/packem_shared/{serveRelationFanout-C56hKu1O.mjs → serveRelationFanout-BgaNg3Hu.mjs} +1 -1
- package/package.json +2 -1
package/dist/index.d.mts
CHANGED
|
@@ -2531,6 +2531,7 @@ declare const ADMIN_FUNCTIONS: {
|
|
|
2531
2531
|
readonly getCapturedMail: "__lunora_admin__:getCapturedMail";
|
|
2532
2532
|
readonly getFanoutMetrics: "__lunora_admin__:getFanoutMetrics";
|
|
2533
2533
|
readonly getFunctionStats: "__lunora_admin__:getFunctionStats";
|
|
2534
|
+
readonly getIssues: "__lunora_admin__:getIssues";
|
|
2534
2535
|
readonly listSubscriptions: "__lunora_admin__:listSubscriptions";
|
|
2535
2536
|
readonly listTableIndexes: "__lunora_admin__:listTableIndexes";
|
|
2536
2537
|
readonly getLogs: "__lunora_admin__:getLogs";
|
|
@@ -3346,10 +3347,14 @@ type LogLevel = "debug" | "error" | "info" | "warn";
|
|
|
3346
3347
|
/**
|
|
3347
3348
|
* One buffered log line. `functionPath` is the RPC that produced it (when the
|
|
3348
3349
|
* entry came from the RPC dispatch site); `timestamp` is `Date.now()` at the
|
|
3349
|
-
* moment it was pushed.
|
|
3350
|
+
* moment it was pushed. `instance`/`exitCode` are populated for container
|
|
3351
|
+
* lifecycle entries: `instance` correlates the per-instance Durable Object id,
|
|
3352
|
+
* `exitCode` carries the process exit code parsed out of a `stop` event.
|
|
3350
3353
|
*/
|
|
3351
3354
|
interface LogEntry {
|
|
3355
|
+
exitCode?: number;
|
|
3352
3356
|
functionPath?: string;
|
|
3357
|
+
instance?: string;
|
|
3353
3358
|
level: LogLevel;
|
|
3354
3359
|
message: string;
|
|
3355
3360
|
timestamp: number;
|
|
@@ -5589,6 +5594,14 @@ declare abstract class ShardDO {
|
|
|
5589
5594
|
* the panel renders it alongside `ctx.log` lines. Admin-gated by
|
|
5590
5595
|
* `handleAdminRpc`'s caller (the same `LUNORA_ADMIN_TOKEN` bearer as every
|
|
5591
5596
|
* other admin write).
|
|
5597
|
+
*
|
|
5598
|
+
* An `error`-level lifecycle event (a crash/`onError`, or a non-zero-exit
|
|
5599
|
+
* `stop`) is ALSO appended as an `error`-outcome row to the durable
|
|
5600
|
+
* `__lunora_reqlog__` — the same readout `getIssues` groups over — so a
|
|
5601
|
+
* crash-looping container folds into the Issues list right beside Worker
|
|
5602
|
+
* errors (they share the `fingerprintError` hash over `functionPath ::
|
|
5603
|
+
* bucket(message)`). The in-memory buffer stays the live Logs feed; the
|
|
5604
|
+
* durable row is what survives hibernation for triage.
|
|
5592
5605
|
*/
|
|
5593
5606
|
private handleRecordContainerEvent;
|
|
5594
5607
|
/**
|
|
@@ -5777,6 +5790,25 @@ declare abstract class ShardDO {
|
|
|
5777
5790
|
*/
|
|
5778
5791
|
private recordRequestLog;
|
|
5779
5792
|
/**
|
|
5793
|
+
* The single durable-row + Logpush write seam for `__lunora_reqlog__`. Both
|
|
5794
|
+
* the per-dispatch {@link recordRequestLog} and the container-crash path
|
|
5795
|
+
* ({@link handleRecordContainerEvent}) funnel through here so they can't
|
|
5796
|
+
* drift — before this was extracted the container writer persisted the row
|
|
5797
|
+
* but silently skipped the Logpush emit every other error got.
|
|
5798
|
+
*
|
|
5799
|
+
* Best-effort by contract: a SQL failure (e.g. a test double with no `sql`
|
|
5800
|
+
* handle) or a serialization hiccup in the emit must NEVER turn a served
|
|
5801
|
+
* request — or a container event push — into a failed one, so each half is
|
|
5802
|
+
* swallowed independently.
|
|
5803
|
+
*
|
|
5804
|
+
* Errors are always streamed to `console` (rare, high-value — they ride CF
|
|
5805
|
+
* Workers Logs at error level and the dev-server formats them in the
|
|
5806
|
+
* terminal), redacted in prod like any other event. The full per-dispatch
|
|
5807
|
+
* summary stream (successful OKs too) stays opt-in behind
|
|
5808
|
+
* `LUNORA_REQUEST_LOG_EMIT` so a hot shard doesn't emit a line per call.
|
|
5809
|
+
*/
|
|
5810
|
+
private persistRequestLog;
|
|
5811
|
+
/**
|
|
5780
5812
|
* Resolve the request-log knobs from the Worker `env`, all PLAN3 §3.3 decisions.
|
|
5781
5813
|
*
|
|
5782
5814
|
* `captureRaw`: raw (un-redacted) args/identity in a dev environment, redacted
|
|
@@ -5910,6 +5942,18 @@ declare abstract class ShardDO {
|
|
|
5910
5942
|
*/
|
|
5911
5943
|
private readAdminRequestLog;
|
|
5912
5944
|
/**
|
|
5945
|
+
* Resolve a `getIssues` admin read: fold the recent `error`-outcome
|
|
5946
|
+
* request-log rows into grouped {@link readErrorIssues Issues} by fingerprint,
|
|
5947
|
+
* accepting the same optional correlation filters as `getRequestLog`
|
|
5948
|
+
* (function-path prefix, exact shardKey/userId) plus a `limit` on rows
|
|
5949
|
+
* scanned. This is a read over the bounded reqlog readout — no new store —
|
|
5950
|
+
* so a self-hosted worker gets grouped error triage for free. Carries the
|
|
5951
|
+
* {@link ADMIN_WILDCARD} like the other log reads so a live Issues
|
|
5952
|
+
* subscription re-runs on every write-flush (the per-socket JSON memo still
|
|
5953
|
+
* suppresses byte-identical pushes).
|
|
5954
|
+
*/
|
|
5955
|
+
private readAdminIssues;
|
|
5956
|
+
/**
|
|
5913
5957
|
* Resolve a `getAuthMetrics` admin read: the durable app-level auth
|
|
5914
5958
|
* attempt/failure counters + minute-bucketed history the studio SLO panel
|
|
5915
5959
|
* charts (PLAN3 §2.3). Auth runs as a top-level `/api/auth/*` worker route,
|
package/dist/index.d.ts
CHANGED
|
@@ -2531,6 +2531,7 @@ declare const ADMIN_FUNCTIONS: {
|
|
|
2531
2531
|
readonly getCapturedMail: "__lunora_admin__:getCapturedMail";
|
|
2532
2532
|
readonly getFanoutMetrics: "__lunora_admin__:getFanoutMetrics";
|
|
2533
2533
|
readonly getFunctionStats: "__lunora_admin__:getFunctionStats";
|
|
2534
|
+
readonly getIssues: "__lunora_admin__:getIssues";
|
|
2534
2535
|
readonly listSubscriptions: "__lunora_admin__:listSubscriptions";
|
|
2535
2536
|
readonly listTableIndexes: "__lunora_admin__:listTableIndexes";
|
|
2536
2537
|
readonly getLogs: "__lunora_admin__:getLogs";
|
|
@@ -3346,10 +3347,14 @@ type LogLevel = "debug" | "error" | "info" | "warn";
|
|
|
3346
3347
|
/**
|
|
3347
3348
|
* One buffered log line. `functionPath` is the RPC that produced it (when the
|
|
3348
3349
|
* entry came from the RPC dispatch site); `timestamp` is `Date.now()` at the
|
|
3349
|
-
* moment it was pushed.
|
|
3350
|
+
* moment it was pushed. `instance`/`exitCode` are populated for container
|
|
3351
|
+
* lifecycle entries: `instance` correlates the per-instance Durable Object id,
|
|
3352
|
+
* `exitCode` carries the process exit code parsed out of a `stop` event.
|
|
3350
3353
|
*/
|
|
3351
3354
|
interface LogEntry {
|
|
3355
|
+
exitCode?: number;
|
|
3352
3356
|
functionPath?: string;
|
|
3357
|
+
instance?: string;
|
|
3353
3358
|
level: LogLevel;
|
|
3354
3359
|
message: string;
|
|
3355
3360
|
timestamp: number;
|
|
@@ -5589,6 +5594,14 @@ declare abstract class ShardDO {
|
|
|
5589
5594
|
* the panel renders it alongside `ctx.log` lines. Admin-gated by
|
|
5590
5595
|
* `handleAdminRpc`'s caller (the same `LUNORA_ADMIN_TOKEN` bearer as every
|
|
5591
5596
|
* other admin write).
|
|
5597
|
+
*
|
|
5598
|
+
* An `error`-level lifecycle event (a crash/`onError`, or a non-zero-exit
|
|
5599
|
+
* `stop`) is ALSO appended as an `error`-outcome row to the durable
|
|
5600
|
+
* `__lunora_reqlog__` — the same readout `getIssues` groups over — so a
|
|
5601
|
+
* crash-looping container folds into the Issues list right beside Worker
|
|
5602
|
+
* errors (they share the `fingerprintError` hash over `functionPath ::
|
|
5603
|
+
* bucket(message)`). The in-memory buffer stays the live Logs feed; the
|
|
5604
|
+
* durable row is what survives hibernation for triage.
|
|
5592
5605
|
*/
|
|
5593
5606
|
private handleRecordContainerEvent;
|
|
5594
5607
|
/**
|
|
@@ -5777,6 +5790,25 @@ declare abstract class ShardDO {
|
|
|
5777
5790
|
*/
|
|
5778
5791
|
private recordRequestLog;
|
|
5779
5792
|
/**
|
|
5793
|
+
* The single durable-row + Logpush write seam for `__lunora_reqlog__`. Both
|
|
5794
|
+
* the per-dispatch {@link recordRequestLog} and the container-crash path
|
|
5795
|
+
* ({@link handleRecordContainerEvent}) funnel through here so they can't
|
|
5796
|
+
* drift — before this was extracted the container writer persisted the row
|
|
5797
|
+
* but silently skipped the Logpush emit every other error got.
|
|
5798
|
+
*
|
|
5799
|
+
* Best-effort by contract: a SQL failure (e.g. a test double with no `sql`
|
|
5800
|
+
* handle) or a serialization hiccup in the emit must NEVER turn a served
|
|
5801
|
+
* request — or a container event push — into a failed one, so each half is
|
|
5802
|
+
* swallowed independently.
|
|
5803
|
+
*
|
|
5804
|
+
* Errors are always streamed to `console` (rare, high-value — they ride CF
|
|
5805
|
+
* Workers Logs at error level and the dev-server formats them in the
|
|
5806
|
+
* terminal), redacted in prod like any other event. The full per-dispatch
|
|
5807
|
+
* summary stream (successful OKs too) stays opt-in behind
|
|
5808
|
+
* `LUNORA_REQUEST_LOG_EMIT` so a hot shard doesn't emit a line per call.
|
|
5809
|
+
*/
|
|
5810
|
+
private persistRequestLog;
|
|
5811
|
+
/**
|
|
5780
5812
|
* Resolve the request-log knobs from the Worker `env`, all PLAN3 §3.3 decisions.
|
|
5781
5813
|
*
|
|
5782
5814
|
* `captureRaw`: raw (un-redacted) args/identity in a dev environment, redacted
|
|
@@ -5910,6 +5942,18 @@ declare abstract class ShardDO {
|
|
|
5910
5942
|
*/
|
|
5911
5943
|
private readAdminRequestLog;
|
|
5912
5944
|
/**
|
|
5945
|
+
* Resolve a `getIssues` admin read: fold the recent `error`-outcome
|
|
5946
|
+
* request-log rows into grouped {@link readErrorIssues Issues} by fingerprint,
|
|
5947
|
+
* accepting the same optional correlation filters as `getRequestLog`
|
|
5948
|
+
* (function-path prefix, exact shardKey/userId) plus a `limit` on rows
|
|
5949
|
+
* scanned. This is a read over the bounded reqlog readout — no new store —
|
|
5950
|
+
* so a self-hosted worker gets grouped error triage for free. Carries the
|
|
5951
|
+
* {@link ADMIN_WILDCARD} like the other log reads so a live Issues
|
|
5952
|
+
* subscription re-runs on every write-flush (the per-socket JSON memo still
|
|
5953
|
+
* suppresses byte-identical pushes).
|
|
5954
|
+
*/
|
|
5955
|
+
private readAdminIssues;
|
|
5956
|
+
/**
|
|
5913
5957
|
* Resolve a `getAuthMetrics` admin read: the durable app-level auth
|
|
5914
5958
|
* attempt/failure counters + minute-bucketed history the studio SLO panel
|
|
5915
5959
|
* charts (PLAN3 §2.3). Auth runs as a top-level `/api/auth/*` worker route,
|
package/dist/index.mjs
CHANGED
|
@@ -11,7 +11,7 @@ export { diffExternalSource } from './packem_shared/diffExternalSource-Cx9HUPJj.
|
|
|
11
11
|
export { materializeExternalRows, readExternalSourceBaseline, runExternalSourceTick } from './packem_shared/materializeExternalRows-CTqZisSC.mjs';
|
|
12
12
|
export { isSourceDue, liftSourceId, pullExternalSourceTick } from './packem_shared/isSourceDue-CYkt7Ru8.mjs';
|
|
13
13
|
export { FUNCTION_METRICS_BUCKETS_TABLE, FUNCTION_METRICS_BUCKET_MS, FUNCTION_METRICS_BUCKET_RETENTION, FUNCTION_METRICS_INDEX_TABLE, FUNCTION_METRICS_TABLE, ensureFunctionMetricsTables, readFunctionMetricBuckets, readFunctionMetricIndexHits, readFunctionMetrics, readFunctionMetricsTotals, recordFunctionMetric } from './packem_shared/FUNCTION_METRICS_BUCKETS_TABLE-UDNVD7FS.mjs';
|
|
14
|
-
export { ADMIN_FUNCTIONS, ADMIN_FUNCTION_PREFIX, FLAGS_FUNCTION_PREFIX, RELATION_FUNCTION_PREFIX, facetColumn, listTables, readTablePage, selectMatchingIds } from './packem_shared/ADMIN_FUNCTIONS-
|
|
14
|
+
export { ADMIN_FUNCTIONS, ADMIN_FUNCTION_PREFIX, FLAGS_FUNCTION_PREFIX, RELATION_FUNCTION_PREFIX, facetColumn, listTables, readTablePage, selectMatchingIds } from './packem_shared/ADMIN_FUNCTIONS-CAHLZMj8.mjs';
|
|
15
15
|
export { LogBuffer } from './packem_shared/LogBuffer-B_Ezju_N.mjs';
|
|
16
16
|
export { MAIL_RETENTION, MAIL_TABLE, clearCapturedMail, ensureMailTable, readCapturedMail, recordCapturedMail } from './packem_shared/MAIL_RETENTION-CPpgl-dX.mjs';
|
|
17
17
|
export { default as NotFoundError } from './packem_shared/NotFoundError-C70b9hLw.mjs';
|
|
@@ -19,14 +19,14 @@ export { armRestore, readBookmark } from './packem_shared/armRestore-4Px61hHS.mj
|
|
|
19
19
|
export { applySelect, buildSeekWhere, decodeCursor, encodeCursor, normalizeOrderKeys, softDeleteScope } from './packem_shared/applySelect-WQY8m62C.mjs';
|
|
20
20
|
export { RANK_TIEBREAK, encodePartitionKey, matchesRankStaticWhere, rankTableName, resolveRankPartition, sortColumnName } from './packem_shared/RANK_TIEBREAK-CXhdcA1o.mjs';
|
|
21
21
|
export { ReactiveCache, reactiveCacheKey } from './packem_shared/ReactiveCache-BYlSGY0N.mjs';
|
|
22
|
-
export { serveRelationFanout } from './packem_shared/serveRelationFanout-
|
|
22
|
+
export { serveRelationFanout } from './packem_shared/serveRelationFanout-BgaNg3Hu.mjs';
|
|
23
23
|
export { DEFAULT_MAX_RELATION_KEYS, assertFlatPredicate, assertShapeShardable, containsRelationPredicate, isRelationPredicate, resolveRelationPredicates } from './packem_shared/DEFAULT_MAX_RELATION_KEYS-BEan1CRD.mjs';
|
|
24
24
|
export { applyOnDelete, fanOutScalarCounts, resolveWith, runRowValidators } from './packem_shared/applyOnDelete-BXSq3S70.mjs';
|
|
25
25
|
export { RLS_UNWRAP_SYMBOL, RlsRequiredError, guardWriter } from './packem_shared/RLS_UNWRAP_SYMBOL-DTvHvRzY.mjs';
|
|
26
26
|
export { buildFtsMatch, ftsTableName, scoreDocument, stringifySearchText, tokenizeSearch } from './packem_shared/buildFtsMatch-BLEMawrp.mjs';
|
|
27
27
|
export { M as MIN_ADMIN_TOKEN_LENGTH, a as MIN_AUTH_SECRET_LENGTH, b as buildSecurityAudit } from './packem_shared/security-audit-CucgBice.mjs';
|
|
28
28
|
export { SESSION_DO_TTL_DEFAULT, SessionDO } from './packem_shared/SESSION_DO_TTL_DEFAULT-BnSKgVO4.mjs';
|
|
29
|
-
export { ROOT_DO_SIZE_WARN_BYTES, ROOT_SHARD_NAME, ShardDO } from './packem_shared/ROOT_DO_SIZE_WARN_BYTES-
|
|
29
|
+
export { ROOT_DO_SIZE_WARN_BYTES, ROOT_SHARD_NAME, ShardDO } from './packem_shared/ROOT_DO_SIZE_WARN_BYTES-BA8QOChj.mjs';
|
|
30
30
|
export { SHARD_REGISTRY_DO_NAME, ShardRegistryDO } from './packem_shared/SHARD_REGISTRY_DO_NAME-D99roc-r.mjs';
|
|
31
31
|
export { MAX_SQL_ROWS, assertReadonly, runReadonlySql } from './packem_shared/MAX_SQL_ROWS-D57CJaT9.mjs';
|
|
32
32
|
export { createSystemReader } from './packem_shared/createSystemReader-D12eNH13.mjs';
|
|
@@ -23,6 +23,7 @@ const ADMIN_FUNCTIONS = {
|
|
|
23
23
|
getCapturedMail: "__lunora_admin__:getCapturedMail",
|
|
24
24
|
getFanoutMetrics: "__lunora_admin__:getFanoutMetrics",
|
|
25
25
|
getFunctionStats: "__lunora_admin__:getFunctionStats",
|
|
26
|
+
getIssues: "__lunora_admin__:getIssues",
|
|
26
27
|
listSubscriptions: "__lunora_admin__:listSubscriptions",
|
|
27
28
|
listTableIndexes: "__lunora_admin__:listTableIndexes",
|
|
28
29
|
getLogs: "__lunora_admin__:getLogs",
|
|
@@ -8,12 +8,13 @@ import { recordAuthEvent, readAuthMetrics } from './AUTH_METRICS_BUCKETS_TABLE-C
|
|
|
8
8
|
import { DATA_MIGRATION_STATE_TABLE, readMigrationStatus } from './DATA_MIGRATION_STATE_TABLE-CYwBpyTr.mjs';
|
|
9
9
|
import { SCAN_DEP, createDependencyTracker, tableFromDepKey } from './SCAN_DEP-DLJF8dsj.mjs';
|
|
10
10
|
import { readFunctionMetricsTotals, readFunctionMetricIndexHits, recordFunctionMetric, mergeScanAttribution, readFunctionMetrics, readFunctionMetricBuckets } from './FUNCTION_METRICS_BUCKETS_TABLE-UDNVD7FS.mjs';
|
|
11
|
-
import { createFanoutCounters, ADMIN_FUNCTION_PREFIX, RELATION_FUNCTION_PREFIX, selectMatchingIds, ADMIN_FUNCTIONS, findStorageReferences, listTables, summarizeSubscriptions, summarizeFanoutTopics, readTablePage, facetColumn, FLAGS_FUNCTION_PREFIX, recordFanoutPass, MAX_PAGE_SIZE } from './ADMIN_FUNCTIONS-
|
|
11
|
+
import { createFanoutCounters, ADMIN_FUNCTION_PREFIX, RELATION_FUNCTION_PREFIX, selectMatchingIds, ADMIN_FUNCTIONS, findStorageReferences, listTables, summarizeSubscriptions, summarizeFanoutTopics, readTablePage, facetColumn, FLAGS_FUNCTION_PREFIX, recordFanoutPass, MAX_PAGE_SIZE } from './ADMIN_FUNCTIONS-CAHLZMj8.mjs';
|
|
12
12
|
import { LogBuffer } from './LogBuffer-B_Ezju_N.mjs';
|
|
13
13
|
import { recordCapturedMail, clearCapturedMail, readCapturedMail, MAIL_TABLE } from './MAIL_RETENTION-CPpgl-dX.mjs';
|
|
14
14
|
import { readBookmark, armRestore } from './armRestore-4Px61hHS.mjs';
|
|
15
15
|
import { ReactiveCache, reactiveCacheKey } from './ReactiveCache-BYlSGY0N.mjs';
|
|
16
16
|
import { stableStringify } from './stableStringify-MydiuScU.mjs';
|
|
17
|
+
import { fingerprintError } from '@lunora/fingerprint';
|
|
17
18
|
import { redact, standardRules } from '@visulima/redact';
|
|
18
19
|
import { i as isDevEnvironment, c as buildSettings, b as buildSecurityAudit } from './security-audit-CucgBice.mjs';
|
|
19
20
|
import { runReadonlySql } from './MAX_SQL_ROWS-D57CJaT9.mjs';
|
|
@@ -1291,6 +1292,52 @@ const readRequestLog = (sql, options = {}) => {
|
|
|
1291
1292
|
return base;
|
|
1292
1293
|
});
|
|
1293
1294
|
};
|
|
1295
|
+
const readErrorIssues = (sql, options = {}) => {
|
|
1296
|
+
ensureRequestLogTable(sql);
|
|
1297
|
+
const limit = Math.max(1, Math.min(options.limit ?? REQUEST_LOG_RETENTION, 1e4));
|
|
1298
|
+
const conjuncts = ["outcome = 'error'"];
|
|
1299
|
+
const parameters = [];
|
|
1300
|
+
if (options.functionPathPrefix !== void 0 && options.functionPathPrefix !== "") {
|
|
1301
|
+
conjuncts.push(String.raw`function_path LIKE ? ESCAPE '\'`);
|
|
1302
|
+
parameters.push(`${escapeLike(options.functionPathPrefix)}%`);
|
|
1303
|
+
}
|
|
1304
|
+
if (options.userId !== void 0 && options.userId !== "") {
|
|
1305
|
+
conjuncts.push("user_id = ?");
|
|
1306
|
+
parameters.push(options.userId);
|
|
1307
|
+
}
|
|
1308
|
+
if (options.shardKey !== void 0 && options.shardKey !== "") {
|
|
1309
|
+
conjuncts.push("shard_key = ?");
|
|
1310
|
+
parameters.push(options.shardKey);
|
|
1311
|
+
}
|
|
1312
|
+
parameters.push(limit);
|
|
1313
|
+
const rows = runSql(
|
|
1314
|
+
sql,
|
|
1315
|
+
`SELECT function_path, error_message, ts
|
|
1316
|
+
FROM "${REQUEST_LOG_TABLE}" WHERE ${conjuncts.join(" AND ")} ORDER BY seq DESC LIMIT ?`,
|
|
1317
|
+
...parameters
|
|
1318
|
+
).toArray();
|
|
1319
|
+
const issues = /* @__PURE__ */ new Map();
|
|
1320
|
+
const sampleTs = /* @__PURE__ */ new Map();
|
|
1321
|
+
for (const row of rows) {
|
|
1322
|
+
const message = row.error_message ?? "";
|
|
1323
|
+
const { culprit, hash, title } = fingerprintError({ functionPath: row.function_path, message });
|
|
1324
|
+
const existing = issues.get(hash);
|
|
1325
|
+
if (existing === void 0) {
|
|
1326
|
+
issues.set(hash, { count: 1, culprit, firstSeen: row.ts, hash, lastSeen: row.ts, sampleMessage: message, title });
|
|
1327
|
+
sampleTs.set(hash, row.ts);
|
|
1328
|
+
continue;
|
|
1329
|
+
}
|
|
1330
|
+
existing.count += 1;
|
|
1331
|
+
existing.firstSeen = Math.min(existing.firstSeen, row.ts);
|
|
1332
|
+
existing.lastSeen = Math.max(existing.lastSeen, row.ts);
|
|
1333
|
+
if (row.ts > (sampleTs.get(hash) ?? Number.NEGATIVE_INFINITY)) {
|
|
1334
|
+
sampleTs.set(hash, row.ts);
|
|
1335
|
+
existing.sampleMessage = message;
|
|
1336
|
+
existing.title = title;
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
return [...issues.values()].toSorted((a, b) => b.lastSeen - a.lastSeen);
|
|
1340
|
+
};
|
|
1294
1341
|
|
|
1295
1342
|
const runSocketPool = async (items, processOne, concurrency = 8) => {
|
|
1296
1343
|
let cursor = 0;
|
|
@@ -1516,6 +1563,7 @@ const parseRecordAuthEventArgs = (args) => {
|
|
|
1516
1563
|
}
|
|
1517
1564
|
return { outcome };
|
|
1518
1565
|
};
|
|
1566
|
+
const CONTAINER_EXIT_CODE_PATTERN = /\(exit (\d+)\)/;
|
|
1519
1567
|
const parseRecordContainerEventArgs = (args) => {
|
|
1520
1568
|
const raw = args["event"];
|
|
1521
1569
|
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
@@ -1530,8 +1578,13 @@ const parseRecordContainerEventArgs = (args) => {
|
|
|
1530
1578
|
const level = envelope["level"] === "error" ? "error" : "info";
|
|
1531
1579
|
const detail = typeof envelope["message"] === "string" ? envelope["message"] : void 0;
|
|
1532
1580
|
const timestamp = typeof envelope["ts"] === "number" ? envelope["ts"] : Date.now();
|
|
1581
|
+
const instance = typeof envelope["instance"] === "string" && envelope["instance"] !== "" ? envelope["instance"] : void 0;
|
|
1582
|
+
const exitRaw = detail === void 0 ? void 0 : CONTAINER_EXIT_CODE_PATTERN.exec(detail)?.[1];
|
|
1583
|
+
const exitCode = exitRaw === void 0 ? void 0 : Number.parseInt(exitRaw, 10);
|
|
1533
1584
|
return {
|
|
1585
|
+
exitCode,
|
|
1534
1586
|
functionPath: `container:${container}`,
|
|
1587
|
+
instance,
|
|
1535
1588
|
level,
|
|
1536
1589
|
message: detail === void 0 || detail === "" ? event : `${event}: ${detail}`,
|
|
1537
1590
|
timestamp
|
|
@@ -2391,6 +2444,8 @@ class ShardDO {
|
|
|
2391
2444
|
message,
|
|
2392
2445
|
timestamp: Date.now()
|
|
2393
2446
|
});
|
|
2447
|
+
this.recordChangedTable(REQUEST_LOG_TABLE);
|
|
2448
|
+
await this.flushChangedTables();
|
|
2394
2449
|
return this.errorToResponse(error);
|
|
2395
2450
|
} finally {
|
|
2396
2451
|
this.currentRequestBookmark = void 0;
|
|
@@ -4394,10 +4449,32 @@ class ShardDO {
|
|
|
4394
4449
|
* the panel renders it alongside `ctx.log` lines. Admin-gated by
|
|
4395
4450
|
* `handleAdminRpc`'s caller (the same `LUNORA_ADMIN_TOKEN` bearer as every
|
|
4396
4451
|
* other admin write).
|
|
4397
|
-
|
|
4398
|
-
|
|
4452
|
+
*
|
|
4453
|
+
* An `error`-level lifecycle event (a crash/`onError`, or a non-zero-exit
|
|
4454
|
+
* `stop`) is ALSO appended as an `error`-outcome row to the durable
|
|
4455
|
+
* `__lunora_reqlog__` — the same readout `getIssues` groups over — so a
|
|
4456
|
+
* crash-looping container folds into the Issues list right beside Worker
|
|
4457
|
+
* errors (they share the `fingerprintError` hash over `functionPath ::
|
|
4458
|
+
* bucket(message)`). The in-memory buffer stays the live Logs feed; the
|
|
4459
|
+
* durable row is what survives hibernation for triage.
|
|
4460
|
+
*/
|
|
4461
|
+
async handleRecordContainerEvent(args) {
|
|
4399
4462
|
const entry = parseRecordContainerEventArgs(args);
|
|
4400
4463
|
this.logs.push(entry);
|
|
4464
|
+
const crashed = entry.level === "error" || entry.exitCode !== void 0 && entry.exitCode !== 0;
|
|
4465
|
+
if (crashed) {
|
|
4466
|
+
const logEntry = {
|
|
4467
|
+
durationMs: 0,
|
|
4468
|
+
errorMessage: entry.message,
|
|
4469
|
+
functionPath: entry.functionPath,
|
|
4470
|
+
outcome: "error",
|
|
4471
|
+
shardKey: this.state.id?.name,
|
|
4472
|
+
ts: entry.timestamp
|
|
4473
|
+
};
|
|
4474
|
+
this.persistRequestLog(logEntry, this.requestLogConfig());
|
|
4475
|
+
this.recordChangedTable(REQUEST_LOG_TABLE);
|
|
4476
|
+
await this.flushChangedTables();
|
|
4477
|
+
}
|
|
4401
4478
|
return jsonResponse({ result: { recorded: true } }, 200);
|
|
4402
4479
|
}
|
|
4403
4480
|
/**
|
|
@@ -4738,12 +4815,33 @@ class ShardDO {
|
|
|
4738
4815
|
ts: Date.now(),
|
|
4739
4816
|
userId: this.getCurrentUserId()
|
|
4740
4817
|
};
|
|
4818
|
+
this.persistRequestLog(entry, config);
|
|
4819
|
+
}
|
|
4820
|
+
/**
|
|
4821
|
+
* The single durable-row + Logpush write seam for `__lunora_reqlog__`. Both
|
|
4822
|
+
* the per-dispatch {@link recordRequestLog} and the container-crash path
|
|
4823
|
+
* ({@link handleRecordContainerEvent}) funnel through here so they can't
|
|
4824
|
+
* drift — before this was extracted the container writer persisted the row
|
|
4825
|
+
* but silently skipped the Logpush emit every other error got.
|
|
4826
|
+
*
|
|
4827
|
+
* Best-effort by contract: a SQL failure (e.g. a test double with no `sql`
|
|
4828
|
+
* handle) or a serialization hiccup in the emit must NEVER turn a served
|
|
4829
|
+
* request — or a container event push — into a failed one, so each half is
|
|
4830
|
+
* swallowed independently.
|
|
4831
|
+
*
|
|
4832
|
+
* Errors are always streamed to `console` (rare, high-value — they ride CF
|
|
4833
|
+
* Workers Logs at error level and the dev-server formats them in the
|
|
4834
|
+
* terminal), redacted in prod like any other event. The full per-dispatch
|
|
4835
|
+
* summary stream (successful OKs too) stays opt-in behind
|
|
4836
|
+
* `LUNORA_REQUEST_LOG_EMIT` so a hot shard doesn't emit a line per call.
|
|
4837
|
+
*/
|
|
4838
|
+
persistRequestLog(entry, config) {
|
|
4741
4839
|
const writeOptions = { captureRaw: config.captureRaw, retention: config.retention };
|
|
4742
4840
|
try {
|
|
4743
4841
|
appendRequestLogEntry(this.state.storage.sql, entry, writeOptions);
|
|
4744
4842
|
} catch {
|
|
4745
4843
|
}
|
|
4746
|
-
if (config.emit || outcome === "error") {
|
|
4844
|
+
if (config.emit || entry.outcome === "error") {
|
|
4747
4845
|
try {
|
|
4748
4846
|
emitRequestLogEvent(entry, writeOptions);
|
|
4749
4847
|
} catch {
|
|
@@ -4836,6 +4934,9 @@ class ShardDO {
|
|
|
4836
4934
|
if (functionPath === ADMIN_FUNCTIONS.getRequestLog) {
|
|
4837
4935
|
return this.readAdminRequestLog(sql, args);
|
|
4838
4936
|
}
|
|
4937
|
+
if (functionPath === ADMIN_FUNCTIONS.getIssues) {
|
|
4938
|
+
return this.readAdminIssues(sql, args);
|
|
4939
|
+
}
|
|
4839
4940
|
const durable = this.readAdminDurableSignal(functionPath, sql, args);
|
|
4840
4941
|
if (durable) {
|
|
4841
4942
|
return durable;
|
|
@@ -5066,6 +5167,30 @@ class ShardDO {
|
|
|
5066
5167
|
};
|
|
5067
5168
|
return { result, tables: /* @__PURE__ */ new Set([ADMIN_WILDCARD]) };
|
|
5068
5169
|
}
|
|
5170
|
+
/**
|
|
5171
|
+
* Resolve a `getIssues` admin read: fold the recent `error`-outcome
|
|
5172
|
+
* request-log rows into grouped {@link readErrorIssues Issues} by fingerprint,
|
|
5173
|
+
* accepting the same optional correlation filters as `getRequestLog`
|
|
5174
|
+
* (function-path prefix, exact shardKey/userId) plus a `limit` on rows
|
|
5175
|
+
* scanned. This is a read over the bounded reqlog readout — no new store —
|
|
5176
|
+
* so a self-hosted worker gets grouped error triage for free. Carries the
|
|
5177
|
+
* {@link ADMIN_WILDCARD} like the other log reads so a live Issues
|
|
5178
|
+
* subscription re-runs on every write-flush (the per-socket JSON memo still
|
|
5179
|
+
* suppresses byte-identical pushes).
|
|
5180
|
+
*/
|
|
5181
|
+
// eslint-disable-next-line class-methods-use-this -- kept an instance method for symmetry with the other `readAdmin*` resolvers
|
|
5182
|
+
readAdminIssues(sql, args) {
|
|
5183
|
+
ensureRequestLogTable(sql);
|
|
5184
|
+
const result = {
|
|
5185
|
+
issues: readErrorIssues(sql, {
|
|
5186
|
+
functionPathPrefix: typeof args["functionPathPrefix"] === "string" ? args["functionPathPrefix"] : void 0,
|
|
5187
|
+
limit: typeof args["limit"] === "number" ? args["limit"] : void 0,
|
|
5188
|
+
shardKey: typeof args["shardKey"] === "string" ? args["shardKey"] : void 0,
|
|
5189
|
+
userId: typeof args["userId"] === "string" ? args["userId"] : void 0
|
|
5190
|
+
})
|
|
5191
|
+
};
|
|
5192
|
+
return { result, tables: /* @__PURE__ */ new Set([ADMIN_WILDCARD]) };
|
|
5193
|
+
}
|
|
5069
5194
|
/**
|
|
5070
5195
|
* Resolve a `getAuthMetrics` admin read: the durable app-level auth
|
|
5071
5196
|
* attempt/failure counters + minute-bucketed history the studio SLO panel
|
package/dist/packem_shared/{serveRelationFanout-C56hKu1O.mjs → serveRelationFanout-BgaNg3Hu.mjs}
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { LunoraError } from '@lunora/errors';
|
|
2
|
-
import { RELATION_FUNCTION_PREFIX } from './ADMIN_FUNCTIONS-
|
|
2
|
+
import { RELATION_FUNCTION_PREFIX } from './ADMIN_FUNCTIONS-CAHLZMj8.mjs';
|
|
3
3
|
|
|
4
4
|
const serveRelationFanout = async (schema, database, functionPath, args) => {
|
|
5
5
|
const table = typeof args["table"] === "string" ? args["table"] : "";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lunora/do",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.30",
|
|
4
4
|
"description": "Lunora Durable Objects: ShardDO (SQLite, OCC, hibernated WebSocket subscriptions) and SessionDO",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cloudflare",
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"@lunora/errors": "1.0.0-alpha.4",
|
|
50
|
+
"@lunora/fingerprint": "1.0.0-alpha.1",
|
|
50
51
|
"@visulima/redact": "3.0.0",
|
|
51
52
|
"drizzle-orm": "^0.45.2"
|
|
52
53
|
},
|