@kaeawc/auto-mobile 0.0.40 → 0.0.42

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 (47) hide show
  1. package/README.md +3 -3
  2. package/README.md.backup +3 -3
  3. package/dist/schemas/test-plan.schema.json +6 -2
  4. package/dist/schemas/tool-definitions.json +2549 -546
  5. package/dist/src/db/eventTables.ts +8 -0
  6. package/dist/src/db/migrations/2025_12_28_000_initial_schema.ts +3 -3
  7. package/dist/src/db/migrations/2025_12_30_000_performance_thresholds.ts +3 -3
  8. package/dist/src/db/migrations/2025_12_30_001_navigation_graph.ts +9 -9
  9. package/dist/src/db/migrations/2025_12_31_000_accessibility_baselines.ts +3 -3
  10. package/dist/src/db/migrations/2025_12_31_001_memory_audit.ts +4 -4
  11. package/dist/src/db/migrations/2026_01_01_000_recomposition_metrics.ts +2 -2
  12. package/dist/src/db/migrations/2026_01_02_000_prediction_history.ts +4 -4
  13. package/dist/src/db/migrations/2026_01_03_000_feature_flags.ts +3 -3
  14. package/dist/src/db/migrations/2026_01_03_000_test_executions.ts +2 -2
  15. package/dist/src/db/migrations/2026_01_05_000_tool_calls.ts +2 -2
  16. package/dist/src/db/migrations/2026_01_08_000_test_coverage.ts +4 -4
  17. package/dist/src/db/migrations/2026_01_09_000_video_recordings.ts +3 -3
  18. package/dist/src/db/migrations/2026_01_10_000_device_snapshots.ts +3 -3
  19. package/dist/src/db/migrations/2026_01_14_000_appearance_config.ts +3 -3
  20. package/dist/src/db/migrations/2026_01_25_001_test_run_details.ts +2 -2
  21. package/dist/src/db/migrations/2026_01_27_000_crash_anr_monitoring.ts +2 -2
  22. package/dist/src/db/migrations/2026_01_27_000_failures.ts +7 -7
  23. package/dist/src/db/migrations/2026_01_29_000_named_nodes.ts +3 -3
  24. package/dist/src/db/migrations/2026_01_30_000_performance_live_metrics.ts +1 -1
  25. package/dist/src/db/migrations/2026_03_15_000_telemetry_events.ts +5 -5
  26. package/dist/src/db/migrations/2026_03_18_000_navigation_events.ts +2 -2
  27. package/dist/src/db/migrations/2026_03_19_000_storage_events.ts +2 -2
  28. package/dist/src/db/migrations/2026_03_19_001_layout_events.ts +2 -2
  29. package/dist/src/db/migrations/2026_04_01_000_drop_custom_events.ts +2 -2
  30. package/dist/src/db/migrations/2026_04_02_000_device_sessions.ts +3 -3
  31. package/dist/src/db/migrations/2026_07_01_000_failure_groups_signature_unique.ts +162 -0
  32. package/dist/src/db/migrations/2026_07_02_000_event_composite_indexes.ts +62 -0
  33. package/dist/src/db/migrations/2026_07_03_000_drop_redundant_device_indexes.ts +66 -0
  34. package/dist/src/db/migrations/2026_07_03_000_repair_datetime_now_defaults.ts +268 -0
  35. package/dist/src/db/migrations/2026_07_04_000_storage_events_key_lookup.ts +60 -0
  36. package/dist/src/db/migrations/2026_07_05_000_repair_updated_at_defaults.ts +242 -0
  37. package/dist/src/index.js +533 -390
  38. package/dist/src/index.js.map +1 -1
  39. package/dist/vendor/libwebp/COPYING +30 -0
  40. package/dist/vendor/libwebp/PATENTS +23 -0
  41. package/dist/vendor/libwebp/README.md +13 -0
  42. package/dist/vendor/libwebp/README.upstream.md +54 -0
  43. package/dist/vendor/libwebp/win32-x64/cwebp.exe +0 -0
  44. package/dist/vendor/libwebp/win32-x64/dwebp.exe +0 -0
  45. package/package.json +9 -4
  46. package/schemas/test-plan.schema.json +6 -2
  47. package/schemas/tool-definitions.json +2549 -546
