@o1-labs/mina-archive-node-graphql 0.0.6 → 2.0.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 (176) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +92 -268
  3. package/build/src/blockchain/utils.js.map +1 -1
  4. package/build/src/config.js +86 -0
  5. package/build/src/config.js.map +1 -0
  6. package/build/src/db/archive-node-adapter/archive-node-adapter.js +29 -2
  7. package/build/src/db/archive-node-adapter/archive-node-adapter.js.map +1 -1
  8. package/build/src/db/archive-node-adapter/postgres-options.js +35 -0
  9. package/build/src/db/archive-node-adapter/postgres-options.js.map +1 -0
  10. package/build/src/db/sql/best-chain.js +5 -0
  11. package/build/src/db/sql/best-chain.js.map +1 -0
  12. package/build/src/db/sql/events-actions/queries.js +102 -21
  13. package/build/src/db/sql/events-actions/queries.js.map +1 -1
  14. package/build/src/db/sql/verification-key-updates/queries.js +100 -0
  15. package/build/src/db/sql/verification-key-updates/queries.js.map +1 -0
  16. package/build/src/db/sql/zkapp-commands/queries.js +363 -0
  17. package/build/src/db/sql/zkapp-commands/queries.js.map +1 -0
  18. package/build/src/db/sql/zkapp-commands/types.js +2 -0
  19. package/build/src/db/sql/zkapp-commands/types.js.map +1 -0
  20. package/build/src/errors/error.js +4 -1
  21. package/build/src/errors/error.js.map +1 -1
  22. package/build/src/index.js +44 -7
  23. package/build/src/index.js.map +1 -1
  24. package/build/src/resolvers.js +42 -23
  25. package/build/src/resolvers.js.map +1 -1
  26. package/build/src/schema-version.js +2 -0
  27. package/build/src/schema-version.js.map +1 -0
  28. package/build/src/server/cors.js +41 -0
  29. package/build/src/server/cors.js.map +1 -0
  30. package/build/src/server/graceful-shutdown.js +42 -0
  31. package/build/src/server/graceful-shutdown.js.map +1 -0
  32. package/build/src/server/graphql-armor.js +39 -0
  33. package/build/src/server/graphql-armor.js.map +1 -0
  34. package/build/src/server/logger.js +45 -0
  35. package/build/src/server/logger.js.map +1 -0
  36. package/build/src/server/metrics.js +72 -0
  37. package/build/src/server/metrics.js.map +1 -0
  38. package/build/src/server/plugins.js +31 -15
  39. package/build/src/server/plugins.js.map +1 -1
  40. package/build/src/server/rate-limit.js +143 -0
  41. package/build/src/server/rate-limit.js.map +1 -0
  42. package/build/src/server/readiness.js +45 -0
  43. package/build/src/server/readiness.js.map +1 -0
  44. package/build/src/server/request-logging.js +58 -0
  45. package/build/src/server/request-logging.js.map +1 -0
  46. package/build/src/server/server.js +38 -13
  47. package/build/src/server/server.js.map +1 -1
  48. package/build/src/services/actions-service/actions-service.js +26 -15
  49. package/build/src/services/actions-service/actions-service.js.map +1 -1
  50. package/build/src/services/blocks-service/blocks-service.js +10 -9
  51. package/build/src/services/blocks-service/blocks-service.js.map +1 -1
  52. package/build/src/services/network-service/network-service.js +17 -9
  53. package/build/src/services/network-service/network-service.js.map +1 -1
  54. package/build/src/services/verification-key-updates-service/verification-key-updates-service.interface.js +2 -0
  55. package/build/src/services/verification-key-updates-service/verification-key-updates-service.interface.js.map +1 -0
  56. package/build/src/services/verification-key-updates-service/verification-key-updates-service.js +41 -0
  57. package/build/src/services/verification-key-updates-service/verification-key-updates-service.js.map +1 -0
  58. package/build/src/services/zkapp-commands-service/zkapp-commands-service.interface.js +2 -0
  59. package/build/src/services/zkapp-commands-service/zkapp-commands-service.interface.js.map +1 -0
  60. package/build/src/services/zkapp-commands-service/zkapp-commands-service.js +115 -0
  61. package/build/src/services/zkapp-commands-service/zkapp-commands-service.js.map +1 -0
  62. package/build/src/tracing/jaeger-tracing.js +2 -1
  63. package/build/src/tracing/jaeger-tracing.js.map +1 -1
  64. package/build/tests/devnet-dump/action-state-invariants.js +165 -0
  65. package/build/tests/devnet-dump/action-state-invariants.js.map +1 -0
  66. package/build/tests/devnet-dump/devnet-dump.test.js +24 -10
  67. package/build/tests/devnet-dump/devnet-dump.test.js.map +1 -1
  68. package/build/tests/devnet-dump/setup.js +63 -17
  69. package/build/tests/devnet-dump/setup.js.map +1 -1
  70. package/build/tests/integration/action-state-ordering.test.js +139 -0
  71. package/build/tests/integration/action-state-ordering.test.js.map +1 -0
  72. package/build/tests/integration/action-state-setup.js +48 -0
  73. package/build/tests/integration/action-state-setup.js.map +1 -0
  74. package/build/tests/integration/consensus-vs-chain.test.js +81 -0
  75. package/build/tests/integration/consensus-vs-chain.test.js.map +1 -0
  76. package/build/tests/integration/integration.test.js +166 -2
  77. package/build/tests/integration/integration.test.js.map +1 -1
  78. package/build/tests/integration/postgres-timeout.test.js +92 -0
  79. package/build/tests/integration/postgres-timeout.test.js.map +1 -0
  80. package/build/tests/integration/setup.js +58 -1
  81. package/build/tests/integration/setup.js.map +1 -1
  82. package/build/tests/integration/verification-key-setup.js +49 -0
  83. package/build/tests/integration/verification-key-setup.js.map +1 -0
  84. package/build/tests/integration/verification-key-updates.test.js +159 -0
  85. package/build/tests/integration/verification-key-updates.test.js.map +1 -0
  86. package/build/tests/live-api/events.test.js +11 -7
  87. package/build/tests/live-api/events.test.js.map +1 -1
  88. package/build/tests/live-api/networkState.test.js +1 -0
  89. package/build/tests/live-api/networkState.test.js.map +1 -1
  90. package/build/tests/live-network/live-network.test.js +1 -0
  91. package/build/tests/live-network/live-network.test.js.map +1 -1
  92. package/build/tests/resolvers.test.js +65 -37
  93. package/build/tests/resolvers.test.js.map +1 -1
  94. package/build/tests/services/verification-key-updates-service/verification-key-updates-service.test.js +35 -0
  95. package/build/tests/services/verification-key-updates-service/verification-key-updates-service.test.js.map +1 -0
  96. package/build/tests/services/zkapp-commands-service/zkapp-commands-service.test.js +159 -0
  97. package/build/tests/services/zkapp-commands-service/zkapp-commands-service.test.js.map +1 -0
  98. package/build/tests/test-helpers.js +15 -0
  99. package/build/tests/test-helpers.js.map +1 -1
  100. package/build/tests/unit/best-chain-anchor.test.js +70 -0
  101. package/build/tests/unit/best-chain-anchor.test.js.map +1 -0
  102. package/build/tests/unit/config.test.js +106 -0
  103. package/build/tests/unit/config.test.js.map +1 -0
  104. package/build/tests/unit/cors.test.js +95 -0
  105. package/build/tests/unit/cors.test.js.map +1 -0
  106. package/build/tests/unit/error-masking.test.js +119 -0
  107. package/build/tests/unit/error-masking.test.js.map +1 -0
  108. package/build/tests/unit/graceful-shutdown.test.js +99 -0
  109. package/build/tests/unit/graceful-shutdown.test.js.map +1 -0
  110. package/build/tests/unit/graphql-armor.test.js +121 -0
  111. package/build/tests/unit/graphql-armor.test.js.map +1 -0
  112. package/build/tests/unit/logging.test.js +120 -0
  113. package/build/tests/unit/logging.test.js.map +1 -0
  114. package/build/tests/unit/metrics.test.js +114 -0
  115. package/build/tests/unit/metrics.test.js.map +1 -0
  116. package/build/tests/unit/network-service.test.js +66 -0
  117. package/build/tests/unit/network-service.test.js.map +1 -0
  118. package/build/tests/unit/postgres-options.test.js +56 -0
  119. package/build/tests/unit/postgres-options.test.js.map +1 -0
  120. package/build/tests/unit/rate-limit.test.js +271 -0
  121. package/build/tests/unit/rate-limit.test.js.map +1 -0
  122. package/build/tests/unit/readiness.test.js +75 -0
  123. package/build/tests/unit/readiness.test.js.map +1 -0
  124. package/build/tests/unit/schema-nullability.test.js +40 -0
  125. package/build/tests/unit/schema-nullability.test.js.map +1 -0
  126. package/build/tests/unit/schema-version.test.js +48 -0
  127. package/build/tests/unit/schema-version.test.js.map +1 -0
  128. package/build/tests/unit/yoga-http-contract.test.js +30 -0
  129. package/build/tests/unit/yoga-http-contract.test.js.map +1 -0
  130. package/build/tests/unit/zkapp-commands-gating.test.js +38 -0
  131. package/build/tests/unit/zkapp-commands-gating.test.js.map +1 -0
  132. package/build/zkapp/utils.js +2 -0
  133. package/build/zkapp/utils.js.map +1 -1
  134. package/package.json +33 -14
  135. package/schema.graphql +126 -8
  136. package/src/blockchain/types.ts +65 -1
  137. package/src/blockchain/utils.ts +26 -2
  138. package/src/config.ts +133 -0
  139. package/src/db/archive-node-adapter/archive-node-adapter.interface.ts +26 -3
  140. package/src/db/archive-node-adapter/archive-node-adapter.ts +65 -2
  141. package/src/db/archive-node-adapter/postgres-options.ts +90 -0
  142. package/src/db/sql/best-chain.ts +34 -0
  143. package/src/db/sql/events-actions/queries.ts +168 -24
  144. package/src/db/sql/verification-key-updates/queries.ts +132 -0
  145. package/src/db/sql/zkapp-commands/queries.ts +406 -0
  146. package/src/db/sql/zkapp-commands/types.ts +43 -0
  147. package/src/envionment.d.ts +20 -0
  148. package/src/errors/error.ts +17 -1
  149. package/src/index.ts +64 -7
  150. package/src/resolvers-types.ts +311 -24
  151. package/src/resolvers.ts +70 -34
  152. package/src/schema-version.ts +24 -0
  153. package/src/server/cors.ts +97 -0
  154. package/src/server/graceful-shutdown.ts +76 -0
  155. package/src/server/graphql-armor.ts +78 -0
  156. package/src/server/logger.ts +72 -0
  157. package/src/server/metrics.ts +104 -0
  158. package/src/server/plugins.ts +53 -19
  159. package/src/server/rate-limit.ts +274 -0
  160. package/src/server/readiness.ts +78 -0
  161. package/src/server/request-logging.ts +83 -0
  162. package/src/server/server.ts +70 -15
  163. package/src/services/actions-service/actions-service.ts +87 -24
  164. package/src/services/blocks-service/blocks-service.ts +16 -14
  165. package/src/services/network-service/network-service.ts +30 -12
  166. package/src/services/verification-key-updates-service/verification-key-updates-service.interface.ts +9 -0
  167. package/src/services/verification-key-updates-service/verification-key-updates-service.ts +72 -0
  168. package/src/services/zkapp-commands-service/zkapp-commands-service.interface.ts +9 -0
  169. package/src/services/zkapp-commands-service/zkapp-commands-service.ts +181 -0
  170. package/src/tracing/jaeger-tracing.ts +2 -1
  171. package/build/tests/consensus/run-compare.js +0 -40
  172. package/build/tests/consensus/run-compare.js.map +0 -1
  173. package/build/tests/consensus/select-consensus-precomputed.js +0 -80
  174. package/build/tests/consensus/select-consensus-precomputed.js.map +0 -1
  175. package/build/tests/consensus/types.js +0 -15
  176. package/build/tests/consensus/types.js.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@o1-labs/mina-archive-node-graphql",
