@kaeawc/auto-mobile 0.0.85 → 0.0.87
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/README.md +3 -3
- package/README.md.backup +3 -3
- package/dist/build-info.json +10 -10
- package/dist/src/index.js +1173 -840
- package/dist/src/utils/workers/iosBundleExtractWorker.js +3 -3
- package/node_modules/strtok3/lib/stream/StreamReader.js +6 -1
- package/node_modules/strtok3/package.json +1 -1
- package/package.json +21 -17
- package/dist/schemas/managed-slot-contract.json +0 -142
- package/dist/src/db/eventTables.ts +0 -8
- package/dist/src/db/migrations/2025_12_28_000_initial_schema.ts +0 -28
- package/dist/src/db/migrations/2025_12_30_000_performance_thresholds.ts +0 -85
- package/dist/src/db/migrations/2025_12_30_001_navigation_graph.ts +0 -223
- package/dist/src/db/migrations/2025_12_31_000_accessibility_baselines.ts +0 -34
- package/dist/src/db/migrations/2025_12_31_001_memory_audit.ts +0 -120
- package/dist/src/db/migrations/2026_01_01_000_recomposition_metrics.ts +0 -44
- package/dist/src/db/migrations/2026_01_02_000_prediction_history.ts +0 -87
- package/dist/src/db/migrations/2026_01_03_000_feature_flags.ts +0 -17
- package/dist/src/db/migrations/2026_01_03_000_test_executions.ts +0 -45
- package/dist/src/db/migrations/2026_01_05_000_tool_calls.ts +0 -31
- package/dist/src/db/migrations/2026_01_08_000_test_coverage.ts +0 -109
- package/dist/src/db/migrations/2026_01_09_000_video_recordings.ts +0 -66
- package/dist/src/db/migrations/2026_01_10_000_device_snapshots.ts +0 -54
- package/dist/src/db/migrations/2026_01_11_000_installed_apps.ts +0 -33
- package/dist/src/db/migrations/2026_01_11_000_video_recording_highlights.ts +0 -24
- package/dist/src/db/migrations/2026_01_13_000_installed_apps_session_tracking.ts +0 -27
- package/dist/src/db/migrations/2026_01_14_000_appearance_config.ts +0 -16
- package/dist/src/db/migrations/2026_01_25_000_navigation_screenshots.ts +0 -27
- package/dist/src/db/migrations/2026_01_25_001_test_run_details.ts +0 -97
- package/dist/src/db/migrations/2026_01_27_000_crash_anr_monitoring.ts +0 -204
- package/dist/src/db/migrations/2026_01_27_000_failures.ts +0 -217
- package/dist/src/db/migrations/2026_01_29_000_named_nodes.ts +0 -88
- package/dist/src/db/migrations/2026_01_30_000_performance_live_metrics.ts +0 -108
- package/dist/src/db/migrations/2026_03_15_000_telemetry_events.ts +0 -184
- package/dist/src/db/migrations/2026_03_18_000_navigation_events.ts +0 -36
- package/dist/src/db/migrations/2026_03_19_000_storage_events.ts +0 -37
- package/dist/src/db/migrations/2026_03_19_001_layout_events.ts +0 -39
- package/dist/src/db/migrations/2026_03_19_002_storage_events_previous_value.ts +0 -23
- package/dist/src/db/migrations/2026_03_19_003_layout_events_screen_name.ts +0 -23
- package/dist/src/db/migrations/2026_03_20_000_network_event_details.ts +0 -32
- package/dist/src/db/migrations/2026_04_01_000_drop_custom_events.ts +0 -41
- package/dist/src/db/migrations/2026_04_02_000_device_sessions.ts +0 -51
- package/dist/src/db/migrations/2026_07_01_000_failure_groups_signature_unique.ts +0 -162
- package/dist/src/db/migrations/2026_07_02_000_event_composite_indexes.ts +0 -62
- package/dist/src/db/migrations/2026_07_03_000_drop_redundant_device_indexes.ts +0 -66
- package/dist/src/db/migrations/2026_07_03_000_repair_datetime_now_defaults.ts +0 -278
- package/dist/src/db/migrations/2026_07_04_000_storage_events_key_lookup.ts +0 -60
- package/dist/src/db/migrations/2026_07_05_000_repair_updated_at_defaults.ts +0 -239
- package/dist/src/db/migrations/2026_07_24_000_device_locks.ts +0 -27
- package/dist/src/db/migrations/2026_07_27_000_session_tool_capabilities.ts +0 -17
- package/dist/src/db/migrations/2026_07_31_000_video_recordings_owner_session.ts +0 -39
- package/dist/src/db/migrations/2026_08_02_000_navigation_provenance.ts +0 -159
- package/dist/src/db/migrations/2026_08_17_000_emulator_loss_incidents.ts +0 -39
- package/dist/src/db/migrations/2026_08_22_000_provision_device_operations.ts +0 -21
- package/dist/src/db/migrations/2026_08_22_001_session_tool_overrides.ts +0 -106
- package/dist/src/db/migrations/2026_08_22_002_navigation_observation_eviction_index.ts +0 -67
- package/dist/src/db/migrations/2026_08_22_003_device_teardown_operations.ts +0 -19
- package/dist/src/db/migrations/2026_08_23_000_device_teardown_operation_owner.ts +0 -19
- package/dist/src/db/migrations/2026_08_23_001_default_existing_delete_device_disabled.ts +0 -26
- package/dist/src/db/migrations/2026_08_24_000_installed_apps_profile_type.ts +0 -15
- package/dist/src/db/migrations/2026_09_06_000_tool_selection_profile_provenance.ts +0 -36
- package/dist/src/db/migrations/2026_09_08_000_provision_device_operations_expiry.ts +0 -25
- package/dist/src/db/migrations/2026_09_10_000_provision_device_operation_attempt.ts +0 -25
- package/dist/src/db/migrations/2026_09_10_001_device_session_retention_index.ts +0 -14
- package/dist/src/db/migrations/2026_09_13_000_device_snapshot_vm_reclaim.ts +0 -63
- package/dist/src/db/migrations/2026_09_14_000_device_session_stable_identity.ts +0 -9
- package/dist/src/db/migrations/2026_09_14_001_device_session_identity_writer_fence.ts +0 -45
- package/dist/src/db/migrations/2026_09_16_000_device_session_liveness_owner.ts +0 -9
- package/dist/src/db/migrations/2026_09_16_001_device_session_liveness_contract.ts +0 -37
- package/dist/src/db/migrations/2026_09_17_000_device_session_liveness_writer_fence.ts +0 -48
- package/dist/src/db/migrations/2026_09_17_001_device_session_identity_fence_same_serial.ts +0 -53
- package/dist/src/db/migrations/2026_09_20_000_provision_device_lifecycle_outcome.ts +0 -32
- package/dist/src/db/migrations/2026_09_29_000_navigation_suggestion_provenance.ts +0 -41
- package/dist/src/db/migrations/2026_09_30_000_video_recording_display.ts +0 -17
- package/dist/src/db/migrations/2026_09_30_001_network_capture_fields.ts +0 -30
- package/dist/src/db/migrations/2026_10_05_000_device_locks_device_identity.ts +0 -28
- package/dist/src/db/migrations/2026_10_05_001_storage_events_key_lookup_insertion_order.ts +0 -38
- package/dist/src/db/migrations/2026_10_05_002_test_edge_coverage_edge_id_index.ts +0 -19
- package/dist/src/db/migrations/2026_10_09_000_device_resource_applications.ts +0 -22
- package/dist/src/db/migrations/2026_10_09_001_drop_provision_device_operations.ts +0 -32
- package/dist/src/db/migrations/2026_10_09_002_drop_device_teardown_operations.ts +0 -28
- package/dist/src/db/migrations/2026_10_12_000_rename_recording_tool_names.ts +0 -44
- package/dist/src/db/migrations/2026_10_12_001_video_recording_capture_teardown.ts +0 -17
- package/dist/src/db/migrations/2026_10_13_000_video_recording_recorder_daemon.ts +0 -17
- package/dist/src/index.js.map +0 -1
- package/dist/src/utils/workers/iosBundleExtractWorker.js.map +0 -1
- package/schemas/managed-slot-contract.json +0 -142
|
@@ -1,217 +0,0 @@
|
|
|
1
|
-
import { type Kysely, sql } from "kysely";
|
|
2
|
-
|
|
3
|
-
// Keep raw migrations independent of app modules that are not shipped beside them.
|
|
4
|
-
interface SqliteCatalogSchema {
|
|
5
|
-
sqlite_master: { type: string; name: string };
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
async function tableExists(db: Kysely<unknown>, tableName: string): Promise<boolean> {
|
|
9
|
-
const result = await (db as Kysely<SqliteCatalogSchema>)
|
|
10
|
-
.selectFrom("sqlite_master")
|
|
11
|
-
.select("name")
|
|
12
|
-
.where("type", "=", "table")
|
|
13
|
-
.where("name", "=", tableName)
|
|
14
|
-
.execute();
|
|
15
|
-
return result.length > 0;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export async function up(db: Kysely<unknown>): Promise<void> {
|
|
19
|
-
await createFailureGroups(db);
|
|
20
|
-
await createFailureOccurrences(db);
|
|
21
|
-
await createFailureOccurrenceScreens(db);
|
|
22
|
-
await createFailureCaptures(db);
|
|
23
|
-
await createFailureNotifications(db);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
async function createFailureGroups(db: Kysely<unknown>): Promise<void> {
|
|
27
|
-
// Main failure groups table - groups similar failures by signature
|
|
28
|
-
if (!(await tableExists(db, "failure_groups"))) {
|
|
29
|
-
await db.schema
|
|
30
|
-
.createTable("failure_groups")
|
|
31
|
-
.ifNotExists()
|
|
32
|
-
.addColumn("id", "text", (col) => col.primaryKey())
|
|
33
|
-
.addColumn("type", "text", (col) => col.notNull()) // crash, anr, tool_failure
|
|
34
|
-
.addColumn("signature", "text", (col) => col.notNull()) // Stack trace hash or tool+error
|
|
35
|
-
.addColumn("title", "text", (col) => col.notNull())
|
|
36
|
-
.addColumn("message", "text", (col) => col.notNull())
|
|
37
|
-
.addColumn("severity", "text", (col) => col.notNull()) // critical, high, medium, low
|
|
38
|
-
.addColumn("first_occurrence", "integer", (col) => col.notNull())
|
|
39
|
-
.addColumn("last_occurrence", "integer", (col) => col.notNull())
|
|
40
|
-
.addColumn("total_count", "integer", (col) => col.notNull().defaultTo(0))
|
|
41
|
-
.addColumn("unique_sessions", "integer", (col) => col.notNull().defaultTo(0))
|
|
42
|
-
.addColumn("stack_trace_json", "text") // JSON array of StackTraceElement
|
|
43
|
-
.addColumn("tool_call_info_json", "text") // JSON of AggregatedToolCallInfo
|
|
44
|
-
.addColumn("created_at", "text", (col) => col.notNull().defaultTo(sql`(datetime('now'))`))
|
|
45
|
-
.addColumn("updated_at", "text", (col) => col.notNull().defaultTo(sql`(datetime('now'))`))
|
|
46
|
-
.execute();
|
|
47
|
-
|
|
48
|
-
await db.schema
|
|
49
|
-
.createIndex("idx_failure_groups_type")
|
|
50
|
-
.ifNotExists()
|
|
51
|
-
.on("failure_groups")
|
|
52
|
-
.column("type")
|
|
53
|
-
.execute();
|
|
54
|
-
|
|
55
|
-
await db.schema
|
|
56
|
-
.createIndex("idx_failure_groups_signature")
|
|
57
|
-
.ifNotExists()
|
|
58
|
-
.on("failure_groups")
|
|
59
|
-
.column("signature")
|
|
60
|
-
.execute();
|
|
61
|
-
|
|
62
|
-
await db.schema
|
|
63
|
-
.createIndex("idx_failure_groups_last_occurrence")
|
|
64
|
-
.ifNotExists()
|
|
65
|
-
.on("failure_groups")
|
|
66
|
-
.column("last_occurrence")
|
|
67
|
-
.execute();
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
async function createFailureOccurrences(db: Kysely<unknown>): Promise<void> {
|
|
72
|
-
// Individual failure occurrences
|
|
73
|
-
if (!(await tableExists(db, "failure_occurrences"))) {
|
|
74
|
-
await db.schema
|
|
75
|
-
.createTable("failure_occurrences")
|
|
76
|
-
.ifNotExists()
|
|
77
|
-
.addColumn("id", "text", (col) => col.primaryKey())
|
|
78
|
-
.addColumn("group_id", "text", (col) =>
|
|
79
|
-
col.notNull().references("failure_groups.id").onDelete("cascade"),
|
|
80
|
-
)
|
|
81
|
-
.addColumn("timestamp", "integer", (col) => col.notNull())
|
|
82
|
-
.addColumn("device_id", "text")
|
|
83
|
-
.addColumn("device_model", "text", (col) => col.notNull())
|
|
84
|
-
.addColumn("os", "text", (col) => col.notNull())
|
|
85
|
-
.addColumn("app_version", "text", (col) => col.notNull())
|
|
86
|
-
.addColumn("session_id", "text", (col) => col.notNull())
|
|
87
|
-
.addColumn("screen_at_failure", "text")
|
|
88
|
-
.addColumn("test_name", "text")
|
|
89
|
-
.addColumn("test_execution_id", "integer") // FK to test_executions if from a test
|
|
90
|
-
.addColumn("error_code", "text") // For tool failures
|
|
91
|
-
.addColumn("duration_ms", "integer") // For tool failures
|
|
92
|
-
.addColumn("tool_args_json", "text") // For tool failures - JSON of parameters
|
|
93
|
-
.addColumn("created_at", "text", (col) => col.notNull().defaultTo(sql`(datetime('now'))`))
|
|
94
|
-
.execute();
|
|
95
|
-
|
|
96
|
-
await db.schema
|
|
97
|
-
.createIndex("idx_failure_occurrences_group")
|
|
98
|
-
.ifNotExists()
|
|
99
|
-
.on("failure_occurrences")
|
|
100
|
-
.column("group_id")
|
|
101
|
-
.execute();
|
|
102
|
-
|
|
103
|
-
await db.schema
|
|
104
|
-
.createIndex("idx_failure_occurrences_timestamp")
|
|
105
|
-
.ifNotExists()
|
|
106
|
-
.on("failure_occurrences")
|
|
107
|
-
.column("timestamp")
|
|
108
|
-
.execute();
|
|
109
|
-
|
|
110
|
-
await db.schema
|
|
111
|
-
.createIndex("idx_failure_occurrences_session")
|
|
112
|
-
.ifNotExists()
|
|
113
|
-
.on("failure_occurrences")
|
|
114
|
-
.column("session_id")
|
|
115
|
-
.execute();
|
|
116
|
-
|
|
117
|
-
await db.schema
|
|
118
|
-
.createIndex("idx_failure_occurrences_device")
|
|
119
|
-
.ifNotExists()
|
|
120
|
-
.on("failure_occurrences")
|
|
121
|
-
.column("device_id")
|
|
122
|
-
.execute();
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
async function createFailureOccurrenceScreens(db: Kysely<unknown>): Promise<void> {
|
|
127
|
-
// Screens visited during a failure occurrence
|
|
128
|
-
if (!(await tableExists(db, "failure_occurrence_screens"))) {
|
|
129
|
-
await db.schema
|
|
130
|
-
.createTable("failure_occurrence_screens")
|
|
131
|
-
.ifNotExists()
|
|
132
|
-
.addColumn("id", "integer", (col) => col.primaryKey().autoIncrement())
|
|
133
|
-
.addColumn("occurrence_id", "text", (col) =>
|
|
134
|
-
col.notNull().references("failure_occurrences.id").onDelete("cascade"),
|
|
135
|
-
)
|
|
136
|
-
.addColumn("screen_name", "text", (col) => col.notNull())
|
|
137
|
-
.addColumn("visit_order", "integer", (col) => col.notNull())
|
|
138
|
-
.addColumn("created_at", "text", (col) => col.notNull().defaultTo(sql`(datetime('now'))`))
|
|
139
|
-
.execute();
|
|
140
|
-
|
|
141
|
-
await db.schema
|
|
142
|
-
.createIndex("idx_failure_occurrence_screens_occurrence")
|
|
143
|
-
.ifNotExists()
|
|
144
|
-
.on("failure_occurrence_screens")
|
|
145
|
-
.column("occurrence_id")
|
|
146
|
-
.execute();
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
async function createFailureCaptures(db: Kysely<unknown>): Promise<void> {
|
|
151
|
-
// Captures (screenshots/videos) associated with failures
|
|
152
|
-
if (!(await tableExists(db, "failure_captures"))) {
|
|
153
|
-
await db.schema
|
|
154
|
-
.createTable("failure_captures")
|
|
155
|
-
.ifNotExists()
|
|
156
|
-
.addColumn("id", "text", (col) => col.primaryKey())
|
|
157
|
-
.addColumn("occurrence_id", "text", (col) =>
|
|
158
|
-
col.notNull().references("failure_occurrences.id").onDelete("cascade"),
|
|
159
|
-
)
|
|
160
|
-
.addColumn("type", "text", (col) => col.notNull()) // screenshot, video
|
|
161
|
-
.addColumn("path", "text", (col) => col.notNull())
|
|
162
|
-
.addColumn("timestamp", "integer", (col) => col.notNull())
|
|
163
|
-
.addColumn("device_model", "text", (col) => col.notNull())
|
|
164
|
-
.addColumn("created_at", "text", (col) => col.notNull().defaultTo(sql`(datetime('now'))`))
|
|
165
|
-
.execute();
|
|
166
|
-
|
|
167
|
-
await db.schema
|
|
168
|
-
.createIndex("idx_failure_captures_occurrence")
|
|
169
|
-
.ifNotExists()
|
|
170
|
-
.on("failure_captures")
|
|
171
|
-
.column("occurrence_id")
|
|
172
|
-
.execute();
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
async function createFailureNotifications(db: Kysely<unknown>): Promise<void> {
|
|
177
|
-
// Notifications for real-time streaming - tracks which occurrences are new
|
|
178
|
-
if (!(await tableExists(db, "failure_notifications"))) {
|
|
179
|
-
await db.schema
|
|
180
|
-
.createTable("failure_notifications")
|
|
181
|
-
.ifNotExists()
|
|
182
|
-
.addColumn("id", "integer", (col) => col.primaryKey().autoIncrement())
|
|
183
|
-
.addColumn("occurrence_id", "text", (col) =>
|
|
184
|
-
col.notNull().references("failure_occurrences.id").onDelete("cascade"),
|
|
185
|
-
)
|
|
186
|
-
.addColumn("group_id", "text", (col) => col.notNull())
|
|
187
|
-
.addColumn("type", "text", (col) => col.notNull()) // crash, anr, tool_failure
|
|
188
|
-
.addColumn("severity", "text", (col) => col.notNull())
|
|
189
|
-
.addColumn("title", "text", (col) => col.notNull())
|
|
190
|
-
.addColumn("timestamp", "integer", (col) => col.notNull())
|
|
191
|
-
.addColumn("acknowledged", "integer", (col) => col.notNull().defaultTo(0)) // SQLite boolean
|
|
192
|
-
.addColumn("created_at", "text", (col) => col.notNull().defaultTo(sql`(datetime('now'))`))
|
|
193
|
-
.execute();
|
|
194
|
-
|
|
195
|
-
await db.schema
|
|
196
|
-
.createIndex("idx_failure_notifications_timestamp")
|
|
197
|
-
.ifNotExists()
|
|
198
|
-
.on("failure_notifications")
|
|
199
|
-
.column("timestamp")
|
|
200
|
-
.execute();
|
|
201
|
-
|
|
202
|
-
await db.schema
|
|
203
|
-
.createIndex("idx_failure_notifications_acknowledged")
|
|
204
|
-
.ifNotExists()
|
|
205
|
-
.on("failure_notifications")
|
|
206
|
-
.column("acknowledged")
|
|
207
|
-
.execute();
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
export async function down(db: Kysely<unknown>): Promise<void> {
|
|
212
|
-
await db.schema.dropTable("failure_notifications").ifExists().execute();
|
|
213
|
-
await db.schema.dropTable("failure_captures").ifExists().execute();
|
|
214
|
-
await db.schema.dropTable("failure_occurrence_screens").ifExists().execute();
|
|
215
|
-
await db.schema.dropTable("failure_occurrences").ifExists().execute();
|
|
216
|
-
await db.schema.dropTable("failure_groups").ifExists().execute();
|
|
217
|
-
}
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import { type Kysely, sql } from "kysely";
|
|
2
|
-
|
|
3
|
-
export async function up(db: Kysely<unknown>): Promise<void> {
|
|
4
|
-
// 1. Clear all existing navigation data (cascade handles related tables)
|
|
5
|
-
// This is a breaking change - navigation graphs will need to be rebuilt
|
|
6
|
-
await db.deleteFrom("navigation_edges" as never).execute();
|
|
7
|
-
await db.deleteFrom("navigation_nodes" as never).execute();
|
|
8
|
-
|
|
9
|
-
// 2. Create navigation_node_fingerprints table
|
|
10
|
-
// Tracks view hierarchy fingerprints associated with named navigation nodes
|
|
11
|
-
// Fingerprints are scoped per app to prevent cross-app collisions
|
|
12
|
-
await db.schema
|
|
13
|
-
.createTable("navigation_node_fingerprints")
|
|
14
|
-
.ifNotExists()
|
|
15
|
-
.addColumn("id", "integer", (col) => col.primaryKey().autoIncrement())
|
|
16
|
-
.addColumn("app_id", "text", (col) =>
|
|
17
|
-
col.notNull().references("navigation_apps.app_id").onDelete("cascade"),
|
|
18
|
-
)
|
|
19
|
-
.addColumn("node_id", "integer", (col) =>
|
|
20
|
-
col.notNull().references("navigation_nodes.id").onDelete("cascade"),
|
|
21
|
-
)
|
|
22
|
-
.addColumn("fingerprint_hash", "text", (col) => col.notNull())
|
|
23
|
-
.addColumn("fingerprint_data", "text", (col) => col.notNull())
|
|
24
|
-
.addColumn("first_seen_at", "integer", (col) => col.notNull())
|
|
25
|
-
.addColumn("last_seen_at", "integer", (col) => col.notNull())
|
|
26
|
-
.addColumn("occurrence_count", "integer", (col) => col.notNull().defaultTo(1))
|
|
27
|
-
.addColumn("created_at", "text", (col) => col.notNull().defaultTo(sql`(datetime('now'))`))
|
|
28
|
-
.execute();
|
|
29
|
-
|
|
30
|
-
// Create unique index on (app_id, fingerprint_hash) - fingerprints are unique per app
|
|
31
|
-
await db.schema
|
|
32
|
-
.createIndex("idx_navigation_node_fingerprints_app_hash")
|
|
33
|
-
.ifNotExists()
|
|
34
|
-
.on("navigation_node_fingerprints")
|
|
35
|
-
.columns(["app_id", "fingerprint_hash"])
|
|
36
|
-
.unique()
|
|
37
|
-
.execute();
|
|
38
|
-
|
|
39
|
-
// Create index on node_id for efficient lookups
|
|
40
|
-
await db.schema
|
|
41
|
-
.createIndex("idx_navigation_node_fingerprints_node")
|
|
42
|
-
.ifNotExists()
|
|
43
|
-
.on("navigation_node_fingerprints")
|
|
44
|
-
.column("node_id")
|
|
45
|
-
.execute();
|
|
46
|
-
|
|
47
|
-
// 3. Create navigation_suggestions table
|
|
48
|
-
// Stores uncorrelated fingerprints for apps that have named nodes
|
|
49
|
-
await db.schema
|
|
50
|
-
.createTable("navigation_suggestions")
|
|
51
|
-
.ifNotExists()
|
|
52
|
-
.addColumn("id", "integer", (col) => col.primaryKey().autoIncrement())
|
|
53
|
-
.addColumn("app_id", "text", (col) =>
|
|
54
|
-
col.notNull().references("navigation_apps.app_id").onDelete("cascade"),
|
|
55
|
-
)
|
|
56
|
-
.addColumn("fingerprint_hash", "text", (col) => col.notNull())
|
|
57
|
-
.addColumn("fingerprint_data", "text", (col) => col.notNull())
|
|
58
|
-
.addColumn("first_seen_at", "integer", (col) => col.notNull())
|
|
59
|
-
.addColumn("last_seen_at", "integer", (col) => col.notNull())
|
|
60
|
-
.addColumn("occurrence_count", "integer", (col) => col.notNull().defaultTo(1))
|
|
61
|
-
.addColumn("promoted_to_fingerprint_id", "integer", (col) =>
|
|
62
|
-
col.references("navigation_node_fingerprints.id").onDelete("set null"),
|
|
63
|
-
)
|
|
64
|
-
.addColumn("created_at", "text", (col) => col.notNull().defaultTo(sql`(datetime('now'))`))
|
|
65
|
-
.execute();
|
|
66
|
-
|
|
67
|
-
// Create unique index on (app_id, fingerprint_hash) for deduplication
|
|
68
|
-
await db.schema
|
|
69
|
-
.createIndex("idx_navigation_suggestions_app_hash")
|
|
70
|
-
.ifNotExists()
|
|
71
|
-
.on("navigation_suggestions")
|
|
72
|
-
.columns(["app_id", "fingerprint_hash"])
|
|
73
|
-
.unique()
|
|
74
|
-
.execute();
|
|
75
|
-
|
|
76
|
-
// Create index on app_id for efficient queries
|
|
77
|
-
await db.schema
|
|
78
|
-
.createIndex("idx_navigation_suggestions_app")
|
|
79
|
-
.ifNotExists()
|
|
80
|
-
.on("navigation_suggestions")
|
|
81
|
-
.column("app_id")
|
|
82
|
-
.execute();
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export async function down(db: Kysely<unknown>): Promise<void> {
|
|
86
|
-
await db.schema.dropTable("navigation_suggestions").execute();
|
|
87
|
-
await db.schema.dropTable("navigation_node_fingerprints").execute();
|
|
88
|
-
}
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
import type { Kysely } from "kysely";
|
|
2
|
-
import { sql } from "kysely";
|
|
3
|
-
|
|
4
|
-
async function columnExists(
|
|
5
|
-
db: Kysely<unknown>,
|
|
6
|
-
tableName: string,
|
|
7
|
-
columnName: string,
|
|
8
|
-
): Promise<boolean> {
|
|
9
|
-
const result = await sql<{ name: string }>`
|
|
10
|
-
SELECT name FROM pragma_table_info(${tableName}) WHERE name = ${columnName}
|
|
11
|
-
`.execute(db);
|
|
12
|
-
return result.rows.length > 0;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
async function addColumnIfNotExists(
|
|
16
|
-
db: Kysely<unknown>,
|
|
17
|
-
tableName: string,
|
|
18
|
-
columnName: string,
|
|
19
|
-
columnType: "real" | "integer",
|
|
20
|
-
): Promise<void> {
|
|
21
|
-
if (await columnExists(db, tableName, columnName)) {
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
await db.schema.alterTable(tableName).addColumn(columnName, columnType).execute();
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export async function up(db: Kysely<unknown>): Promise<void> {
|
|
28
|
-
// Add new columns to performance_audit_results table for live metrics
|
|
29
|
-
await addColumnIfNotExists(db, "performance_audit_results", "time_to_first_frame_ms", "real");
|
|
30
|
-
await addColumnIfNotExists(db, "performance_audit_results", "time_to_interactive_ms", "real");
|
|
31
|
-
await addColumnIfNotExists(db, "performance_audit_results", "frame_rate_fps", "real");
|
|
32
|
-
await addColumnIfNotExists(db, "performance_audit_results", "node_id", "integer");
|
|
33
|
-
|
|
34
|
-
// Create index on node_id for navigation node lookups
|
|
35
|
-
await db.schema
|
|
36
|
-
.createIndex("idx_performance_audit_results_node_id")
|
|
37
|
-
.ifNotExists()
|
|
38
|
-
.on("performance_audit_results")
|
|
39
|
-
.column("node_id")
|
|
40
|
-
.execute();
|
|
41
|
-
|
|
42
|
-
// Create composite index on (package_name, timestamp) for efficient pruning
|
|
43
|
-
await db.schema
|
|
44
|
-
.createIndex("idx_performance_audit_results_package_timestamp")
|
|
45
|
-
.ifNotExists()
|
|
46
|
-
.on("performance_audit_results")
|
|
47
|
-
.columns(["package_name", "timestamp"])
|
|
48
|
-
.execute();
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export async function down(db: Kysely<unknown>): Promise<void> {
|
|
52
|
-
// Drop indexes first
|
|
53
|
-
await db.schema.dropIndex("idx_performance_audit_results_package_timestamp").execute();
|
|
54
|
-
|
|
55
|
-
await db.schema.dropIndex("idx_performance_audit_results_node_id").execute();
|
|
56
|
-
|
|
57
|
-
// SQLite doesn't support DROP COLUMN directly, so we need to recreate the table
|
|
58
|
-
// For simplicity in migration rollback, we'll just drop and recreate the table
|
|
59
|
-
// This is acceptable for development but should be handled more carefully in production
|
|
60
|
-
|
|
61
|
-
// Create a backup table with original schema
|
|
62
|
-
await db.schema
|
|
63
|
-
.createTable("performance_audit_results_backup")
|
|
64
|
-
.addColumn("id", "integer", (col) => col.primaryKey().autoIncrement())
|
|
65
|
-
.addColumn("device_id", "text", (col) => col.notNull())
|
|
66
|
-
.addColumn("session_id", "text", (col) => col.notNull())
|
|
67
|
-
.addColumn("package_name", "text", (col) => col.notNull())
|
|
68
|
-
.addColumn("timestamp", "text", (col) => col.notNull())
|
|
69
|
-
.addColumn("passed", "integer", (col) => col.notNull())
|
|
70
|
-
.addColumn("p50_ms", "real")
|
|
71
|
-
.addColumn("p90_ms", "real")
|
|
72
|
-
.addColumn("p95_ms", "real")
|
|
73
|
-
.addColumn("p99_ms", "real")
|
|
74
|
-
.addColumn("jank_count", "integer")
|
|
75
|
-
.addColumn("missed_vsync_count", "integer")
|
|
76
|
-
.addColumn("slow_ui_thread_count", "integer")
|
|
77
|
-
.addColumn("frame_deadline_missed_count", "integer")
|
|
78
|
-
.addColumn("cpu_usage_percent", "real")
|
|
79
|
-
.addColumn("touch_latency_ms", "real")
|
|
80
|
-
.addColumn("diagnostics_json", "text")
|
|
81
|
-
.addColumn("created_at", "text", (col) => col.notNull().defaultTo(sql`(datetime('now'))`))
|
|
82
|
-
.execute();
|
|
83
|
-
|
|
84
|
-
// Copy data to backup
|
|
85
|
-
await sql`
|
|
86
|
-
INSERT INTO performance_audit_results_backup
|
|
87
|
-
SELECT id, device_id, session_id, package_name, timestamp, passed,
|
|
88
|
-
p50_ms, p90_ms, p95_ms, p99_ms, jank_count, missed_vsync_count,
|
|
89
|
-
slow_ui_thread_count, frame_deadline_missed_count, cpu_usage_percent,
|
|
90
|
-
touch_latency_ms, diagnostics_json, created_at
|
|
91
|
-
FROM performance_audit_results
|
|
92
|
-
`.execute(db);
|
|
93
|
-
|
|
94
|
-
// Drop original table
|
|
95
|
-
await db.schema.dropTable("performance_audit_results").execute();
|
|
96
|
-
|
|
97
|
-
// Rename backup to original
|
|
98
|
-
await sql`ALTER TABLE performance_audit_results_backup RENAME TO performance_audit_results`.execute(
|
|
99
|
-
db,
|
|
100
|
-
);
|
|
101
|
-
|
|
102
|
-
// Recreate original index
|
|
103
|
-
await db.schema
|
|
104
|
-
.createIndex("idx_performance_audit_results_device_timestamp")
|
|
105
|
-
.on("performance_audit_results")
|
|
106
|
-
.columns(["device_id", "timestamp"])
|
|
107
|
-
.execute();
|
|
108
|
-
}
|
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
import { type Kysely, sql } from "kysely";
|
|
2
|
-
|
|
3
|
-
// Keep raw migrations independent of app modules that are not shipped beside them.
|
|
4
|
-
interface SqliteCatalogSchema {
|
|
5
|
-
sqlite_master: { type: string; name: string };
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
async function tableExists(db: Kysely<unknown>, tableName: string): Promise<boolean> {
|
|
9
|
-
const result = await (db as Kysely<SqliteCatalogSchema>)
|
|
10
|
-
.selectFrom("sqlite_master")
|
|
11
|
-
.select("name")
|
|
12
|
-
.where("type", "=", "table")
|
|
13
|
-
.where("name", "=", tableName)
|
|
14
|
-
.execute();
|
|
15
|
-
return result.length > 0;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export async function up(db: Kysely<unknown>): Promise<void> {
|
|
19
|
-
// Network events table
|
|
20
|
-
if (!(await tableExists(db, "network_events"))) {
|
|
21
|
-
await db.schema
|
|
22
|
-
.createTable("network_events")
|
|
23
|
-
.ifNotExists()
|
|
24
|
-
.addColumn("id", "integer", (col) => col.primaryKey().autoIncrement())
|
|
25
|
-
.addColumn("device_id", "text")
|
|
26
|
-
.addColumn("timestamp", "integer", (col) => col.notNull())
|
|
27
|
-
.addColumn("application_id", "text")
|
|
28
|
-
.addColumn("session_id", "text")
|
|
29
|
-
.addColumn("url", "text", (col) => col.notNull())
|
|
30
|
-
.addColumn("method", "text", (col) => col.notNull())
|
|
31
|
-
.addColumn("status_code", "integer", (col) => col.notNull().defaultTo(0))
|
|
32
|
-
.addColumn("duration_ms", "integer", (col) => col.notNull().defaultTo(0))
|
|
33
|
-
.addColumn("request_body_size", "integer", (col) => col.defaultTo(-1))
|
|
34
|
-
.addColumn("response_body_size", "integer", (col) => col.defaultTo(-1))
|
|
35
|
-
.addColumn("protocol", "text")
|
|
36
|
-
.addColumn("host", "text")
|
|
37
|
-
.addColumn("path", "text")
|
|
38
|
-
.addColumn("error", "text")
|
|
39
|
-
.addColumn("created_at", "text", (col) => col.notNull().defaultTo(sql`(datetime('now'))`))
|
|
40
|
-
.execute();
|
|
41
|
-
|
|
42
|
-
await db.schema
|
|
43
|
-
.createIndex("idx_network_events_timestamp")
|
|
44
|
-
.ifNotExists()
|
|
45
|
-
.on("network_events")
|
|
46
|
-
.column("timestamp")
|
|
47
|
-
.execute();
|
|
48
|
-
|
|
49
|
-
await db.schema
|
|
50
|
-
.createIndex("idx_network_events_host")
|
|
51
|
-
.ifNotExists()
|
|
52
|
-
.on("network_events")
|
|
53
|
-
.column("host")
|
|
54
|
-
.execute();
|
|
55
|
-
|
|
56
|
-
await db.schema
|
|
57
|
-
.createIndex("idx_network_events_device")
|
|
58
|
-
.ifNotExists()
|
|
59
|
-
.on("network_events")
|
|
60
|
-
.column("device_id")
|
|
61
|
-
.execute();
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Log events table
|
|
65
|
-
if (!(await tableExists(db, "log_events"))) {
|
|
66
|
-
await db.schema
|
|
67
|
-
.createTable("log_events")
|
|
68
|
-
.ifNotExists()
|
|
69
|
-
.addColumn("id", "integer", (col) => col.primaryKey().autoIncrement())
|
|
70
|
-
.addColumn("device_id", "text")
|
|
71
|
-
.addColumn("timestamp", "integer", (col) => col.notNull())
|
|
72
|
-
.addColumn("application_id", "text")
|
|
73
|
-
.addColumn("session_id", "text")
|
|
74
|
-
.addColumn("level", "integer", (col) => col.notNull())
|
|
75
|
-
.addColumn("tag", "text", (col) => col.notNull())
|
|
76
|
-
.addColumn("message", "text", (col) => col.notNull())
|
|
77
|
-
.addColumn("filter_name", "text", (col) => col.notNull())
|
|
78
|
-
.addColumn("created_at", "text", (col) => col.notNull().defaultTo(sql`(datetime('now'))`))
|
|
79
|
-
.execute();
|
|
80
|
-
|
|
81
|
-
await db.schema
|
|
82
|
-
.createIndex("idx_log_events_timestamp")
|
|
83
|
-
.ifNotExists()
|
|
84
|
-
.on("log_events")
|
|
85
|
-
.column("timestamp")
|
|
86
|
-
.execute();
|
|
87
|
-
|
|
88
|
-
await db.schema
|
|
89
|
-
.createIndex("idx_log_events_tag")
|
|
90
|
-
.ifNotExists()
|
|
91
|
-
.on("log_events")
|
|
92
|
-
.column("tag")
|
|
93
|
-
.execute();
|
|
94
|
-
|
|
95
|
-
await db.schema
|
|
96
|
-
.createIndex("idx_log_events_device")
|
|
97
|
-
.ifNotExists()
|
|
98
|
-
.on("log_events")
|
|
99
|
-
.column("device_id")
|
|
100
|
-
.execute();
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// Custom events table
|
|
104
|
-
if (!(await tableExists(db, "custom_events"))) {
|
|
105
|
-
await db.schema
|
|
106
|
-
.createTable("custom_events")
|
|
107
|
-
.ifNotExists()
|
|
108
|
-
.addColumn("id", "integer", (col) => col.primaryKey().autoIncrement())
|
|
109
|
-
.addColumn("device_id", "text")
|
|
110
|
-
.addColumn("timestamp", "integer", (col) => col.notNull())
|
|
111
|
-
.addColumn("application_id", "text")
|
|
112
|
-
.addColumn("session_id", "text")
|
|
113
|
-
.addColumn("name", "text", (col) => col.notNull())
|
|
114
|
-
.addColumn("properties_json", "text") // JSON of string key-value pairs
|
|
115
|
-
.addColumn("created_at", "text", (col) => col.notNull().defaultTo(sql`(datetime('now'))`))
|
|
116
|
-
.execute();
|
|
117
|
-
|
|
118
|
-
await db.schema
|
|
119
|
-
.createIndex("idx_custom_events_timestamp")
|
|
120
|
-
.ifNotExists()
|
|
121
|
-
.on("custom_events")
|
|
122
|
-
.column("timestamp")
|
|
123
|
-
.execute();
|
|
124
|
-
|
|
125
|
-
await db.schema
|
|
126
|
-
.createIndex("idx_custom_events_name")
|
|
127
|
-
.ifNotExists()
|
|
128
|
-
.on("custom_events")
|
|
129
|
-
.column("name")
|
|
130
|
-
.execute();
|
|
131
|
-
|
|
132
|
-
await db.schema
|
|
133
|
-
.createIndex("idx_custom_events_device")
|
|
134
|
-
.ifNotExists()
|
|
135
|
-
.on("custom_events")
|
|
136
|
-
.column("device_id")
|
|
137
|
-
.execute();
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// OS events table (lifecycle, broadcast, websocket_frame)
|
|
141
|
-
if (!(await tableExists(db, "os_events"))) {
|
|
142
|
-
await db.schema
|
|
143
|
-
.createTable("os_events")
|
|
144
|
-
.ifNotExists()
|
|
145
|
-
.addColumn("id", "integer", (col) => col.primaryKey().autoIncrement())
|
|
146
|
-
.addColumn("device_id", "text")
|
|
147
|
-
.addColumn("timestamp", "integer", (col) => col.notNull())
|
|
148
|
-
.addColumn("application_id", "text")
|
|
149
|
-
.addColumn("session_id", "text")
|
|
150
|
-
.addColumn("category", "text", (col) => col.notNull()) // lifecycle, broadcast, websocket_frame
|
|
151
|
-
.addColumn("kind", "text", (col) => col.notNull()) // e.g., foreground, screen_on, connectivity_change
|
|
152
|
-
.addColumn("details_json", "text") // JSON of additional details
|
|
153
|
-
.addColumn("created_at", "text", (col) => col.notNull().defaultTo(sql`(datetime('now'))`))
|
|
154
|
-
.execute();
|
|
155
|
-
|
|
156
|
-
await db.schema
|
|
157
|
-
.createIndex("idx_os_events_timestamp")
|
|
158
|
-
.ifNotExists()
|
|
159
|
-
.on("os_events")
|
|
160
|
-
.column("timestamp")
|
|
161
|
-
.execute();
|
|
162
|
-
|
|
163
|
-
await db.schema
|
|
164
|
-
.createIndex("idx_os_events_category")
|
|
165
|
-
.ifNotExists()
|
|
166
|
-
.on("os_events")
|
|
167
|
-
.column("category")
|
|
168
|
-
.execute();
|
|
169
|
-
|
|
170
|
-
await db.schema
|
|
171
|
-
.createIndex("idx_os_events_device")
|
|
172
|
-
.ifNotExists()
|
|
173
|
-
.on("os_events")
|
|
174
|
-
.column("device_id")
|
|
175
|
-
.execute();
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
export async function down(db: Kysely<unknown>): Promise<void> {
|
|
180
|
-
await db.schema.dropTable("os_events").ifExists().execute();
|
|
181
|
-
await db.schema.dropTable("custom_events").ifExists().execute();
|
|
182
|
-
await db.schema.dropTable("log_events").ifExists().execute();
|
|
183
|
-
await db.schema.dropTable("network_events").ifExists().execute();
|
|
184
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { type Kysely, sql } from "kysely";
|
|
2
|
-
|
|
3
|
-
export async function up(db: Kysely<unknown>): Promise<void> {
|
|
4
|
-
await db.schema
|
|
5
|
-
.createTable("navigation_events")
|
|
6
|
-
.ifNotExists()
|
|
7
|
-
.addColumn("id", "integer", (col) => col.primaryKey().autoIncrement())
|
|
8
|
-
.addColumn("device_id", "text")
|
|
9
|
-
.addColumn("timestamp", "integer", (col) => col.notNull())
|
|
10
|
-
.addColumn("application_id", "text")
|
|
11
|
-
.addColumn("session_id", "text")
|
|
12
|
-
.addColumn("destination", "text", (col) => col.notNull())
|
|
13
|
-
.addColumn("source", "text")
|
|
14
|
-
.addColumn("arguments_json", "text")
|
|
15
|
-
.addColumn("metadata_json", "text")
|
|
16
|
-
.addColumn("created_at", "text", (col) => col.notNull().defaultTo(sql`(datetime('now'))`))
|
|
17
|
-
.execute();
|
|
18
|
-
|
|
19
|
-
await db.schema
|
|
20
|
-
.createIndex("idx_navigation_events_timestamp")
|
|
21
|
-
.ifNotExists()
|
|
22
|
-
.on("navigation_events")
|
|
23
|
-
.column("timestamp")
|
|
24
|
-
.execute();
|
|
25
|
-
|
|
26
|
-
await db.schema
|
|
27
|
-
.createIndex("idx_navigation_events_device")
|
|
28
|
-
.ifNotExists()
|
|
29
|
-
.on("navigation_events")
|
|
30
|
-
.column("device_id")
|
|
31
|
-
.execute();
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export async function down(db: Kysely<unknown>): Promise<void> {
|
|
35
|
-
await db.schema.dropTable("navigation_events").ifExists().execute();
|
|
36
|
-
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { type Kysely, sql } from "kysely";
|
|
2
|
-
|
|
3
|
-
export async function up(db: Kysely<unknown>): Promise<void> {
|
|
4
|
-
await db.schema
|
|
5
|
-
.createTable("storage_events")
|
|
6
|
-
.ifNotExists()
|
|
7
|
-
.addColumn("id", "integer", (col) => col.primaryKey().autoIncrement())
|
|
8
|
-
.addColumn("device_id", "text")
|
|
9
|
-
.addColumn("timestamp", "integer", (col) => col.notNull())
|
|
10
|
-
.addColumn("application_id", "text")
|
|
11
|
-
.addColumn("session_id", "text")
|
|
12
|
-
.addColumn("file_name", "text", (col) => col.notNull())
|
|
13
|
-
.addColumn("key", "text")
|
|
14
|
-
.addColumn("value", "text")
|
|
15
|
-
.addColumn("value_type", "text")
|
|
16
|
-
.addColumn("change_type", "text", (col) => col.notNull())
|
|
17
|
-
.addColumn("created_at", "text", (col) => col.notNull().defaultTo(sql`(datetime('now'))`))
|
|
18
|
-
.execute();
|
|
19
|
-
|
|
20
|
-
await db.schema
|
|
21
|
-
.createIndex("idx_storage_events_timestamp")
|
|
22
|
-
.ifNotExists()
|
|
23
|
-
.on("storage_events")
|
|
24
|
-
.column("timestamp")
|
|
25
|
-
.execute();
|
|
26
|
-
|
|
27
|
-
await db.schema
|
|
28
|
-
.createIndex("idx_storage_events_device")
|
|
29
|
-
.ifNotExists()
|
|
30
|
-
.on("storage_events")
|
|
31
|
-
.column("device_id")
|
|
32
|
-
.execute();
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export async function down(db: Kysely<unknown>): Promise<void> {
|
|
36
|
-
await db.schema.dropTable("storage_events").ifExists().execute();
|
|
37
|
-
}
|