@@ -0,0 +1,242 @@
1
+ import { type Kysely, sql } from "kysely";
2
+
3
+ /**
4
+ * #2937 — give eight tables' `updated_at` columns their `(datetime('now'))`
5
+ * default on already-upgraded databases.
6
+ *
7
+ * PR #2922 (issue #2913) reconciled the schema by adding
8
+ * `.defaultTo(sql`(datetime('now'))`)` to these eight tables' `updated_at`
9
+ * migration declarations, matching their `created_at` sibling. But editing a
10
+ * historical migration body does NOT re-run it — Kysely tracks executed
11
+ * migrations by name, with no content checksum — so the added default lands only
12
+ * on FRESH/replayed databases. A database that already ran the pre-#2922
13
+ * migrations keeps the old `updated_at TEXT NOT NULL` column with no default,
14
+ * and a defaulted insert (one that omits `updated_at`) fails its NOT NULL
15
+ * constraint there.
16
+ *
17
+ * This mirrors the `created_at` situation before #2915, minus the live bug:
18
+ * `created_at` had a broken string-literal default actively poisoning new rows,
19
+ * which the {@link file://./2026_07_03_000_repair_datetime_now_defaults.ts}
20
+ * rebuild repaired. `updated_at` has no such poison — every current writer
21
+ * supplies it explicitly — so this migration only needs to REBUILD each column's
22
+ * default; the existing rows already hold real, explicitly-written timestamps and
23
+ * are copied through untouched (no row repair).
24
+ *
25
+ * REBUILD MECHANICS — identical to the #2915 repair: SQLite cannot alter a column
26
+ * default in place, and bun:sqlite forbids editing `sqlite_master` directly, so
27
+ * each table is rebuilt via a twin: create a twin whose `updated_at` carries the
28
+ * corrected default, copy the rows with `INSERT ... SELECT *`, drop the original,
29
+ * rename the twin back, replay the table's explicit indexes/triggers, and restore
30
+ * its `sqlite_sequence` AUTOINCREMENT high-water mark (so a rebuilt table cannot
31
+ * reuse a deleted top id). FK enforcement is toggled OFF around the transaction so
32
+ * dropping a referenced parent (e.g. `navigation_apps`) cannot cascade-delete its
33
+ * children.
34
+ *
35
+ * TARGETING — the eight tables are enumerated in {@link TABLES_WITH_DEFAULTED_UPDATED_AT}
36
+ * (exactly the set #2922 edited), and each is rebuilt only when its live
37
+ * `updated_at` default is absent. Detection reads
38
+ * `pragma_table_info(...).dflt_value`: `NULL` means the no-default upgraded column
39
+ * (rebuild it); the corrected default reports as `datetime('now')` and is skipped.
40
+ * `device_sessions` / `failure_groups` are deliberately NOT listed — their
41
+ * `updated_at` default has existed on every database since #2915 repaired the
42
+ * broken literal they originally shipped, so they need no rebuild.
43
+ *
44
+ * Safe on a fresh DB and idempotent: a fresh/replayed or already-rebuilt schema
45
+ * carries the default, so nothing matches and the whole pass is a no-op that never
46
+ * touches FK state. A second run finds every column already defaulted.
47
+ */
48
+
49
+ /**
50
+ * The eight tables whose `updated_at` default was added by PR #2922 (a migration
51
+ * edit that does not replay), so an upgraded DB's copy still lacks it. Ordered
52
+ * arbitrarily; each is independently rebuilt.
53
+ */
54
+ const TABLES_WITH_DEFAULTED_UPDATED_AT = [
55
+ "device_configs",
56
+ "navigation_apps",
57
+ "prediction_transition_stats",
58
+ "accessibility_baselines",
59
+ "feature_flags",
60
+ "video_recording_configs",
61
+ "device_snapshot_configs",
62
+ "appearance_configs",
63
+ ] as const;
64
+
65
+ /** The corrected default expression `updated_at` should carry, as raw SQL. */
66
+ const CORRECTED_DEFAULT = "(datetime('now'))";
67
+
68
+ interface ColumnInfoRow {
69
+ name: string;
70
+ dflt_value: string | null;
71
+ }
72
+
73
+ interface SqlRow {
74
+ sql: string | null;
75
+ }
76
+
77
+ export async function up(db: Kysely<unknown>): Promise<void> {
78
+ // Fast path: only the tables whose `updated_at` column exists AND has no
79
+ // default need a rebuild. On a fresh/already-fixed schema this is empty, so we
80
+ // return WITHOUT touching PRAGMA foreign_keys — leaving the connection's FK
81
+ // state exactly as the caller set it, as the common case (every existing DB and
82
+ // test) requires. Only the rare upgraded-DB path below toggles it.
83
+ const tablesToRebuild: string[] = [];
84
+ for (const table of TABLES_WITH_DEFAULTED_UPDATED_AT) {
85
+ if (await updatedAtLacksDefault(db, table)) {
86
+ tablesToRebuild.push(table);
87
+ }
88
+ }
89
+ if (tablesToRebuild.length === 0) {
90
+ return;
91
+ }
92
+
93
+ // Rebuilding a table that OTHER tables reference by foreign key requires FK
94
+ // enforcement OFF: `DROP TABLE` performs an implicit DELETE that would otherwise
95
+ // fire `ON DELETE CASCADE` and wipe the child rows (deferral only delays the
96
+ // constraint *check*, not the cascade *action*). `PRAGMA foreign_keys` is a
97
+ // no-op inside a transaction, so it is toggled on the connection around the
98
+ // transaction — safe because the migrator does not wrap SQLite migrations in a
99
+ // transaction and the run is serialized by the migration lock. Restored to ON
100
+ // afterwards, the state `configureSqliteDatabase` and the rest of the chain
101
+ // expect on the production connection.
102
+ await sql`PRAGMA foreign_keys = OFF`.execute(db);
103
+ try {
104
+ // One transaction so a mid-rebuild failure cannot leave a half-swapped schema.
105
+ await db.transaction().execute(async trx => {
106
+ for (const table of tablesToRebuild) {
107
+ await rebuildTableWithUpdatedAtDefault(trx, table);
108
+ }
109
+ });
110
+ } finally {
111
+ await sql`PRAGMA foreign_keys = ON`.execute(db);
112
+ }
113
+ }
114
+
115
+ /**
116
+ * True when `table` exists and its `updated_at` column is present with NO stored
117
+ * default (the pre-#2922 upgraded-DB shape). A corrected `(datetime('now'))`
118
+ * default reports its `dflt_value` as `datetime('now')` (non-null) and is skipped;
119
+ * a missing table or column yields no matching row and is also skipped.
120
+ *
121
+ * The table name is inlined as a literal (`sql.lit`) rather than a bound
122
+ * parameter: a parameterized `pragma_table_info(?)` read was observed to
123
+ * intermittently return a null `dflt_value` under parallel-file load (a bun:sqlite
124
+ * quirk, see #2922's test), which here — where null is the very signal that
125
+ * triggers a rebuild — would spuriously rebuild a fresh table.
126
+ */
127
+ async function updatedAtLacksDefault(db: Kysely<unknown>, table: string): Promise<boolean> {
128
+ const columns = await sql<ColumnInfoRow>`
129
+ SELECT name, dflt_value FROM pragma_table_info(${sql.lit(table)})
130
+ `.execute(db);
131
+ const updatedAt = columns.rows.find(column => column.name === "updated_at");
132
+ return updatedAt !== undefined && updatedAt.dflt_value === null;
133
+ }
134
+
135
+ /**
136
+ * Rebuild `table` so its `updated_at` column gains the `(datetime('now'))`
137
+ * default, preserving data, indexes, and triggers. Only the `updated_at` column's
138
+ * `DEFAULT` clause changes; the column set and order are untouched, so
139
+ * `INSERT ... SELECT *` lines the rows up 1:1.
140
+ */
141
+ async function rebuildTableWithUpdatedAtDefault(
142
+ trx: Kysely<unknown>,
143
+ table: string
144
+ ): Promise<void> {
145
+ const createRow = await sql<SqlRow>`
146
+ SELECT sql FROM sqlite_master WHERE type = 'table' AND name = ${table}
147
+ `.execute(trx);
148
+ const createSql = createRow.rows[0]?.sql;
149
+ if (!createSql) {
150
+ return;
151
+ }
152
+
153
+ const tempTable = `__rebuild_${table}`;
154
+ const correctedSql = addUpdatedAtDefault(replaceFirstTableName(createSql, table, tempTable));
155
+
156
+ // Capture the table's own indexes and triggers before the drop — auto-indexes
157
+ // backing PRIMARY KEY / UNIQUE constraints have a NULL `sql` and are recreated
158
+ // by the CREATE TABLE itself, so only the explicit ones need replaying.
159
+ const auxiliary = await sql<SqlRow>`
160
+ SELECT sql FROM sqlite_master
161
+ WHERE tbl_name = ${table} AND type IN ('index', 'trigger') AND sql IS NOT NULL
162
+ `.execute(trx);
163
+
164
+ // Capture the AUTOINCREMENT high-water mark. `sqlite_sequence` records the
165
+ // highest rowid EVER used so AUTOINCREMENT never reuses a deleted id; the
166
+ // copy/drop/rename below would otherwise reset it to `max(current id)` and let a
167
+ // deleted top id be handed out again. Restored after the rename.
168
+ const originalSequence = await captureSequence(trx, table);
169
+
170
+ await sql.raw(correctedSql).execute(trx);
171
+ await sql`INSERT INTO ${sql.ref(tempTable)} SELECT * FROM ${sql.ref(table)}`.execute(trx);
172
+ await sql`DROP TABLE ${sql.ref(table)}`.execute(trx);
173
+ await sql`ALTER TABLE ${sql.ref(tempTable)} RENAME TO ${sql.ref(table)}`.execute(trx);
174
+ for (const { sql: auxSql } of auxiliary.rows) {
175
+ if (auxSql) {
176
+ await sql.raw(auxSql).execute(trx);
177
+ }
178
+ }
179
+
180
+ if (originalSequence !== undefined) {
181
+ await sql`DELETE FROM sqlite_sequence WHERE name = ${table}`.execute(trx);
182
+ await sql`INSERT INTO sqlite_sequence (name, seq) VALUES (${table}, ${originalSequence})`.execute(
183
+ trx
184
+ );
185
+ }
186
+ }
187
+
188
+ /**
189
+ * Read a table's `sqlite_sequence` high-water mark, or `undefined` when the table
190
+ * is not AUTOINCREMENT / has never been inserted into (no sequence row) or the
191
+ * `sqlite_sequence` table does not exist (no AUTOINCREMENT table in the DB).
192
+ */
193
+ async function captureSequence(
194
+ trx: Kysely<unknown>,
195
+ table: string
196
+ ): Promise<number | undefined> {
197
+ const hasSequenceTable = await sql<{ name: string }>`
198
+ SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'sqlite_sequence'
199
+ `.execute(trx);
200
+ if (hasSequenceTable.rows.length === 0) {
201
+ return undefined;
202
+ }
203
+ const seqRow = await sql<{ seq: number }>`
204
+ SELECT seq FROM sqlite_sequence WHERE name = ${table}
205
+ `.execute(trx);
206
+ return seqRow.rows[0]?.seq;
207
+ }
208
+
209
+ /**
210
+ * Inject `default (datetime('now'))` into the `updated_at` column definition of a
211
+ * `CREATE TABLE` statement, right after its type keyword. Matches only when the
212
+ * column has no existing default (the type is immediately followed by `not null`),
213
+ * so it is a no-op on a schema that already carries the default and never
214
+ * double-adds. Only the first (and only) `"updated_at"` column definition is
215
+ * rewritten.
216
+ */
217
+ function addUpdatedAtDefault(createSql: string): string {
218
+ return createSql.replace(
219
+ /("updated_at"\s+\w+)(\s+not\s+null)/i,
220
+ `$1 default ${CORRECTED_DEFAULT}$2`
221
+ );
222
+ }
223
+
224
+ /**
225
+ * Replace the table name in a `CREATE TABLE` statement, tolerating the double-
226
+ * quoted (Kysely) and bare forms and arbitrary whitespace after the keyword.
227
+ * Only the first occurrence — the table being declared — is rewritten; a later
228
+ * self-reference (e.g. a table-level FK) keeps pointing at the real name so the
229
+ * copy still works.
230
+ */
231
+ function replaceFirstTableName(createSql: string, from: string, to: string): string {
232
+ return createSql.replace(
233
+ /^(\s*CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?)(?:"([^"]+)"|(\w+))/i,
234
+ (match, prefix: string, quotedName: string | undefined, bareName: string | undefined) =>
235
+ (quotedName ?? bareName) === from ? `${prefix}"${to}"` : match
236
+ );
237
+ }
238
+
239
+ export async function down(): Promise<void> {
240
+ // Irreversible repair: the corrected default carries no information worth
241
+ // reverting, so the down migration is intentionally a no-op.
242
+ }