3
- "version": "0.0.6",
3
+ "version": "2.0.0",
4
4
  "description": "A NodeJS GraphQL server for exposing Mina Protocol archive node data for o1js/zkApps",
5
5
  "repository": {
6
6
  "type": "git",
@@ -24,9 +24,10 @@
24
24
  "start": "node build/src/index.js",
25
25
  "dev": "cross-env NODE_NO_WARNINGS=1 npx nodemon --exec 'node -r dotenv/config --loader ts-node/esm' src/index.ts",
26
26
  "test": "./run-tests.sh",
27
- "test:unit": "npm run build && for f in ./build/tests/unit/*test.js ./build/tests/utils.test.js ./build/tests/services/*/*.test.js; do node --enable-source-maps --test \"$f\"; done",
28
- "test:coverage": "npm run build && npx c8 --include 'build/src/**' --reporter text --reporter lcov -- bash -c 'for f in ./build/tests/unit/*test.js ./build/tests/utils.test.js ./build/tests/services/*/*.test.js; do node --enable-source-maps --test \"$f\"; done'",
29
- "test:integration": "npm run build && node --enable-source-maps --test ./build/tests/integration/integration.test.js",
27
+ "test:unit": "npm run build && (ec=0; for f in ./build/tests/unit/*test.js ./build/tests/utils.test.js ./build/tests/services/*/*.test.js; do node --enable-source-maps --test \"$f\" || ec=$?; done; exit $ec)",
28
+ "test:coverage": "npm run build && npx c8 --include 'build/src/**' --check-coverage --lines 50 --functions 50 --statements 50 --branches 80 --reporter text --reporter lcov -- bash -c 'ec=0; for f in ./build/tests/unit/*test.js ./build/tests/utils.test.js ./build/tests/services/*/*.test.js; do node --enable-source-maps --test \"$f\" || ec=$?; done; exit $ec'",
29
+ "test:integration": "npm run build && (ec=0; for f in ./build/tests/integration/*.test.js; do node --enable-source-maps --test \"$f\" || ec=$?; done; exit $ec)",
30
+ "test:live-api": "npm run build && node --enable-source-maps --test ./build/tests/live-api/*.test.js",
30
31
  "test:live-network": "npm run build && node --enable-source-maps --test-timeout 600000 --test ./build/tests/live-network/live-network.test.js",
31
32
  "test:devnet-dump": "npm run build && node --enable-source-maps --test-timeout 900000 --test ./build/tests/devnet-dump/devnet-dump.test.js",
32
33
  "clean": "rimraf ./build",
@@ -38,15 +39,26 @@
38
39
  "compose": "docker compose up",
39
40
  "prepublishOnly": "npm run build"
40
41
  },
