ugly-app 0.1.821 → 0.1.823

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.
Files changed (67) hide show
  1. package/dist/cli/version.d.ts +1 -1
  2. package/dist/cli/version.js +1 -1
  3. package/dist/inspect/agent/perfAgent.d.ts +8 -0
  4. package/dist/inspect/agent/perfAgent.d.ts.map +1 -1
  5. package/dist/inspect/agent/perfAgent.js +69 -6
  6. package/dist/inspect/agent/perfAgent.js.map +1 -1
  7. package/dist/inspect/agent-bundle.js +3 -3
  8. package/dist/inspect/host-bundle.js +8 -8
  9. package/dist/inspect/protocol.d.ts +3 -1
  10. package/dist/inspect/protocol.d.ts.map +1 -1
  11. package/dist/inspect/protocol.js.map +1 -1
  12. package/dist/server/Logging.d.ts +1 -1
  13. package/dist/server/Logging.d.ts.map +1 -1
  14. package/dist/server/sqlite/SqliteDoc.d.ts +44 -0
  15. package/dist/server/sqlite/SqliteDoc.d.ts.map +1 -0
  16. package/dist/server/sqlite/SqliteDoc.js +201 -0
  17. package/dist/server/sqlite/SqliteDoc.js.map +1 -0
  18. package/dist/server/sqlite/SqliteExec.d.ts +18 -0
  19. package/dist/server/sqlite/SqliteExec.d.ts.map +1 -0
  20. package/dist/server/sqlite/SqliteExec.js +19 -0
  21. package/dist/server/sqlite/SqliteExec.js.map +1 -0
  22. package/dist/server/sqlite/SqliteFilter.d.ts +21 -0
  23. package/dist/server/sqlite/SqliteFilter.d.ts.map +1 -0
  24. package/dist/server/sqlite/SqliteFilter.js +143 -0
  25. package/dist/server/sqlite/SqliteFilter.js.map +1 -0
  26. package/dist/server/sqlite/SqlitePipeline.d.ts +8 -0
  27. package/dist/server/sqlite/SqlitePipeline.d.ts.map +1 -0
  28. package/dist/server/sqlite/SqlitePipeline.js +154 -0
  29. package/dist/server/sqlite/SqlitePipeline.js.map +1 -0
  30. package/dist/server/sqlite/SqliteSchema.d.ts +15 -0
  31. package/dist/server/sqlite/SqliteSchema.d.ts.map +1 -0
  32. package/dist/server/sqlite/SqliteSchema.js +35 -0
  33. package/dist/server/sqlite/SqliteSchema.js.map +1 -0
  34. package/dist/server/sqlite/assertIndexed.d.ts +20 -0
  35. package/dist/server/sqlite/assertIndexed.d.ts.map +1 -0
  36. package/dist/server/sqlite/assertIndexed.js +43 -0
  37. package/dist/server/sqlite/assertIndexed.js.map +1 -0
  38. package/dist/server/sqlite/sqliteUpdateOps.d.ts +17 -0
  39. package/dist/server/sqlite/sqliteUpdateOps.d.ts.map +1 -0
  40. package/dist/server/sqlite/sqliteUpdateOps.js +100 -0
  41. package/dist/server/sqlite/sqliteUpdateOps.js.map +1 -0
  42. package/dist/shared/FrameworkRequests.d.ts +1 -1
  43. package/dist/shared/FrameworkRequests.d.ts.map +1 -1
  44. package/dist/shared/FrameworkRequests.js +1 -0
  45. package/dist/shared/FrameworkRequests.js.map +1 -1
  46. package/package.json +3 -1
  47. package/src/cli/version.ts +1 -1
  48. package/src/inspect/agent/perfAgent.test.ts +24 -0
  49. package/src/inspect/agent/perfAgent.ts +67 -6
  50. package/src/inspect/protocol.ts +3 -1
  51. package/src/server/App.ts +1 -1
  52. package/src/server/Logging.ts +1 -1
  53. package/src/server/adapter/workers/createWorkersApp.ts +1 -1
  54. package/src/server/sqlite/SqliteDoc.test.ts +145 -0
  55. package/src/server/sqlite/SqliteDoc.ts +356 -0
  56. package/src/server/sqlite/SqliteExec.ts +35 -0
  57. package/src/server/sqlite/SqliteFilter.test.ts +110 -0
  58. package/src/server/sqlite/SqliteFilter.ts +161 -0
  59. package/src/server/sqlite/SqlitePipeline.test.ts +54 -0
  60. package/src/server/sqlite/SqlitePipeline.ts +172 -0
  61. package/src/server/sqlite/SqliteSchema.test.ts +64 -0
  62. package/src/server/sqlite/SqliteSchema.ts +56 -0
  63. package/src/server/sqlite/assertIndexed.test.ts +58 -0
  64. package/src/server/sqlite/assertIndexed.ts +58 -0
  65. package/src/server/sqlite/sqliteUpdateOps.test.ts +74 -0
  66. package/src/server/sqlite/sqliteUpdateOps.ts +111 -0
  67. package/src/shared/FrameworkRequests.ts +1 -0
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Backend-agnostic index-coverage pre-flight. Throws when a query filters or
3
+ * sorts on a field with no declared index — surfacing missing indexes in
4
+ * dev/test instead of silently full-scanning (D1 has no GIN fallback; this also
5
+ * hardens Neon). Coverage mirrors PostgresIndexes: every field named in any
6
+ * declared index is covered (a composite credits all its fields). Enhancement
7
+ * over PG's warn-only checker: sort keys are checked too, and it THROWS.
8
+ */
9
+ import type { IndexDef } from '../../shared/DB.js';
10
+ import { collectJsonFields } from './SqliteFilter.js';
11
+
12
+ const TOP_LEVEL_COLUMNS = new Set(['_id', 'created', 'updated', 'version']);
13
+
14
+ export class UnindexedQueryError extends Error {
15
+ constructor(
16
+ public readonly collection: string,
17
+ public readonly field: string,
18
+ ) {
19
+ super(
20
+ `Collection "${collection}" filters/sorts on "${field}" which has no index. ` +
21
+ `Add to meta.indexes: { fields: { ${field}: 1 } } — or pass { allowScan: true } ` +
22
+ `for a deliberate full scan.`,
23
+ );
24
+ this.name = 'UnindexedQueryError';
25
+ }
26
+ }
27
+
28
+ /** Every field named in any declared index (composite indexes credit all fields). */
29
+ export function coveredFields(indexes?: IndexDef[]): Set<string> {
30
+ const covered = new Set<string>();
31
+ for (const idx of indexes ?? []) {
32
+ for (const field of Object.keys(idx.fields)) covered.add(field);
33
+ }
34
+ return covered;
35
+ }
36
+
37
+ export function assertIndexed(
38
+ collection: string,
39
+ indexes: IndexDef[] | undefined,
40
+ filter?: Record<string, unknown>,
41
+ sort?: Record<string, 1 | -1>,
42
+ opts?: { allowScan?: boolean },
43
+ ): void {
44
+ if (opts?.allowScan) return;
45
+ const covered = coveredFields(indexes);
46
+
47
+ const fields = new Set<string>();
48
+ if (filter) for (const f of collectJsonFields(filter)) fields.add(f);
49
+ if (sort) {
50
+ for (const f of Object.keys(sort)) {
51
+ if (!TOP_LEVEL_COLUMNS.has(f)) fields.add(f);
52
+ }
53
+ }
54
+
55
+ for (const field of fields) {
56
+ if (!covered.has(field)) throw new UnindexedQueryError(collection, field);
57
+ }
58
+ }
@@ -0,0 +1,74 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { applyOpInMemory } from './sqliteUpdateOps.js';
3
+
4
+ describe('applyOpInMemory', () => {
5
+ it('$set sets a top-level field', () => {
6
+ expect(applyOpInMemory({ a: 1 }, { $set: { b: 2 } })).toEqual({ a: 1, b: 2 });
7
+ });
8
+
9
+ it('$set creates missing parent objects for a dot-path', () => {
10
+ expect(applyOpInMemory({}, { $set: { 'x.y.z': 5 } })).toEqual({
11
+ x: { y: { z: 5 } },
12
+ });
13
+ });
14
+
15
+ it('$unset removes a top-level key', () => {
16
+ expect(applyOpInMemory({ a: 1, b: 2 }, { $unset: { b: '' } })).toEqual({ a: 1 });
17
+ });
18
+
19
+ it('$unset removes a nested key, leaving the parent', () => {
20
+ expect(
21
+ applyOpInMemory({ x: { y: 1, z: 2 } }, { $unset: { 'x.z': '' } }),
22
+ ).toEqual({ x: { y: 1 } });
23
+ });
24
+
25
+ it('$inc adds to an existing number', () => {
26
+ expect(applyOpInMemory({ n: 3 }, { $inc: { n: 2 } })).toEqual({ n: 5 });
27
+ });
28
+
29
+ it('$inc treats a missing field as 0', () => {
30
+ expect(applyOpInMemory({}, { $inc: { n: 4 } })).toEqual({ n: 4 });
31
+ });
32
+
33
+ it('$addToSet appends a new scalar', () => {
34
+ expect(applyOpInMemory({ tags: ['a'] }, { $addToSet: { tags: 'b' } })).toEqual({
35
+ tags: ['a', 'b'],
36
+ });
37
+ });
38
+
39
+ it('$addToSet is a no-op when the value is already present', () => {
40
+ expect(applyOpInMemory({ tags: ['a'] }, { $addToSet: { tags: 'a' } })).toEqual({
41
+ tags: ['a'],
42
+ });
43
+ });
44
+
45
+ it('$addToSet creates the array when the field is missing', () => {
46
+ expect(applyOpInMemory({}, { $addToSet: { tags: 'a' } })).toEqual({ tags: ['a'] });
47
+ });
48
+
49
+ it('$pull removes all deep-equal elements', () => {
50
+ expect(
51
+ applyOpInMemory({ tags: ['a', 'b', 'a'] }, { $pull: { tags: 'a' } }),
52
+ ).toEqual({ tags: ['b'] });
53
+ });
54
+
55
+ it('does not mutate the input object', () => {
56
+ const input = { a: 1 };
57
+ applyOpInMemory(input, { $set: { a: 2 } });
58
+ expect(input).toEqual({ a: 1 });
59
+ });
60
+
61
+ it('applies operators in $set,$unset,$inc,$addToSet,$pull order', () => {
62
+ expect(
63
+ applyOpInMemory(
64
+ { keep: 1, drop: 1, n: 1, tags: ['x'] },
65
+ {
66
+ $set: { added: 9 },
67
+ $unset: { drop: '' },
68
+ $inc: { n: 1 },
69
+ $addToSet: { tags: 'y' },
70
+ },
71
+ ),
72
+ ).toEqual({ keep: 1, added: 9, n: 2, tags: ['x', 'y'] });
73
+ });
74
+ });
@@ -0,0 +1,111 @@
1
+ /**
2
+ * MongoDB-style update operators applied in-memory over a plain doc object.
3
+ * Mirrors PostgresOperators.buildUpdateExpression's SEMANTICS (not its SQL):
4
+ * the SQLite store does read-modify-write, so the resulting document must match
5
+ * what Postgres's jsonb_set UPDATE would produce. Pure — no DB, no native import.
6
+ */
7
+
8
+ export interface UpdateOp {
9
+ $set?: Record<string, unknown>;
10
+ $unset?: Record<string, unknown>;
11
+ $inc?: Record<string, number>;
12
+ $addToSet?: Record<string, unknown>;
13
+ $pull?: Record<string, unknown>;
14
+ }
15
+
16
+ /** Structural deep-equality, mirroring Postgres jsonb `=` (key order irrelevant). */
17
+ export function deepEqual(a: unknown, b: unknown): boolean {
18
+ if (a === b) return true;
19
+ if (typeof a !== typeof b) return false;
20
+ if (a === null || b === null) return a === b;
21
+ if (Array.isArray(a) || Array.isArray(b)) {
22
+ if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) return false;
23
+ return a.every((v, i) => deepEqual(v, b[i]));
24
+ }
25
+ if (typeof a === 'object') {
26
+ const ao = a as Record<string, unknown>;
27
+ const bo = b as Record<string, unknown>;
28
+ const ak = Object.keys(ao);
29
+ const bk = Object.keys(bo);
30
+ if (ak.length !== bk.length) return false;
31
+ return ak.every((k) => k in bo && deepEqual(ao[k], bo[k]));
32
+ }
33
+ return false;
34
+ }
35
+
36
+ function setPath(root: Record<string, unknown>, path: string, value: unknown): void {
37
+ const parts = path.split('.');
38
+ let node = root;
39
+ for (let i = 0; i < parts.length - 1; i++) {
40
+ const key = parts[i]!;
41
+ const next = node[key];
42
+ if (typeof next !== 'object' || next === null || Array.isArray(next)) {
43
+ node[key] = {};
44
+ }
45
+ node = node[key] as Record<string, unknown>;
46
+ }
47
+ node[parts[parts.length - 1]!] = value;
48
+ }
49
+
50
+ function unsetPath(root: Record<string, unknown>, path: string): void {
51
+ const parts = path.split('.');
52
+ let node = root;
53
+ for (let i = 0; i < parts.length - 1; i++) {
54
+ const next = node[parts[i]!];
55
+ if (typeof next !== 'object' || next === null) return;
56
+ node = next as Record<string, unknown>;
57
+ }
58
+ delete node[parts[parts.length - 1]!];
59
+ }
60
+
61
+ function getPath(root: Record<string, unknown>, path: string): unknown {
62
+ const parts = path.split('.');
63
+ let node: unknown = root;
64
+ for (const key of parts) {
65
+ if (typeof node !== 'object' || node === null) return undefined;
66
+ node = (node as Record<string, unknown>)[key];
67
+ }
68
+ return node;
69
+ }
70
+
71
+ export function applyOpInMemory(
72
+ data: Record<string, unknown>,
73
+ op: UpdateOp,
74
+ ): Record<string, unknown> {
75
+ const out = structuredClone(data);
76
+
77
+ if (op.$set) {
78
+ for (const [field, value] of Object.entries(op.$set)) setPath(out, field, value);
79
+ }
80
+ if (op.$unset) {
81
+ for (const field of Object.keys(op.$unset)) unsetPath(out, field);
82
+ }
83
+ if (op.$inc) {
84
+ for (const [field, amount] of Object.entries(op.$inc)) {
85
+ const cur = getPath(out, field);
86
+ const base = typeof cur === 'number' ? cur : 0;
87
+ setPath(out, field, base + amount);
88
+ }
89
+ }
90
+ if (op.$addToSet) {
91
+ for (const [field, value] of Object.entries(op.$addToSet)) {
92
+ const cur = getPath(out, field);
93
+ const arr = Array.isArray(cur) ? [...(cur as unknown[])] : [];
94
+ if (!arr.some((el) => deepEqual(el, value))) arr.push(value);
95
+ setPath(out, field, arr);
96
+ }
97
+ }
98
+ if (op.$pull) {
99
+ for (const [field, value] of Object.entries(op.$pull)) {
100
+ const cur = getPath(out, field);
101
+ const arr = Array.isArray(cur) ? (cur as unknown[]) : [];
102
+ setPath(
103
+ out,
104
+ field,
105
+ arr.filter((el) => !deepEqual(el, value)),
106
+ );
107
+ }
108
+ }
109
+
110
+ return out;
111
+ }
@@ -367,6 +367,7 @@ export const frameworkRequests = defineRequests({
367
367
  "dropped-frames",
368
368
  "long-task",
369
369
  "page-unload",
370
+ "page-load",
370
371
  "self-snapshot",
371
372
  "app-quit",
372
373
  ]),