@gokiteam/goki-dev 0.2.0

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 (205) hide show
  1. package/README.md +478 -0
  2. package/bin/goki-dev.js +452 -0
  3. package/bin/mcp-server.js +16 -0
  4. package/bin/secrets-cli.js +302 -0
  5. package/cli/ComposeOverrideGenerator.js +226 -0
  6. package/cli/ComposeParser.js +73 -0
  7. package/cli/ConfigGenerator.js +304 -0
  8. package/cli/ConfigManager.js +46 -0
  9. package/cli/DatabaseManager.js +94 -0
  10. package/cli/DevToolsChecker.js +21 -0
  11. package/cli/DevToolsDir.js +66 -0
  12. package/cli/DevToolsManager.js +451 -0
  13. package/cli/DockerManager.js +138 -0
  14. package/cli/FunctionManager.js +95 -0
  15. package/cli/HttpProxyRewriter.js +91 -0
  16. package/cli/Logger.js +10 -0
  17. package/cli/McpConfigManager.js +123 -0
  18. package/cli/NgrokManager.js +431 -0
  19. package/cli/ProjectCLI.js +2322 -0
  20. package/cli/PubSubManager.js +129 -0
  21. package/cli/SnapshotManager.js +88 -0
  22. package/cli/UiFormatter.js +292 -0
  23. package/cli/WebhookUrlRewriter.js +32 -0
  24. package/cli/secrets/BiometricAuth.js +125 -0
  25. package/cli/secrets/SecretInjector.js +47 -0
  26. package/cli/secrets/SecretsConfig.js +141 -0
  27. package/cli/secrets/SecretsDoctor.js +384 -0
  28. package/cli/secrets/SecretsManager.js +255 -0
  29. package/client/dist/client.d.ts +332 -0
  30. package/client/dist/client.js +507 -0
  31. package/client/dist/helpers.d.ts +62 -0
  32. package/client/dist/helpers.js +122 -0
  33. package/client/dist/index.d.ts +59 -0
  34. package/client/dist/index.js +78 -0
  35. package/client/dist/package.json +1 -0
  36. package/client/dist/types.d.ts +280 -0
  37. package/client/dist/types.js +7 -0
  38. package/config.development +46 -0
  39. package/config.test +18 -0
  40. package/guidelines/CodingStyleGuideline.md +148 -0
  41. package/guidelines/CommentingGuideline.md +10 -0
  42. package/guidelines/HttpApiImplementationGuideline.md +137 -0
  43. package/guidelines/NamingGuideline.md +182 -0
  44. package/package.json +138 -0
  45. package/patterns/api/[collectionName]/Controllers.md +62 -0
  46. package/patterns/api/[collectionName]/Logic.md +154 -0
  47. package/patterns/api/[collectionName]/Permissions.md +81 -0
  48. package/patterns/api/[collectionName]/Router.md +83 -0
  49. package/patterns/api/[collectionName]/Schemas.md +197 -0
  50. package/patterns/configs/Patterns.md +7 -0
  51. package/patterns/enums/Patterns.md +24 -0
  52. package/patterns/errorHandling/Patterns.md +185 -0
  53. package/patterns/testing/Patterns.md +232 -0
  54. package/src/Server.js +238 -0
  55. package/src/api/dashboard/Controllers.js +9 -0
  56. package/src/api/dashboard/Logic.js +76 -0
  57. package/src/api/dashboard/Router.js +11 -0
  58. package/src/api/dashboard/Schemas.js +47 -0
  59. package/src/api/data/Controllers.js +26 -0
  60. package/src/api/data/Logic.js +188 -0
  61. package/src/api/data/Router.js +16 -0
  62. package/src/api/docker/Controllers.js +33 -0
  63. package/src/api/docker/Logic.js +268 -0
  64. package/src/api/docker/Router.js +15 -0
  65. package/src/api/docker/Schemas.js +80 -0
  66. package/src/api/docs/Controllers.js +15 -0
  67. package/src/api/docs/Logic.js +85 -0
  68. package/src/api/docs/Router.js +12 -0
  69. package/src/api/export/Controllers.js +30 -0
  70. package/src/api/export/Logic.js +143 -0
  71. package/src/api/export/Router.js +18 -0
  72. package/src/api/export/Schemas.js +104 -0
  73. package/src/api/firestore/Controllers.js +152 -0
  74. package/src/api/firestore/Logic.js +474 -0
  75. package/src/api/firestore/Router.js +23 -0
  76. package/src/api/functions/Controllers.js +261 -0
  77. package/src/api/functions/Logic.js +710 -0
  78. package/src/api/functions/Router.js +50 -0
  79. package/src/api/functions/Schemas.js +193 -0
  80. package/src/api/gateway/Controllers.js +72 -0
  81. package/src/api/gateway/Logic.js +74 -0
  82. package/src/api/gateway/Router.js +10 -0
  83. package/src/api/gateway/Schemas.js +19 -0
  84. package/src/api/health/Controllers.js +14 -0
  85. package/src/api/health/Logic.js +24 -0
  86. package/src/api/health/Router.js +12 -0
  87. package/src/api/httpTraffic/Controllers.js +29 -0
  88. package/src/api/httpTraffic/Logic.js +33 -0
  89. package/src/api/httpTraffic/Router.js +9 -0
  90. package/src/api/httpTraffic/Schemas.js +23 -0
  91. package/src/api/logging/Controllers.js +80 -0
  92. package/src/api/logging/Logic.js +461 -0
  93. package/src/api/logging/Router.js +24 -0
  94. package/src/api/logging/Schemas.js +43 -0
  95. package/src/api/mqtt/Controllers.js +17 -0
  96. package/src/api/mqtt/Logic.js +66 -0
  97. package/src/api/mqtt/Router.js +12 -0
  98. package/src/api/postgres/Controllers.js +97 -0
  99. package/src/api/postgres/Logic.js +221 -0
  100. package/src/api/postgres/Router.js +21 -0
  101. package/src/api/pubsub/Controllers.js +236 -0
  102. package/src/api/pubsub/Logic.js +732 -0
  103. package/src/api/pubsub/Router.js +41 -0
  104. package/src/api/pubsub/Schemas.js +355 -0
  105. package/src/api/redis/Controllers.js +63 -0
  106. package/src/api/redis/Logic.js +239 -0
  107. package/src/api/redis/Router.js +21 -0
  108. package/src/api/scheduler/Controllers.js +27 -0
  109. package/src/api/scheduler/Logic.js +49 -0
  110. package/src/api/scheduler/Router.js +16 -0
  111. package/src/api/services/Controllers.js +26 -0
  112. package/src/api/services/Logic.js +205 -0
  113. package/src/api/services/Router.js +14 -0
  114. package/src/api/services/Schemas.js +66 -0
  115. package/src/api/snapshots/Controllers.js +37 -0
  116. package/src/api/snapshots/Logic.js +797 -0
  117. package/src/api/snapshots/Router.js +15 -0
  118. package/src/api/snapshots/Schemas.js +23 -0
  119. package/src/api/webhooks/Controllers.js +49 -0
  120. package/src/api/webhooks/Logic.js +137 -0
  121. package/src/api/webhooks/Router.js +12 -0
  122. package/src/api/webhooks/Schemas.js +31 -0
  123. package/src/configs/Application.js +147 -0
  124. package/src/configs/Default.js +13 -0
  125. package/src/consumers/BlackboxLogsConsumer.js +235 -0
  126. package/src/consumers/DockerLogsConsumer.js +687 -0
  127. package/src/db/Tables.js +66 -0
  128. package/src/db/schemas/firestore.js +18 -0
  129. package/src/db/schemas/functions.js +65 -0
  130. package/src/db/schemas/httpTraffic.js +43 -0
  131. package/src/db/schemas/logging.js +74 -0
  132. package/src/db/schemas/migrations.js +64 -0
  133. package/src/db/schemas/mqtt.js +56 -0
  134. package/src/db/schemas/pubsub.js +90 -0
  135. package/src/db/schemas/pubsubRegistry.js +22 -0
  136. package/src/db/schemas/webhooks.js +28 -0
  137. package/src/emulation/awsiot/Controllers.js +91 -0
  138. package/src/emulation/awsiot/Logic.js +70 -0
  139. package/src/emulation/awsiot/Router.js +19 -0
  140. package/src/emulation/awsiot/Server.js +100 -0
  141. package/src/emulation/firestore/Server.js +136 -0
  142. package/src/emulation/logging/Controllers.js +212 -0
  143. package/src/emulation/logging/Logic.js +416 -0
  144. package/src/emulation/logging/Router.js +36 -0
  145. package/src/emulation/logging/Schemas.js +82 -0
  146. package/src/emulation/logging/Server.js +108 -0
  147. package/src/emulation/pubsub/Controllers.js +279 -0
  148. package/src/emulation/pubsub/DefaultTopics.js +162 -0
  149. package/src/emulation/pubsub/Logic.js +427 -0
  150. package/src/emulation/pubsub/README.md +309 -0
  151. package/src/emulation/pubsub/Router.js +33 -0
  152. package/src/emulation/pubsub/Server.js +104 -0
  153. package/src/emulation/pubsub/ShadowPoller.js +276 -0
  154. package/src/emulation/pubsub/ShadowSubscriptionManager.js +199 -0
  155. package/src/enums/ContainerNames.js +106 -0
  156. package/src/enums/ErrorReason.js +28 -0
  157. package/src/enums/FunctionStatuses.js +15 -0
  158. package/src/enums/FunctionTriggerTypes.js +15 -0
  159. package/src/enums/GatewayState.js +7 -0
  160. package/src/enums/ServiceNames.js +68 -0
  161. package/src/jobs/DatabaseMaintenance.js +184 -0
  162. package/src/jobs/MessageHistoryCleanup.js +152 -0
  163. package/src/mcp/ApiClient.js +25 -0
  164. package/src/mcp/Server.js +52 -0
  165. package/src/mcp/prompts/debugging.js +104 -0
  166. package/src/mcp/resources/platform.js +118 -0
  167. package/src/mcp/tools/data.js +84 -0
  168. package/src/mcp/tools/docker.js +166 -0
  169. package/src/mcp/tools/firestore.js +162 -0
  170. package/src/mcp/tools/functions.js +380 -0
  171. package/src/mcp/tools/httpTraffic.js +69 -0
  172. package/src/mcp/tools/logging.js +174 -0
  173. package/src/mcp/tools/mqtt.js +37 -0
  174. package/src/mcp/tools/postgres.js +130 -0
  175. package/src/mcp/tools/pubsub.js +316 -0
  176. package/src/mcp/tools/redis.js +146 -0
  177. package/src/mcp/tools/services.js +169 -0
  178. package/src/mcp/tools/snapshots.js +88 -0
  179. package/src/mcp/tools/webhooks.js +115 -0
  180. package/src/middleware/DevProxy.js +67 -0
  181. package/src/middleware/ErrorCatcher.js +35 -0
  182. package/src/middleware/HttpProxy.js +215 -0
  183. package/src/middleware/Reply.js +24 -0
  184. package/src/middleware/TraceId.js +9 -0
  185. package/src/middleware/WebhookProxy.js +234 -0
  186. package/src/protocols/mqtt/Broker.js +92 -0
  187. package/src/protocols/mqtt/Handlers.js +175 -0
  188. package/src/protocols/mqtt/PubSubBridge.js +162 -0
  189. package/src/protocols/mqtt/Server.js +116 -0
  190. package/src/runtime/FunctionRunner.js +179 -0
  191. package/src/services/AppGatewayService.js +582 -0
  192. package/src/singletons/FirestoreBroadcaster.js +367 -0
  193. package/src/singletons/FunctionTriggerDispatcher.js +456 -0
  194. package/src/singletons/FunctionsService.js +418 -0
  195. package/src/singletons/HttpProxy.js +224 -0
  196. package/src/singletons/LogBroadcaster.js +159 -0
  197. package/src/singletons/Logger.js +49 -0
  198. package/src/singletons/MemoryJsonStore.js +175 -0
  199. package/src/singletons/MessageBroadcaster.js +190 -0
  200. package/src/singletons/PostgresBroadcaster.js +367 -0
  201. package/src/singletons/PostgresClient.js +180 -0
  202. package/src/singletons/RedisClient.js +184 -0
  203. package/src/singletons/SqliteStore.js +480 -0
  204. package/src/singletons/TickService.js +151 -0
  205. package/src/singletons/WebhookProxy.js +223 -0
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Database table names - single source of truth
3
+ * Use these constants instead of hardcoded strings
4
+ */
5
+
6
+ export const Tables = Object.freeze({
7
+ // Pub/Sub tables
8
+ PUBSUB_TOPICS: 'pubsub_topics',
9
+ PUBSUB_SUBSCRIPTIONS: 'pubsub_subscriptions',
10
+ PUBSUB_MESSAGES: 'pubsub_messages',
11
+ PUBSUB_MESSAGE_HISTORY: 'pubsub_message_history',
12
+ PUBSUB_TOPIC_REGISTRY: 'pubsub_topic_registry',
13
+
14
+ // Logging tables
15
+ LOGGING_ENTRIES: 'logging_entries',
16
+ LOGGING_LOGS: 'logging_logs',
17
+
18
+ // MQTT tables
19
+ MQTT_CLIENTS: 'mqtt_clients',
20
+ MQTT_MESSAGES: 'mqtt_messages',
21
+ MQTT_SUBSCRIPTIONS: 'mqtt_subscriptions',
22
+
23
+ // Firestore tables
24
+ FIRESTORE_METADATA: 'firestore_metadata',
25
+
26
+ // Webhook tables
27
+ WEBHOOK_ROUTES: 'webhook_routes',
28
+ WEBHOOK_SETTINGS: 'webhook_settings',
29
+
30
+ // HTTP traffic proxy tables
31
+ HTTP_TRAFFIC: 'http_traffic',
32
+
33
+ // Cloud Functions tables
34
+ CLOUD_FUNCTIONS: 'cloud_functions',
35
+ CLOUD_FUNCTION_INVOCATIONS: 'cloud_function_invocations',
36
+
37
+ // System tables
38
+ SCHEMA_MIGRATIONS: 'schema_migrations'
39
+ })
40
+
41
+ // Export individual constants for convenience
42
+ export const {
43
+ PUBSUB_TOPICS,
44
+ PUBSUB_SUBSCRIPTIONS,
45
+ PUBSUB_MESSAGES,
46
+ PUBSUB_MESSAGE_HISTORY,
47
+ PUBSUB_TOPIC_REGISTRY,
48
+ LOGGING_ENTRIES,
49
+ LOGGING_LOGS,
50
+ MQTT_CLIENTS,
51
+ MQTT_MESSAGES,
52
+ MQTT_SUBSCRIPTIONS,
53
+ FIRESTORE_METADATA,
54
+ WEBHOOK_ROUTES,
55
+ WEBHOOK_SETTINGS,
56
+ HTTP_TRAFFIC,
57
+ CLOUD_FUNCTIONS,
58
+ CLOUD_FUNCTION_INVOCATIONS,
59
+ SCHEMA_MIGRATIONS
60
+ } = Tables
61
+
62
+ // Helper to get all table names as array
63
+ export const getAllTables = () => Object.values(Tables)
64
+
65
+ // Helper to check if a table name is valid
66
+ export const isValidTable = (tableName) => getAllTables().includes(tableName)
@@ -0,0 +1,18 @@
1
+ // Firestore metadata schema
2
+
3
+ export const METADATA_SCHEMA = `
4
+ CREATE TABLE IF NOT EXISTS firestore_metadata (
5
+ _id TEXT PRIMARY KEY,
6
+ collection_name TEXT NOT NULL UNIQUE,
7
+ document_count INTEGER DEFAULT 0,
8
+ created_at INTEGER DEFAULT (unixepoch()),
9
+ updated_at INTEGER DEFAULT (unixepoch())
10
+ )
11
+ `
12
+
13
+ export const FIRESTORE_INDEXES = []
14
+
15
+ export const FIRESTORE_SCHEMAS = [
16
+ METADATA_SCHEMA,
17
+ ...FIRESTORE_INDEXES
18
+ ]
@@ -0,0 +1,65 @@
1
+ // Cloud Functions emulator table schemas
2
+
3
+ export const CLOUD_FUNCTIONS_SCHEMA = `
4
+ CREATE TABLE IF NOT EXISTS cloud_functions (
5
+ id TEXT PRIMARY KEY,
6
+ name TEXT NOT NULL UNIQUE,
7
+ source TEXT NOT NULL,
8
+ source_path TEXT,
9
+ entry_point TEXT NOT NULL,
10
+ trigger_type TEXT NOT NULL,
11
+ trigger_config TEXT DEFAULT '{}',
12
+ signature_type TEXT DEFAULT 'cloudevent',
13
+ runtime TEXT DEFAULT 'nodejs20',
14
+ environment_variables TEXT DEFAULT '{}',
15
+ timeout_seconds INTEGER DEFAULT 60,
16
+ description TEXT,
17
+ status TEXT DEFAULT 'stopped',
18
+ port INTEGER,
19
+ pid INTEGER,
20
+ error_message TEXT,
21
+ enabled INTEGER DEFAULT 1,
22
+ invocation_count INTEGER DEFAULT 0,
23
+ last_invoked_at TEXT,
24
+ last_status INTEGER,
25
+ created_at TEXT DEFAULT (datetime('now')),
26
+ updated_at TEXT DEFAULT (datetime('now'))
27
+ )
28
+ `
29
+
30
+ export const CLOUD_FUNCTIONS_INDEXES = `
31
+ CREATE INDEX IF NOT EXISTS idx_cf_trigger_type ON cloud_functions(trigger_type);
32
+ CREATE INDEX IF NOT EXISTS idx_cf_status ON cloud_functions(status)
33
+ `
34
+
35
+ export const CLOUD_FUNCTION_INVOCATIONS_SCHEMA = `
36
+ CREATE TABLE IF NOT EXISTS cloud_function_invocations (
37
+ id TEXT PRIMARY KEY,
38
+ function_id TEXT NOT NULL,
39
+ function_name TEXT NOT NULL,
40
+ trigger_type TEXT NOT NULL,
41
+ trigger_source TEXT,
42
+ cloud_event TEXT,
43
+ response_status INTEGER,
44
+ response_body TEXT,
45
+ response_time_ms INTEGER,
46
+ error TEXT,
47
+ started_at TEXT NOT NULL,
48
+ completed_at TEXT,
49
+ created_at TEXT DEFAULT (datetime('now')),
50
+ FOREIGN KEY (function_id) REFERENCES cloud_functions(id) ON DELETE CASCADE
51
+ )
52
+ `
53
+
54
+ export const CLOUD_FUNCTION_INVOCATIONS_INDEXES = `
55
+ CREATE INDEX IF NOT EXISTS idx_cfi_function_id ON cloud_function_invocations(function_id);
56
+ CREATE INDEX IF NOT EXISTS idx_cfi_started_at ON cloud_function_invocations(started_at);
57
+ CREATE INDEX IF NOT EXISTS idx_cfi_created_at ON cloud_function_invocations(created_at)
58
+ `
59
+
60
+ export const FUNCTIONS_SCHEMAS = [
61
+ CLOUD_FUNCTIONS_SCHEMA,
62
+ CLOUD_FUNCTIONS_INDEXES,
63
+ CLOUD_FUNCTION_INVOCATIONS_SCHEMA,
64
+ CLOUD_FUNCTION_INVOCATIONS_INDEXES
65
+ ]
@@ -0,0 +1,43 @@
1
+ // HTTP traffic proxy table schemas
2
+
3
+ export const HTTP_TRAFFIC_SCHEMA = `
4
+ CREATE TABLE IF NOT EXISTS http_traffic (
5
+ _id TEXT PRIMARY KEY,
6
+ method TEXT NOT NULL,
7
+ target_url TEXT NOT NULL,
8
+ target_host TEXT,
9
+ target_path TEXT,
10
+ query_params TEXT,
11
+ request_headers TEXT,
12
+ request_body TEXT,
13
+ request_cookies TEXT,
14
+ content_type TEXT,
15
+ status_code INTEGER,
16
+ response_headers TEXT,
17
+ response_body TEXT,
18
+ response_content_type TEXT,
19
+ response_time_ms INTEGER,
20
+ started_at TEXT,
21
+ completed_at TEXT,
22
+ source_service TEXT,
23
+ error TEXT,
24
+ mock_response TEXT,
25
+ is_mocked INTEGER DEFAULT 0,
26
+ trace_id TEXT,
27
+ created_at TEXT DEFAULT (datetime('now'))
28
+ )
29
+ `
30
+
31
+ export const HTTP_TRAFFIC_INDEXES = [
32
+ 'CREATE INDEX IF NOT EXISTS idx_http_traffic_target_host ON http_traffic(target_host)',
33
+ 'CREATE INDEX IF NOT EXISTS idx_http_traffic_method ON http_traffic(method)',
34
+ 'CREATE INDEX IF NOT EXISTS idx_http_traffic_status_code ON http_traffic(status_code)',
35
+ 'CREATE INDEX IF NOT EXISTS idx_http_traffic_created_at ON http_traffic(created_at)',
36
+ 'CREATE INDEX IF NOT EXISTS idx_http_traffic_source_service ON http_traffic(source_service)',
37
+ 'CREATE INDEX IF NOT EXISTS idx_http_traffic_trace_id ON http_traffic(trace_id)'
38
+ ]
39
+
40
+ export const HTTP_TRAFFIC_SCHEMAS = [
41
+ HTTP_TRAFFIC_SCHEMA,
42
+ ...HTTP_TRAFFIC_INDEXES
43
+ ]
@@ -0,0 +1,74 @@
1
+ // Logging table schemas and indexes
2
+
3
+ export const LOGS_SCHEMA = `
4
+ CREATE TABLE IF NOT EXISTS logging_logs (
5
+ _id TEXT PRIMARY KEY,
6
+ project_id TEXT NOT NULL,
7
+ log_id TEXT NOT NULL,
8
+ log_name TEXT NOT NULL,
9
+ created_at TEXT,
10
+ updated_at TEXT,
11
+ UNIQUE(project_id, log_id)
12
+ )
13
+ `
14
+
15
+ export const ENTRIES_SCHEMA = `
16
+ CREATE TABLE IF NOT EXISTS logging_entries (
17
+ _id TEXT PRIMARY KEY,
18
+ log_name TEXT NOT NULL,
19
+ service_name TEXT,
20
+ resource TEXT,
21
+ labels TEXT,
22
+ text_payload TEXT,
23
+ json_payload TEXT,
24
+ proto_payload TEXT,
25
+ severity TEXT,
26
+ insert_id TEXT,
27
+ timestamp TEXT,
28
+ receive_timestamp TEXT,
29
+ trace TEXT,
30
+ span_id TEXT,
31
+ trace_sampled INTEGER,
32
+ http_request TEXT,
33
+ operation TEXT,
34
+ source_location TEXT,
35
+ source TEXT,
36
+ level TEXT,
37
+ error_message TEXT,
38
+ stack_trace TEXT,
39
+ event TEXT,
40
+ duration REAL,
41
+ error_code TEXT,
42
+ error_status INTEGER,
43
+ error_reason TEXT,
44
+ error_resource TEXT,
45
+ property_id TEXT,
46
+ device_id TEXT,
47
+ door_id TEXT,
48
+ subscription_name TEXT,
49
+ message_id TEXT,
50
+ created_at TEXT
51
+ )
52
+ `
53
+
54
+ export const LOGGING_INDEXES = [
55
+ 'CREATE INDEX IF NOT EXISTS idx_entries_log_name ON logging_entries(log_name)',
56
+ 'CREATE INDEX IF NOT EXISTS idx_entries_service_name ON logging_entries(service_name)',
57
+ 'CREATE INDEX IF NOT EXISTS idx_entries_severity ON logging_entries(severity)',
58
+ 'CREATE INDEX IF NOT EXISTS idx_entries_timestamp ON logging_entries(timestamp)',
59
+ 'CREATE INDEX IF NOT EXISTS idx_entries_trace ON logging_entries(trace)',
60
+ 'CREATE INDEX IF NOT EXISTS idx_entries_source ON logging_entries(source)',
61
+ 'CREATE INDEX IF NOT EXISTS idx_entries_event ON logging_entries(event)',
62
+ 'CREATE INDEX IF NOT EXISTS idx_entries_property_id ON logging_entries(property_id)',
63
+ 'CREATE INDEX IF NOT EXISTS idx_entries_device_id ON logging_entries(device_id)',
64
+ 'CREATE INDEX IF NOT EXISTS idx_logs_project_id ON logging_logs(project_id)',
65
+ // Dedupe index: equality on text_payload + service_name, range on timestamp
66
+ 'CREATE INDEX IF NOT EXISTS idx_dedupe_logs_v2 ON logging_entries(text_payload, service_name, timestamp)',
67
+ 'CREATE INDEX IF NOT EXISTS idx_entries_created_at ON logging_entries(created_at)'
68
+ ]
69
+
70
+ export const LOGGING_SCHEMAS = [
71
+ LOGS_SCHEMA,
72
+ ENTRIES_SCHEMA,
73
+ ...LOGGING_INDEXES
74
+ ]
@@ -0,0 +1,64 @@
1
+ // Schema version tracking
2
+
3
+ export const MIGRATIONS_SCHEMA = `
4
+ CREATE TABLE IF NOT EXISTS schema_migrations (
5
+ version INTEGER PRIMARY KEY,
6
+ name TEXT NOT NULL,
7
+ applied_at INTEGER DEFAULT (unixepoch())
8
+ )
9
+ `
10
+
11
+ export const CURRENT_SCHEMA_VERSION = 10
12
+
13
+ export const MIGRATIONS = [
14
+ {
15
+ version: 1,
16
+ name: 'initial_schema',
17
+ description: 'Create initial tables for pubsub, logging, mqtt, and firestore'
18
+ },
19
+ {
20
+ version: 2,
21
+ name: 'logging_schema_update',
22
+ description: 'Add missing columns to logging_entries (span_id, trace_sampled, proto_payload, http_request, operation, source_location)'
23
+ },
24
+ {
25
+ version: 3,
26
+ name: 'add_service_name',
27
+ description: 'Add service_name column to logging_entries for efficient service filtering'
28
+ },
29
+ {
30
+ version: 4,
31
+ name: 'log_parsing_normalisation',
32
+ description: 'Add source, level, error_message columns and improved dedupe index'
33
+ },
34
+ {
35
+ version: 5,
36
+ name: 'webhook_proxy',
37
+ description: 'Add webhook_routes and webhook_settings tables for webhook proxy feature'
38
+ },
39
+ {
40
+ version: 6,
41
+ name: 'logging_nested_fields',
42
+ description: 'Add extracted nested log fields (stack_trace, event, duration, error_*, property_id, device_id, door_id, subscription_name, message_id)'
43
+ },
44
+ {
45
+ version: 7,
46
+ name: 'http_traffic',
47
+ description: 'Add http_traffic table for HTTP proxy request/response recording'
48
+ },
49
+ {
50
+ version: 8,
51
+ name: 'pubsub_topic_registry',
52
+ description: 'Add pubsub_topic_registry table for persisting topic/subscription declarations across emulator restarts'
53
+ },
54
+ {
55
+ version: 9,
56
+ name: 'cloud_functions',
57
+ description: 'Add cloud_functions and cloud_function_invocations tables for Cloud Functions emulator'
58
+ },
59
+ {
60
+ version: 10,
61
+ name: 'db_maintenance_indexes',
62
+ description: 'Add created_at indexes on unbounded tables for efficient retention cleanup'
63
+ }
64
+ ]
@@ -0,0 +1,56 @@
1
+ // MQTT table schemas and indexes
2
+
3
+ export const CLIENTS_SCHEMA = `
4
+ CREATE TABLE IF NOT EXISTS mqtt_clients (
5
+ client_id TEXT PRIMARY KEY,
6
+ connected_at TEXT,
7
+ disconnected_at TEXT,
8
+ will TEXT,
9
+ clean INTEGER,
10
+ version INTEGER,
11
+ created_at INTEGER DEFAULT (unixepoch())
12
+ )
13
+ `
14
+
15
+ export const MESSAGES_SCHEMA = `
16
+ CREATE TABLE IF NOT EXISTS mqtt_messages (
17
+ id TEXT PRIMARY KEY,
18
+ topic TEXT NOT NULL,
19
+ payload TEXT,
20
+ qos INTEGER,
21
+ retain INTEGER,
22
+ timestamp TEXT,
23
+ client_id TEXT,
24
+ created_at INTEGER DEFAULT (unixepoch()),
25
+ FOREIGN KEY (client_id) REFERENCES mqtt_clients(client_id) ON DELETE SET NULL
26
+ )
27
+ `
28
+
29
+ export const SUBSCRIPTIONS_SCHEMA = `
30
+ CREATE TABLE IF NOT EXISTS mqtt_subscriptions (
31
+ id TEXT PRIMARY KEY,
32
+ client_id TEXT NOT NULL,
33
+ topic TEXT NOT NULL,
34
+ qos INTEGER,
35
+ timestamp TEXT,
36
+ created_at INTEGER DEFAULT (unixepoch()),
37
+ FOREIGN KEY (client_id) REFERENCES mqtt_clients(client_id) ON DELETE CASCADE,
38
+ UNIQUE(client_id, topic)
39
+ )
40
+ `
41
+
42
+ export const MQTT_INDEXES = [
43
+ 'CREATE INDEX IF NOT EXISTS idx_messages_topic ON mqtt_messages(topic)',
44
+ 'CREATE INDEX IF NOT EXISTS idx_messages_client_id ON mqtt_messages(client_id)',
45
+ 'CREATE INDEX IF NOT EXISTS idx_messages_timestamp ON mqtt_messages(timestamp)',
46
+ 'CREATE INDEX IF NOT EXISTS idx_subscriptions_client_id ON mqtt_subscriptions(client_id)',
47
+ 'CREATE INDEX IF NOT EXISTS idx_subscriptions_topic ON mqtt_subscriptions(topic)',
48
+ 'CREATE INDEX IF NOT EXISTS idx_mqtt_messages_created_at ON mqtt_messages(created_at)'
49
+ ]
50
+
51
+ export const MQTT_SCHEMAS = [
52
+ CLIENTS_SCHEMA,
53
+ MESSAGES_SCHEMA,
54
+ SUBSCRIPTIONS_SCHEMA,
55
+ ...MQTT_INDEXES
56
+ ]
@@ -0,0 +1,90 @@
1
+ // Pub/Sub table schemas and indexes
2
+
3
+ export const TOPICS_SCHEMA = `
4
+ CREATE TABLE IF NOT EXISTS pubsub_topics (
5
+ name TEXT PRIMARY KEY,
6
+ labels TEXT,
7
+ message_storage_policy TEXT,
8
+ kms_key_name TEXT,
9
+ schema_settings TEXT,
10
+ satisfies_pzs INTEGER,
11
+ message_retention_duration TEXT,
12
+ created_at INTEGER DEFAULT (unixepoch())
13
+ )
14
+ `
15
+
16
+ export const SUBSCRIPTIONS_SCHEMA = `
17
+ CREATE TABLE IF NOT EXISTS pubsub_subscriptions (
18
+ name TEXT PRIMARY KEY,
19
+ topic TEXT NOT NULL,
20
+ push_config TEXT,
21
+ ack_deadline_seconds INTEGER,
22
+ retain_acked_messages INTEGER,
23
+ message_retention_duration TEXT,
24
+ labels TEXT,
25
+ enable_message_ordering INTEGER,
26
+ expiration_policy TEXT,
27
+ filter TEXT,
28
+ dead_letter_policy TEXT,
29
+ retry_policy TEXT,
30
+ detached INTEGER,
31
+ enable_exactly_once_delivery INTEGER,
32
+ topic_message_retention_duration TEXT,
33
+ state TEXT,
34
+ created_at INTEGER DEFAULT (unixepoch()),
35
+ FOREIGN KEY (topic) REFERENCES pubsub_topics(name) ON DELETE CASCADE
36
+ )
37
+ `
38
+
39
+ export const MESSAGES_SCHEMA = `
40
+ CREATE TABLE IF NOT EXISTS pubsub_messages (
41
+ id TEXT PRIMARY KEY,
42
+ data TEXT,
43
+ attributes TEXT,
44
+ ordering_key TEXT,
45
+ publish_time TEXT,
46
+ topic TEXT NOT NULL,
47
+ ack_id TEXT,
48
+ delivery_attempt INTEGER DEFAULT 0,
49
+ last_delivery_time TEXT,
50
+ created_at INTEGER DEFAULT (unixepoch()),
51
+ FOREIGN KEY (topic) REFERENCES pubsub_topics(name) ON DELETE CASCADE
52
+ )
53
+ `
54
+
55
+ export const MESSAGE_HISTORY_SCHEMA = `
56
+ CREATE TABLE IF NOT EXISTS pubsub_message_history (
57
+ id TEXT PRIMARY KEY,
58
+ message_id TEXT NOT NULL,
59
+ data TEXT,
60
+ attributes TEXT,
61
+ ordering_key TEXT,
62
+ publish_time TEXT NOT NULL,
63
+ topic TEXT NOT NULL,
64
+ sender TEXT,
65
+ view_count INTEGER DEFAULT 0,
66
+ last_viewed_at INTEGER,
67
+ created_at INTEGER DEFAULT (unixepoch()),
68
+ FOREIGN KEY (topic) REFERENCES pubsub_topics(name) ON DELETE CASCADE
69
+ )
70
+ `
71
+
72
+ export const PUBSUB_INDEXES = [
73
+ 'CREATE INDEX IF NOT EXISTS idx_subscriptions_topic ON pubsub_subscriptions(topic)',
74
+ 'CREATE INDEX IF NOT EXISTS idx_messages_topic ON pubsub_messages(topic)',
75
+ 'CREATE INDEX IF NOT EXISTS idx_messages_ack_id ON pubsub_messages(ack_id)',
76
+ 'CREATE INDEX IF NOT EXISTS idx_history_topic ON pubsub_message_history(topic)',
77
+ 'CREATE INDEX IF NOT EXISTS idx_history_publish_time ON pubsub_message_history(publish_time)',
78
+ 'CREATE INDEX IF NOT EXISTS idx_history_sender ON pubsub_message_history(sender)',
79
+ 'CREATE INDEX IF NOT EXISTS idx_history_view_count ON pubsub_message_history(view_count)',
80
+ 'CREATE INDEX IF NOT EXISTS idx_history_last_viewed ON pubsub_message_history(last_viewed_at)',
81
+ 'CREATE INDEX IF NOT EXISTS idx_messages_created_at ON pubsub_messages(created_at)'
82
+ ]
83
+
84
+ export const PUBSUB_SCHEMAS = [
85
+ TOPICS_SCHEMA,
86
+ SUBSCRIPTIONS_SCHEMA,
87
+ MESSAGES_SCHEMA,
88
+ MESSAGE_HISTORY_SCHEMA,
89
+ ...PUBSUB_INDEXES
90
+ ]
@@ -0,0 +1,22 @@
1
+ // Pub/Sub topic registry schema — persists topic+subscription declarations across emulator restarts
2
+
3
+ export const PUBSUB_TOPIC_REGISTRY_SCHEMA = `
4
+ CREATE TABLE IF NOT EXISTS pubsub_topic_registry (
5
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
6
+ topic_name TEXT NOT NULL,
7
+ subscription_name TEXT,
8
+ project_name TEXT NOT NULL,
9
+ registered_at INTEGER DEFAULT (unixepoch()),
10
+ UNIQUE(topic_name, subscription_name, project_name)
11
+ )
12
+ `
13
+
14
+ export const PUBSUB_TOPIC_REGISTRY_INDEXES = [
15
+ 'CREATE INDEX IF NOT EXISTS idx_registry_topic ON pubsub_topic_registry(topic_name)',
16
+ 'CREATE INDEX IF NOT EXISTS idx_registry_project ON pubsub_topic_registry(project_name)'
17
+ ]
18
+
19
+ export const PUBSUB_TOPIC_REGISTRY_SCHEMAS = [
20
+ PUBSUB_TOPIC_REGISTRY_SCHEMA,
21
+ ...PUBSUB_TOPIC_REGISTRY_INDEXES
22
+ ]
@@ -0,0 +1,28 @@
1
+ // Webhook proxy table schemas
2
+
3
+ export const WEBHOOK_ROUTES_SCHEMA = `
4
+ CREATE TABLE IF NOT EXISTS webhook_routes (
5
+ prefix TEXT PRIMARY KEY,
6
+ target TEXT NOT NULL,
7
+ path_rewrite TEXT,
8
+ description TEXT,
9
+ enabled INTEGER DEFAULT 1,
10
+ request_count INTEGER DEFAULT 0,
11
+ last_request_at INTEGER,
12
+ created_at INTEGER DEFAULT (unixepoch()),
13
+ updated_at INTEGER DEFAULT (unixepoch())
14
+ )
15
+ `
16
+
17
+ export const WEBHOOK_SETTINGS_SCHEMA = `
18
+ CREATE TABLE IF NOT EXISTS webhook_settings (
19
+ key TEXT PRIMARY KEY,
20
+ value TEXT,
21
+ updated_at INTEGER DEFAULT (unixepoch())
22
+ )
23
+ `
24
+
25
+ export const WEBHOOK_SCHEMAS = [
26
+ WEBHOOK_ROUTES_SCHEMA,
27
+ WEBHOOK_SETTINGS_SCHEMA
28
+ ]
@@ -0,0 +1,91 @@
1
+ import { publishToTopic } from './Logic.js'
2
+ import { Logger } from '../../singletons/Logger.js'
3
+
4
+ /**
5
+ * POST /topics/:topicName?qos=1
6
+ * Mimics AWS IoT Core HTTPS publishing API
7
+ *
8
+ * Accepts:
9
+ * - Binary payload (Buffer) in request body
10
+ * - QoS level in query parameter (optional, defaults to 1)
11
+ * - Authorization header (present but not validated in dev mode)
12
+ *
13
+ * Returns:
14
+ * - Empty 200 response (matching AWS IoT Core behavior)
15
+ */
16
+ export const publish = async (ctx) => {
17
+ const { topicName } = ctx.params
18
+ const qos = parseInt(ctx.query.qos || '1')
19
+ const payload = ctx.request.body
20
+
21
+ try {
22
+ // Validate topic name
23
+ if (!topicName) {
24
+ ctx.status = 400
25
+ ctx.body = {
26
+ error: {
27
+ code: 400,
28
+ message: 'Topic name is required',
29
+ status: 'INVALID_ARGUMENT'
30
+ }
31
+ }
32
+ return
33
+ }
34
+
35
+ // Validate payload
36
+ if (!Buffer.isBuffer(payload)) {
37
+ ctx.status = 400
38
+ ctx.body = {
39
+ error: {
40
+ code: 400,
41
+ message: 'Payload must be binary (Buffer)',
42
+ status: 'INVALID_ARGUMENT'
43
+ }
44
+ }
45
+ return
46
+ }
47
+
48
+ // Validate QoS
49
+ if (qos !== 0 && qos !== 1) {
50
+ ctx.status = 400
51
+ ctx.body = {
52
+ error: {
53
+ code: 400,
54
+ message: 'QoS must be 0 or 1',
55
+ status: 'INVALID_ARGUMENT'
56
+ }
57
+ }
58
+ return
59
+ }
60
+
61
+ // Publish to MQTT broker
62
+ await publishToTopic({
63
+ topicName,
64
+ payload,
65
+ qos
66
+ })
67
+
68
+ // AWS IoT Core returns empty 200 response
69
+ ctx.status = 200
70
+ ctx.body = ''
71
+ } catch (error) {
72
+ Logger.log({
73
+ level: 'error',
74
+ message: 'AWS IoT HTTPS API: Controller error',
75
+ data: {
76
+ topic: topicName,
77
+ error: error.message,
78
+ stack: error.stack
79
+ }
80
+ })
81
+
82
+ ctx.status = 500
83
+ ctx.body = {
84
+ error: {
85
+ code: 500,
86
+ message: error.message || 'Internal server error',
87
+ status: 'INTERNAL'
88
+ }
89
+ }
90
+ }
91
+ }
@@ -0,0 +1,70 @@
1
+ import { Broker } from '../../protocols/mqtt/Broker.js'
2
+ import { Logger } from '../../singletons/Logger.js'
3
+
4
+ /**
5
+ * Publish message to MQTT broker (mimics AWS IoT Core HTTPS API)
6
+ *
7
+ * AWS IoT Core HTTPS API: POST /topics/{topicName}?qos=1
8
+ * Body: Binary packet (Buffer)
9
+ * Response: Empty 200 response
10
+ *
11
+ * @param {Object} params
12
+ * @param {string} params.topicName - MQTT topic name
13
+ * @param {Buffer} params.payload - Binary message payload
14
+ * @param {number} params.qos - Quality of Service (0 or 1)
15
+ * @returns {Promise<Object>} Success status
16
+ */
17
+ export const publishToTopic = async ({ topicName, payload, qos = 1 }) => {
18
+ try {
19
+ // Get MQTT broker instance
20
+ const broker = Broker.getInstance()
21
+
22
+ // Publish to MQTT broker
23
+ // Note: We're publishing as the broker itself (no client), so client will be null in handlers
24
+ broker.publish({
25
+ cmd: 'publish',
26
+ topic: topicName,
27
+ payload,
28
+ qos,
29
+ retain: false
30
+ }, (err) => {
31
+ if (err) {
32
+ Logger.log({
33
+ level: 'error',
34
+ message: 'Failed to publish to MQTT broker',
35
+ data: {
36
+ topic: topicName,
37
+ qos,
38
+ error: err.message
39
+ }
40
+ })
41
+ throw err
42
+ }
43
+ })
44
+
45
+ Logger.log({
46
+ level: 'debug',
47
+ message: 'AWS IoT HTTPS API: Published to MQTT broker',
48
+ data: {
49
+ topic: topicName,
50
+ qos,
51
+ payloadSize: payload.length
52
+ }
53
+ })
54
+
55
+ return {
56
+ success: true
57
+ }
58
+ } catch (error) {
59
+ Logger.log({
60
+ level: 'error',
61
+ message: 'AWS IoT HTTPS API: Publish failed',
62
+ data: {
63
+ topic: topicName,
64
+ error: error.message,
65
+ stack: error.stack
66
+ }
67
+ })
68
+ throw error
69
+ }
70
+ }