41
- "keywords": ["mina", "mina-protocol", "archive-node", "graphql", "o1js", "zkapp", "blockchain"],
42
+ "keywords": [
43
+ "mina",
44
+ "mina-protocol",
45
+ "archive-node",
46
+ "graphql",
47
+ "o1js",
48
+ "zkapp",
49
+ "blockchain"
50
+ ],
42
51
  "author": "O(1) Labs <build@o1labs.org>",
43
- "license": "ISC",
52
+ "license": "Apache-2.0",
53
+ "engines": {
54
+ "node": ">=22.12.0"
55
+ },
44
56
  "devDependencies": {
45
57
  "@graphql-codegen/cli": "^6.0.1",
46
58
  "@graphql-codegen/typescript": "^5.0.2",
47
59
  "@graphql-codegen/typescript-resolvers": "^5.0.1",
48
60
  "@graphql-eslint/eslint-plugin": "^4.4.0",
49
- "@types/node": "^20.5.7",
61
+ "@types/node": "^22.20.1",
50
62
  "@typescript-eslint/eslint-plugin": "^6.5.0",
51
63
  "@typescript-eslint/parser": "^6.5.0",
52
64
  "artillery": "^2.0.0-36",
@@ -62,10 +74,15 @@
62
74
  "typescript": "^5.2.2"
63
75
  },
64
76
  "dependencies": {
65
- "@envelop/core": "^4.0.0",
66
- "@envelop/disable-introspection": "^5.0.0",
67
- "@envelop/graphql-jit": "^6.0.1",
68
- "@envelop/opentelemetry": "^5.0.0",
77
+ "@envelop/core": "^5.5.1",
78
+ "@envelop/disable-introspection": "^9.1.1",
79
+ "@envelop/graphql-jit": "^11.1.1",
80
+ "@envelop/opentelemetry": "^9.1.1",
81
+ "@escape.tech/graphql-armor-block-field-suggestions": "^3.0.1",
82
+ "@escape.tech/graphql-armor-cost-limit": "^2.4.3",
83
+ "@escape.tech/graphql-armor-max-aliases": "^2.6.2",
84
+ "@escape.tech/graphql-armor-max-depth": "^2.4.2",
85
+ "@escape.tech/graphql-armor-max-tokens": "^2.5.1",
69
86
  "@graphql-tools/executor-http": "^3.0.4",
70
87
  "@graphql-tools/graphql-file-loader": "^8.1.2",
71
88
  "@graphql-tools/load": "^8.1.2",
@@ -76,10 +93,12 @@
76
93
  "blakejs": "^1.2.1",
77
94
  "dotenv": "^16.3.1",
78
95
  "graphql": "^16.8.0",
79
- "graphql-yoga": "4.0.4",
80
- "postgres": "^3.3.5"
96
+ "graphql-yoga": "5.21.2",
97
+ "pino": "^10.3.1",
98
+ "postgres": "^3.3.5",
99
+ "prom-client": "^15.1.3"
81
100
  },
82
101
  "volta": {
83
- "node": "20.18.0"
102
+ "node": "22.12.0"
84
103
  }
85
104
  }
package/schema.graphql CHANGED
@@ -66,16 +66,54 @@ input ActionFilterOptionsInput {
66
66
  endActionState: String
67
67
  }
68
68
 
69
+ """
70
+ Find applied account updates that set a specific verification key.
71
+
72
+ The block range is required and limited by the server's configured maximum.
73
+ `from` is inclusive and `to` is exclusive.
74
+ """
75
+ input VerificationKeyUpdateFilterInput {
76
+ verificationKeyHash: String!
77
+ from: Int!
78
+ to: Int!
79
+ status: BlockStatusFilter
80
+ }
81
+
82
+ """
83
+ Filter successful zkApp commands by block range.
84
+
85
+ Both `from` and `to` are required. The range is bounded by `ZKAPP_COMMAND_RANGE_SIZE`, and the server may reject dense ranges that expand to too many account updates.
86
+ """
87
+ input ZkappCommandFilterOptionsInput {
88
+ blockStatus: BlockStatusFilter
89
+ """
90
+ Mina block height to filter zkApp commands from, inclusive
91
+ """
92
+ from: Int!
93
+ """
94
+ Mina block height to filter zkApp commands to, exclusive
95
+ """
96
+ to: Int!
97
+ """
98
+ Optional account update public key filter.
99
+ """
100
+ accountPublicKey: String
101
+ """
102
+ Optional account update token id filter.
103
+ """
104
+ tokenId: String
105
+ }
106
+
69
107
  type EventData {
70
108
  accountUpdateId: String!
71
109
  transactionInfo: TransactionInfo
72
- data: [String]!
110
+ data: [String!]!
73
111
  }
74
112
 
75
113
  type ActionData {
76
114
  accountUpdateId: String!
77
115
  transactionInfo: TransactionInfo
78
- data: [String]!
116
+ data: [String!]!
79
117
  }
80
118
 
81
119
  type BlockInfo {
@@ -101,7 +139,16 @@ type TransactionInfo {
101
139
  memo: String!
102
140
  authorizationKind: String!
103
141
  sequenceNumber: Int!
104
- zkappAccountUpdateIds: [Int]!
142
+ zkappAccountUpdateIds: [Int!]!
143
+ }
144
+
145
+ type VerificationKeyUpdate {
146
+ accountUpdateId: String!
147
+ address: String!
148
+ tokenId: String!
149
+ verificationKeyHash: String!
150
+ blockInfo: BlockInfo!
151
+ transactionInfo: TransactionInfo!
105
152
  }
106
153
 
107
154
  type ActionStates {
@@ -114,16 +161,67 @@ type ActionStates {
114
161
 
115
162
  type EventOutput {
116
163
  blockInfo: BlockInfo
117
- eventData: [EventData]
164
+ eventData: [EventData!]
118
165
  }
119
166
 
120
167
  type ActionOutput {
121
168
  blockInfo: BlockInfo
122
169
  transactionInfo: TransactionInfo
123
- actionData: [ActionData]
170
+ actionData: [ActionData!]
124
171
  actionState: ActionStates!
125
172
  }
126
173
 
174
+ """
175
+ Raw field array from the archive database.
176
+
177
+ An empty `fields` array means the archive row exists but contains no field values.
178
+ Nullable entries mean the corresponding nullable archive state slot has no field value.
179
+ """
180
+ type ZkappFieldArray {
181
+ fields: [String]!
182
+ }
183
+
184
+ type ZkappAccountPrecondition {
185
+ state: ZkappFieldArray
186
+ actionState: ZkappFieldArray
187
+ provedState: Boolean
188
+ isNew: Boolean
189
+ }
190
+
191
+ type ZkappGlobalSlotBounds {
192
+ lowerBound: Int
193
+ upperBound: Int
194
+ }
195
+
196
+ type ZkappNetworkPrecondition {
197
+ globalSlotSinceGenesis: ZkappGlobalSlotBounds
198
+ }
199
+
200
+ type ZkappAccountUpdateOutput {
201
+ id: String!
202
+ publicKey: String!
203
+ tokenId: String!
204
+ authorizationKind: String!
205
+ balanceChange: String!
206
+ incrementNonce: Boolean!
207
+ callDepth: Int!
208
+ actions: [ZkappFieldArray!]!
209
+ events: [ZkappFieldArray!]!
210
+ appState: ZkappFieldArray
211
+ accountPrecondition: ZkappAccountPrecondition
212
+ networkPrecondition: ZkappNetworkPrecondition
213
+ }
214
+
215
+ type ZkappCommandOutput {
216
+ blockInfo: BlockInfo!
217
+ hash: String!
218
+ feePayer: String!
219
+ fee: String!
220
+ memo: String!
221
+ sequenceNumber: Int!
222
+ accountUpdates: [ZkappAccountUpdateOutput!]!
223
+ }
224
+
127
225
  """
128
226
  Metadata about the network
129
227
  """
@@ -214,8 +312,28 @@ type Block {
214
312
  }
215
313
 
216
314
  type Query {
217
- events(input: EventFilterOptionsInput!): [EventOutput]!
218
- actions(input: ActionFilterOptionsInput!): [ActionOutput]!
315
+ """
316
+ The `MAJOR.MINOR` version of this schema.
317
+
318
+ Clients compare it with the schema version they were built against: an equal
319
+ major means the contract they code against still holds, and a higher minor
320
+ means this server has additive changes they may not know about yet.
321
+
322
+ Always available, even when `ENABLED_QUERIES` restricts which data queries
323
+ this server exposes — a compatibility check that can itself be switched off
324
+ is of no use to a client.
325
+ """
326
+ schemaVersion: String!
327
+ events(input: EventFilterOptionsInput!): [EventOutput!]!
328
+ actions(input: ActionFilterOptionsInput!): [ActionOutput!]!
329
+ verificationKeyUpdates(
330
+ input: VerificationKeyUpdateFilterInput!
331
+ ): [VerificationKeyUpdate!]!
332
+ zkappCommands(input: ZkappCommandFilterOptionsInput!): [ZkappCommandOutput!]!
219
333
  networkState: NetworkStateOutput!
220
- blocks(query: BlockQueryInput, limit: Int, sortBy: BlockSortByInput): [Block]!
334
+ blocks(
335
+ query: BlockQueryInput
336
+ limit: Int
337
+ sortBy: BlockSortByInput
338
+ ): [Block!]!
221
339
  }
@@ -25,7 +25,9 @@ export type Action = {
25
25
  };
26
26
 
27
27
  export type NetworkState = {
28
- maxBlockHeight: MaxBlockHeightInfo;
28
+ // Nullable when the archive has no canonical or pending blocks indexed yet.
29
+ // Matches the schema: NetworkStateOutput.maxBlockHeight is nullable.
30
+ maxBlockHeight: MaxBlockHeightInfo | null;
29
31
  };
30
32
 
31
33
  export type MaxBlockHeightInfo = {
@@ -55,6 +57,17 @@ export type TransactionInfo = {
55
57
  zkappAccountUpdateIds: number[];
56
58
  };
57
59
 
60
+ export type VerificationKeyUpdate = {
61
+ accountUpdateId: string;
62
+ address: string;
63
+ tokenId: string;
64
+ verificationKeyHash: string;
65
+ blockInfo: BlockInfo;
66
+ transactionInfo: TransactionInfo;
67
+ };
68
+
69
+ export type VerificationKeyUpdates = VerificationKeyUpdate[];
70
+
58
71
  export type Events = {
59
72
  eventData: Event[];
60
73
  blockInfo: BlockInfo;
@@ -96,6 +109,57 @@ export type ZkAppCommand = {
96
109
  failureReason: string | null;
97
110
  };
98
111
 
112
+ export type ZkappAccountPrecondition = {
113
+ state: ZkappFieldArray | null;
114
+ actionState: ZkappFieldArray | null;
115
+ provedState: boolean | null;
116
+ isNew: boolean | null;
117
+ };
118
+
119
+ export type ZkappFieldArray = {
120
+ fields: (string | null)[];
121
+ };
122
+
123
+ export type ZkappNestedFieldArray = {
124
+ fields: string[];
125
+ };
126
+
127
+ export type ZkappGlobalSlotBounds = {
128
+ lowerBound: number | null;
129
+ upperBound: number | null;
130
+ };
131
+
132
+ export type ZkappNetworkPrecondition = {
133
+ globalSlotSinceGenesis: ZkappGlobalSlotBounds;
134
+ };
135
+
136
+ export type ZkappAccountUpdate = {
137
+ id: string;
138
+ publicKey: string;
139
+ tokenId: string;
140
+ authorizationKind: string;
141
+ balanceChange: string;
142
+ incrementNonce: boolean;
143
+ callDepth: number;
144
+ actions: ZkappNestedFieldArray[];
145
+ events: ZkappNestedFieldArray[];
146
+ appState: ZkappFieldArray | null;
147
+ accountPrecondition: ZkappAccountPrecondition;
148
+ networkPrecondition: ZkappNetworkPrecondition;
149
+ };
150
+
151
+ export type ZkappCommand = {
152
+ blockInfo: BlockInfo;
153
+ hash: string;
154
+ feePayer: string;
155
+ fee: string;
156
+ memo: string;
157
+ sequenceNumber: number;
158
+ accountUpdates: ZkappAccountUpdate[];
159
+ };
160
+
161
+ export type ZkappCommands = ZkappCommand[];
162
+
99
163
  export type FeeTransfer = {
100
164
  recipient: string;
101
165
  fee: string;
@@ -1,7 +1,31 @@
1
1
  import type { BlockInfo, TransactionInfo, Event, Action } from './types.js';
2
2
  import type { ArchiveNodeDatabaseRow } from '../db/sql/events-actions/types.js';
3
3
 
4
- export function createBlockInfo(row: ArchiveNodeDatabaseRow): BlockInfo {
4
+ type BlockInfoRow = Pick<
5
+ ArchiveNodeDatabaseRow,
6
+ | 'height'
7
+ | 'state_hash'
8
+ | 'parent_hash'
9
+ | 'ledger_hash'
10
+ | 'chain_status'
11
+ | 'timestamp'
12
+ | 'global_slot_since_hard_fork'
13
+ | 'global_slot_since_genesis'
14
+ | 'distance_from_max_block_height'
15
+ | 'last_vrf_output'
16
+ >;
17
+
18
+ type TransactionInfoRow = Pick<
19
+ ArchiveNodeDatabaseRow,
20
+ | 'status'
21
+ | 'hash'
22
+ | 'memo'
23
+ | 'authorization_kind'
24
+ | 'sequence_number'
25
+ | 'zkapp_account_updates_ids'
26
+ >;
27
+
28
+ export function createBlockInfo(row: BlockInfoRow): BlockInfo {
5
29
  return {
6
30
  height: Number(row.height),
7
31
  stateHash: row.state_hash,
@@ -17,7 +41,7 @@ export function createBlockInfo(row: ArchiveNodeDatabaseRow): BlockInfo {
17
41
  }
18
42
 
19
43
  export function createTransactionInfo(
20
- row: ArchiveNodeDatabaseRow
44
+ row: TransactionInfoRow
21
45
  ): TransactionInfo {
22
46
  return {
23
47
  status: row.status,
package/src/config.ts ADDED
@@ -0,0 +1,133 @@
1
+ export { parseBoolean, validateConfig, assertValidConfig };
2
+
3
+ type EnvSource = Record<string, string | undefined>;
4
+
5
+ const TRUE_VALUES = new Set(['true', '1', 'yes', 'on']);
6
+ const FALSE_VALUES = new Set(['false', '0', 'no', 'off']);
7
+
8
+ /** Env vars interpreted as booleans. */
9
+ const BOOLEAN_VARS = [
10
+ 'ENABLE_GRAPHIQL',
11
+ 'ENABLE_INTROSPECTION',
12
+ 'ENABLE_LOGGING',
13
+ 'ENABLE_METRICS',
14
+ 'ENABLE_JAEGER',
15
+ 'ENABLE_BLOCK_TRANSACTION_DETAILS',
16
+ 'ENABLE_ZKAPP_COMMANDS_QUERY',
17
+ ] as const;
18
+
19
+ /** Env vars that, when set, must be positive integers. */
20
+ const POSITIVE_INT_VARS = ['PORT', 'BLOCK_RANGE_SIZE'] as const;
21
+
22
+ /** Root query fields in schema.graphql — keep in sync. */
23
+ const KNOWN_QUERIES = [
24
+ 'events',
25
+ 'actions',
26
+ 'verificationKeyUpdates',
27
+ 'zkappCommands',
28
+ 'networkState',
29
+ 'blocks',
30
+ ] as const;
31
+
32
+ /**
33
+ * Parse a boolean environment value. Recognises `true/false`, `1/0`, `yes/no`,
34
+ * `on/off` (case-insensitive). Anything unrecognised — including the empty
35
+ * string or `undefined` — yields `fallback`.
36
+ *
37
+ * This replaces ad-hoc truthiness checks like `if (process.env.ENABLE_X)`, which
38
+ * treated the string `"false"` as `true` (#74).
39
+ */
40
+ function parseBoolean(value: string | undefined, fallback = false): boolean {
41
+ if (value === undefined) return fallback;
42
+ const normalized = value.trim().toLowerCase();
43
+ if (normalized === '') return fallback;
44
+ if (TRUE_VALUES.has(normalized)) return true;
45
+ if (FALSE_VALUES.has(normalized)) return false;
46
+ return fallback;
47
+ }
48
+
49
+ function isRecognisedBoolean(value: string): boolean {
50
+ const normalized = value.trim().toLowerCase();
51
+ return TRUE_VALUES.has(normalized) || FALSE_VALUES.has(normalized);
52
+ }
53
+
54
+ /**
55
+ * Validate the environment, returning a list of human-readable problems (empty
56
+ * when valid). Catches the common misconfigurations — a missing connection
57
+ * string, a non-numeric port, a mistyped boolean — so they surface at startup
58
+ * rather than as confusing runtime behaviour.
59
+ */
60
+ function validateConfig(env: EnvSource = process.env): string[] {
61
+ const errors: string[] = [];
62
+
63
+ if (!env.PG_CONN || env.PG_CONN.trim() === '') {
64
+ errors.push('PG_CONN is required (Postgres connection string).');
65
+ }
66
+
67
+ for (const name of BOOLEAN_VARS) {
68
+ const value = env[name];
69
+ if (
70
+ value !== undefined &&
71
+ value.trim() !== '' &&
72
+ !isRecognisedBoolean(value)
73
+ ) {
74
+ errors.push(`${name} must be a boolean (true/false), got "${value}".`);
75
+ }
76
+ }
77
+
78
+ for (const name of POSITIVE_INT_VARS) {
79
+ const value = env[name];
80
+ if (value !== undefined && value.trim() !== '') {
81
+ const parsed = Number(value);
82
+ if (!Number.isInteger(parsed) || parsed <= 0) {
83
+ errors.push(`${name} must be a positive integer, got "${value}".`);
84
+ }
85
+ }
86
+ }
87
+
88
+ const enabledQueries = env.ENABLED_QUERIES;
89
+ if (enabledQueries !== undefined) {
90
+ const names = enabledQueries
91
+ .split(',')
92
+ .map((query) => query.trim())
93
+ .filter((query) => query !== '');
94
+ if (names.length === 0) {
95
+ errors.push(
96
+ 'ENABLED_QUERIES is set but lists no queries; unset it to expose all of ' +
97
+ `${KNOWN_QUERIES.join(', ')}.`
98
+ );
99
+ }
100
+
101
+ // schemaVersion is served whatever this lists (see src/resolvers.ts), so
102
+ // naming it is redundant rather than wrong. Rejecting it would fail startup
103
+ // over a list that asks for exactly what it already gets.
104
+ const unknown = names.filter(
105
+ (name) =>
106
+ !(KNOWN_QUERIES as readonly string[]).includes(name) &&
107
+ name !== 'schemaVersion'
108
+ );
109
+ if (unknown.length > 0) {
110
+ errors.push(
111
+ `ENABLED_QUERIES contains unknown queries: ${unknown.join(', ')}. ` +
112
+ `Known queries: ${KNOWN_QUERIES.join(', ')}. ` +
113
+ 'schemaVersion is always served and does not need listing.'
114
+ );
115
+ }
116
+ }
117
+
118
+ return errors;
119
+ }
120
+
121
+ /**
122
+ * Validate the environment and throw a single aggregated error if anything is
123
+ * wrong, so the process fails fast at startup with a clear message instead of
124
+ * booting into a broken state.
125
+ */
126
+ function assertValidConfig(env: EnvSource = process.env): void {
127
+ const errors = validateConfig(env);
128
+ if (errors.length > 0) {
129
+ throw new Error(
130
+ `Invalid configuration:\n${errors.map((e) => ` - ${e}`).join('\n')}`
131
+ );
132
+ }
133
+ }
@@ -1,23 +1,46 @@
1
- import type { EventFilterOptionsInput } from '../../resolvers-types.js';
1
+ import type {
2
+ ActionFilterOptionsInput,
3
+ EventFilterOptionsInput,
4
+ VerificationKeyUpdateFilterInput,
5
+ ZkappCommandFilterOptionsInput,
6
+ } from '../../resolvers-types.js';
2
7
  import type {
3
8
  Actions,
4
9
  Events,
5
10
  NetworkState,
6
11
  Blocks,
12
+ VerificationKeyUpdates,
13
+ ZkappCommands,
7
14
  } from '../../blockchain/types.js';
8
- import type { BlockQueryInput, BlockSortByInput } from '../../resolvers-types.js';
15
+ import type {
16
+ BlockQueryInput,
17
+ BlockSortByInput,
18
+ } from '../../resolvers-types.js';
9
19
 
10
20
  export interface DatabaseAdapter {
11
21
  getEvents(input: EventFilterOptionsInput, options?: unknown): Promise<Events>;
12
22
  getActions(
13
- input: EventFilterOptionsInput,
23
+ input: ActionFilterOptionsInput,
14
24
  options?: unknown
15
25
  ): Promise<Actions>;
26
+ getZkappCommands(
27
+ input: ZkappCommandFilterOptionsInput,
28
+ options?: unknown
29
+ ): Promise<ZkappCommands>;
16
30
  getNetworkState(options?: unknown): Promise<NetworkState>;
31
+ getVerificationKeyUpdates(
32
+ input: VerificationKeyUpdateFilterInput,
33
+ options?: unknown
34
+ ): Promise<VerificationKeyUpdates>;
17
35
  getBlocks(
18
36
  query: BlockQueryInput | null | undefined,
19
37
  limit: number | null | undefined,
20
38
  sortBy: BlockSortByInput | null | undefined,
21
39
  options: unknown
22
40
  ): Promise<Blocks>;
41
+ /**
42
+ * Lightweight connectivity check for readiness probes. Resolves `true` when the
43
+ * database answers a trivial query, `false` otherwise. Never throws.
44
+ */
45
+ ping(): Promise<boolean>;
23
46
  }