reqon-dsl 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 (388) hide show
  1. package/.claude/settings.local.json +31 -0
  2. package/.claude/skills/api-integration.md +125 -0
  3. package/.claude/skills/database-schema.md +51 -0
  4. package/.claude/skills/dsl-design.md +80 -0
  5. package/.claude/skills/property-testing.md +143 -0
  6. package/.claude/skills/reqon/SKILL.md +44 -0
  7. package/.claude/skills/reqon/references/examples.md +206 -0
  8. package/.claude/skills/reqon/references/syntax.md +263 -0
  9. package/.claude/skills/vscode-extension.md +113 -0
  10. package/.github/dependabot.yml +32 -0
  11. package/.github/pull_request_template.md +21 -0
  12. package/.github/workflows/ci.yml +174 -0
  13. package/.github/workflows/release.yml +73 -0
  14. package/CLAUDE.md +72 -0
  15. package/CONTRIBUTING.md +161 -0
  16. package/README.md +235 -0
  17. package/TODO.md +51 -0
  18. package/dist/ast/index.d.ts +1 -0
  19. package/dist/ast/index.js +1 -0
  20. package/dist/ast/nodes.d.ts +237 -0
  21. package/dist/ast/nodes.js +12 -0
  22. package/dist/auth/auth.test.d.ts +1 -0
  23. package/dist/auth/auth.test.js +255 -0
  24. package/dist/auth/circuit-breaker.d.ts +115 -0
  25. package/dist/auth/circuit-breaker.js +267 -0
  26. package/dist/auth/credentials.d.ts +91 -0
  27. package/dist/auth/credentials.js +169 -0
  28. package/dist/auth/index.d.ts +5 -0
  29. package/dist/auth/index.js +8 -0
  30. package/dist/auth/oauth2-provider.d.ts +41 -0
  31. package/dist/auth/oauth2-provider.js +131 -0
  32. package/dist/auth/rate-limiter.d.ts +61 -0
  33. package/dist/auth/rate-limiter.js +380 -0
  34. package/dist/auth/token-store.d.ts +30 -0
  35. package/dist/auth/token-store.js +148 -0
  36. package/dist/auth/types.d.ts +142 -0
  37. package/dist/auth/types.js +1 -0
  38. package/dist/cli.d.ts +2 -0
  39. package/dist/cli.js +270 -0
  40. package/dist/errors/errors.test.d.ts +1 -0
  41. package/dist/errors/errors.test.js +165 -0
  42. package/dist/errors/index.d.ts +83 -0
  43. package/dist/errors/index.js +159 -0
  44. package/dist/execution/execution.test.d.ts +1 -0
  45. package/dist/execution/execution.test.js +246 -0
  46. package/dist/execution/index.d.ts +4 -0
  47. package/dist/execution/index.js +2 -0
  48. package/dist/execution/state.d.ts +136 -0
  49. package/dist/execution/state.js +82 -0
  50. package/dist/execution/store.d.ts +52 -0
  51. package/dist/execution/store.js +120 -0
  52. package/dist/index.d.ts +27 -0
  53. package/dist/index.js +57 -0
  54. package/dist/integration.test.d.ts +1 -0
  55. package/dist/integration.test.js +168 -0
  56. package/dist/interpreter/context.d.ts +15 -0
  57. package/dist/interpreter/context.js +29 -0
  58. package/dist/interpreter/evaluator.d.ts +5 -0
  59. package/dist/interpreter/evaluator.js +223 -0
  60. package/dist/interpreter/evaluator.test.d.ts +1 -0
  61. package/dist/interpreter/evaluator.test.js +512 -0
  62. package/dist/interpreter/executor.d.ts +131 -0
  63. package/dist/interpreter/executor.js +663 -0
  64. package/dist/interpreter/fetch-handler.d.ts +43 -0
  65. package/dist/interpreter/fetch-handler.js +203 -0
  66. package/dist/interpreter/http.d.ts +57 -0
  67. package/dist/interpreter/http.js +210 -0
  68. package/dist/interpreter/http.test.d.ts +1 -0
  69. package/dist/interpreter/http.test.js +299 -0
  70. package/dist/interpreter/index.d.ts +7 -0
  71. package/dist/interpreter/index.js +7 -0
  72. package/dist/interpreter/pagination.d.ts +63 -0
  73. package/dist/interpreter/pagination.js +155 -0
  74. package/dist/interpreter/progress.test.d.ts +1 -0
  75. package/dist/interpreter/progress.test.js +216 -0
  76. package/dist/interpreter/schema-matcher.d.ts +16 -0
  77. package/dist/interpreter/schema-matcher.js +136 -0
  78. package/dist/interpreter/schema-matcher.test.d.ts +1 -0
  79. package/dist/interpreter/schema-matcher.test.js +122 -0
  80. package/dist/interpreter/signals.d.ts +57 -0
  81. package/dist/interpreter/signals.js +73 -0
  82. package/dist/interpreter/step-handlers/for-handler.d.ts +17 -0
  83. package/dist/interpreter/step-handlers/for-handler.js +51 -0
  84. package/dist/interpreter/step-handlers/index.d.ts +8 -0
  85. package/dist/interpreter/step-handlers/index.js +8 -0
  86. package/dist/interpreter/step-handlers/map-handler.d.ts +10 -0
  87. package/dist/interpreter/step-handlers/map-handler.js +20 -0
  88. package/dist/interpreter/step-handlers/match-handler.d.ts +27 -0
  89. package/dist/interpreter/step-handlers/match-handler.js +61 -0
  90. package/dist/interpreter/step-handlers/store-handler.d.ts +13 -0
  91. package/dist/interpreter/step-handlers/store-handler.js +66 -0
  92. package/dist/interpreter/step-handlers/types.d.ts +15 -0
  93. package/dist/interpreter/step-handlers/types.js +1 -0
  94. package/dist/interpreter/step-handlers/validate-handler.d.ts +10 -0
  95. package/dist/interpreter/step-handlers/validate-handler.js +26 -0
  96. package/dist/interpreter/step-handlers/webhook-handler.d.ts +36 -0
  97. package/dist/interpreter/step-handlers/webhook-handler.js +104 -0
  98. package/dist/lexer/index.d.ts +10 -0
  99. package/dist/lexer/index.js +12 -0
  100. package/dist/lexer/lexer.d.ts +24 -0
  101. package/dist/lexer/lexer.js +264 -0
  102. package/dist/lexer/lexer.test.d.ts +1 -0
  103. package/dist/lexer/lexer.test.js +259 -0
  104. package/dist/lexer/tokens.d.ts +69 -0
  105. package/dist/lexer/tokens.js +146 -0
  106. package/dist/loader/index.d.ts +36 -0
  107. package/dist/loader/index.js +220 -0
  108. package/dist/loader/loader.test.d.ts +1 -0
  109. package/dist/loader/loader.test.js +287 -0
  110. package/dist/oas/index.d.ts +4 -0
  111. package/dist/oas/index.js +2 -0
  112. package/dist/oas/loader.d.ts +21 -0
  113. package/dist/oas/loader.js +82 -0
  114. package/dist/oas/oas.test.d.ts +1 -0
  115. package/dist/oas/oas.test.js +218 -0
  116. package/dist/oas/validator.d.ts +12 -0
  117. package/dist/oas/validator.js +227 -0
  118. package/dist/parser/base.d.ts +33 -0
  119. package/dist/parser/base.js +97 -0
  120. package/dist/parser/expressions.d.ts +27 -0
  121. package/dist/parser/expressions.js +248 -0
  122. package/dist/parser/expressions.test.d.ts +1 -0
  123. package/dist/parser/expressions.test.js +378 -0
  124. package/dist/parser/index.d.ts +3 -0
  125. package/dist/parser/index.js +3 -0
  126. package/dist/parser/match.test.d.ts +1 -0
  127. package/dist/parser/match.test.js +254 -0
  128. package/dist/parser/parser.d.ts +68 -0
  129. package/dist/parser/parser.js +1229 -0
  130. package/dist/parser/parser.test.d.ts +1 -0
  131. package/dist/parser/parser.test.js +333 -0
  132. package/dist/parser/schedule.test.d.ts +1 -0
  133. package/dist/parser/schedule.test.js +241 -0
  134. package/dist/plugin.d.ts +35 -0
  135. package/dist/plugin.js +68 -0
  136. package/dist/scheduler/cron-parser.d.ts +32 -0
  137. package/dist/scheduler/cron-parser.js +198 -0
  138. package/dist/scheduler/cron-parser.test.d.ts +1 -0
  139. package/dist/scheduler/cron-parser.test.js +188 -0
  140. package/dist/scheduler/index.d.ts +3 -0
  141. package/dist/scheduler/index.js +2 -0
  142. package/dist/scheduler/scheduler.d.ts +81 -0
  143. package/dist/scheduler/scheduler.js +376 -0
  144. package/dist/scheduler/types.d.ts +65 -0
  145. package/dist/scheduler/types.js +1 -0
  146. package/dist/stores/factory.d.ts +36 -0
  147. package/dist/stores/factory.js +73 -0
  148. package/dist/stores/file.d.ts +60 -0
  149. package/dist/stores/file.js +173 -0
  150. package/dist/stores/file.test.d.ts +1 -0
  151. package/dist/stores/file.test.js +165 -0
  152. package/dist/stores/index.d.ts +6 -0
  153. package/dist/stores/index.js +5 -0
  154. package/dist/stores/memory.d.ts +19 -0
  155. package/dist/stores/memory.js +51 -0
  156. package/dist/stores/memory.test.d.ts +1 -0
  157. package/dist/stores/memory.test.js +157 -0
  158. package/dist/stores/postgrest.d.ts +55 -0
  159. package/dist/stores/postgrest.js +217 -0
  160. package/dist/stores/stores.test.d.ts +1 -0
  161. package/dist/stores/stores.test.js +158 -0
  162. package/dist/stores/types.d.ts +31 -0
  163. package/dist/stores/types.js +26 -0
  164. package/dist/sync/index.d.ts +4 -0
  165. package/dist/sync/index.js +2 -0
  166. package/dist/sync/state.d.ts +69 -0
  167. package/dist/sync/state.js +66 -0
  168. package/dist/sync/store.d.ts +49 -0
  169. package/dist/sync/store.js +93 -0
  170. package/dist/sync/sync.test.d.ts +1 -0
  171. package/dist/sync/sync.test.js +221 -0
  172. package/dist/utils/async.d.ts +7 -0
  173. package/dist/utils/async.js +9 -0
  174. package/dist/utils/file.d.ts +38 -0
  175. package/dist/utils/file.js +92 -0
  176. package/dist/utils/index.d.ts +4 -0
  177. package/dist/utils/index.js +4 -0
  178. package/dist/utils/logger.d.ts +34 -0
  179. package/dist/utils/logger.js +39 -0
  180. package/dist/utils/path.d.ts +12 -0
  181. package/dist/utils/path.js +41 -0
  182. package/dist/webhook/index.d.ts +8 -0
  183. package/dist/webhook/index.js +7 -0
  184. package/dist/webhook/server.d.ts +84 -0
  185. package/dist/webhook/server.js +319 -0
  186. package/dist/webhook/store.d.ts +67 -0
  187. package/dist/webhook/store.js +193 -0
  188. package/dist/webhook/types.d.ts +88 -0
  189. package/dist/webhook/types.js +6 -0
  190. package/docusaurus/README.md +41 -0
  191. package/docusaurus/docs/advanced/execution-state.md +283 -0
  192. package/docusaurus/docs/advanced/extending-reqon.md +388 -0
  193. package/docusaurus/docs/advanced/multi-file-missions.md +250 -0
  194. package/docusaurus/docs/advanced/parallel-execution.md +353 -0
  195. package/docusaurus/docs/api-reference.md +443 -0
  196. package/docusaurus/docs/authentication/api-key.md +339 -0
  197. package/docusaurus/docs/authentication/basic.md +276 -0
  198. package/docusaurus/docs/authentication/bearer.md +282 -0
  199. package/docusaurus/docs/authentication/oauth2.md +317 -0
  200. package/docusaurus/docs/authentication/overview.md +251 -0
  201. package/docusaurus/docs/cli.md +229 -0
  202. package/docusaurus/docs/core-concepts/actions.md +286 -0
  203. package/docusaurus/docs/core-concepts/missions.md +264 -0
  204. package/docusaurus/docs/core-concepts/schemas.md +353 -0
  205. package/docusaurus/docs/core-concepts/sources.md +339 -0
  206. package/docusaurus/docs/core-concepts/stores.md +332 -0
  207. package/docusaurus/docs/dsl-syntax/expressions.md +361 -0
  208. package/docusaurus/docs/dsl-syntax/fetch.md +293 -0
  209. package/docusaurus/docs/dsl-syntax/for-loops.md +324 -0
  210. package/docusaurus/docs/dsl-syntax/map.md +345 -0
  211. package/docusaurus/docs/dsl-syntax/match.md +387 -0
  212. package/docusaurus/docs/dsl-syntax/pipelines.md +397 -0
  213. package/docusaurus/docs/dsl-syntax/validate.md +401 -0
  214. package/docusaurus/docs/error-handling/dead-letter-queues.md +399 -0
  215. package/docusaurus/docs/error-handling/flow-control.md +337 -0
  216. package/docusaurus/docs/error-handling/retry-strategies.md +368 -0
  217. package/docusaurus/docs/examples.md +488 -0
  218. package/docusaurus/docs/getting-started.md +256 -0
  219. package/docusaurus/docs/http/circuit-breaker.md +401 -0
  220. package/docusaurus/docs/http/incremental-sync.md +394 -0
  221. package/docusaurus/docs/http/pagination.md +361 -0
  222. package/docusaurus/docs/http/rate-limiting.md +383 -0
  223. package/docusaurus/docs/http/requests.md +328 -0
  224. package/docusaurus/docs/http/retry.md +402 -0
  225. package/docusaurus/docs/intro.md +90 -0
  226. package/docusaurus/docs/openapi/loading-specs.md +305 -0
  227. package/docusaurus/docs/openapi/operation-calls.md +314 -0
  228. package/docusaurus/docs/openapi/overview.md +212 -0
  229. package/docusaurus/docs/openapi/response-validation.md +344 -0
  230. package/docusaurus/docs/scheduling/cron.md +305 -0
  231. package/docusaurus/docs/scheduling/daemon-mode.md +317 -0
  232. package/docusaurus/docs/scheduling/intervals.md +289 -0
  233. package/docusaurus/docs/scheduling/overview.md +231 -0
  234. package/docusaurus/docs/stores/custom-adapters.md +376 -0
  235. package/docusaurus/docs/stores/file.md +236 -0
  236. package/docusaurus/docs/stores/memory.md +193 -0
  237. package/docusaurus/docs/stores/overview.md +274 -0
  238. package/docusaurus/docs/stores/postgrest.md +316 -0
  239. package/docusaurus/docusaurus.config.ts +148 -0
  240. package/docusaurus/package-lock.json +18029 -0
  241. package/docusaurus/package.json +47 -0
  242. package/docusaurus/sidebars.ts +155 -0
  243. package/docusaurus/src/components/HomepageFeatures/index.tsx +105 -0
  244. package/docusaurus/src/components/HomepageFeatures/styles.module.css +12 -0
  245. package/docusaurus/src/css/custom.css +169 -0
  246. package/docusaurus/src/pages/index.module.css +48 -0
  247. package/docusaurus/src/pages/index.tsx +110 -0
  248. package/docusaurus/src/pages/markdown-page.md +7 -0
  249. package/docusaurus/static/.nojekyll +0 -0
  250. package/docusaurus/static/img/docusaurus-social-card.jpg +0 -0
  251. package/docusaurus/static/img/docusaurus.png +0 -0
  252. package/docusaurus/static/img/favicon.ico +0 -0
  253. package/docusaurus/static/img/logo.svg +10 -0
  254. package/docusaurus/static/img/undraw_docusaurus_mountain.svg +171 -0
  255. package/docusaurus/static/img/undraw_docusaurus_react.svg +170 -0
  256. package/docusaurus/static/img/undraw_docusaurus_tree.svg +40 -0
  257. package/docusaurus/tsconfig.json +8 -0
  258. package/examples/README.md +112 -0
  259. package/examples/error-handling/README.md +150 -0
  260. package/examples/error-handling/payment-processor.vague +287 -0
  261. package/examples/github-sync/README.md +74 -0
  262. package/examples/github-sync/fetch-issues.vague +47 -0
  263. package/examples/github-sync/fetch-prs.vague +40 -0
  264. package/examples/github-sync/mission.vague +101 -0
  265. package/examples/github-sync/normalize.vague +70 -0
  266. package/examples/jsonplaceholder/README.md +28 -0
  267. package/examples/jsonplaceholder/posts.vague +48 -0
  268. package/examples/petstore/README.md +35 -0
  269. package/examples/petstore/openapi.yaml +97 -0
  270. package/examples/petstore/sync.vague +52 -0
  271. package/examples/temporal-comparison/README.md +297 -0
  272. package/examples/temporal-comparison/reconciliation.vague +355 -0
  273. package/examples/temporal-comparison/temporal/activities/index.ts +8 -0
  274. package/examples/temporal-comparison/temporal/activities/shipstation.ts +225 -0
  275. package/examples/temporal-comparison/temporal/activities/shopify.ts +257 -0
  276. package/examples/temporal-comparison/temporal/activities/storage.ts +198 -0
  277. package/examples/temporal-comparison/temporal/activities/stripe.ts +169 -0
  278. package/examples/temporal-comparison/temporal/activities/validation.ts +205 -0
  279. package/examples/temporal-comparison/temporal/client/schedule.ts +218 -0
  280. package/examples/temporal-comparison/temporal/config/retry.ts +63 -0
  281. package/examples/temporal-comparison/temporal/types/index.ts +129 -0
  282. package/examples/temporal-comparison/temporal/workers/main.ts +130 -0
  283. package/examples/temporal-comparison/temporal/workflows/orderReconciliation.ts +262 -0
  284. package/examples/xero/README.md +88 -0
  285. package/examples/xero/invoices.vague +189 -0
  286. package/package.json +40 -0
  287. package/src/api-integration.test.ts +954 -0
  288. package/src/ast/index.ts +1 -0
  289. package/src/ast/nodes.ts +310 -0
  290. package/src/auth/auth.test.ts +326 -0
  291. package/src/auth/circuit-breaker.test.ts +390 -0
  292. package/src/auth/circuit-breaker.ts +379 -0
  293. package/src/auth/credentials.test.ts +273 -0
  294. package/src/auth/credentials.ts +246 -0
  295. package/src/auth/index.ts +40 -0
  296. package/src/auth/oauth2-provider.ts +177 -0
  297. package/src/auth/rate-limiter.ts +459 -0
  298. package/src/auth/token-store.ts +177 -0
  299. package/src/auth/types.ts +159 -0
  300. package/src/benchmark/e2e.bench.ts +288 -0
  301. package/src/benchmark/evaluator.bench.ts +331 -0
  302. package/src/benchmark/fixtures.ts +295 -0
  303. package/src/benchmark/index.ts +108 -0
  304. package/src/benchmark/lexer.bench.ts +69 -0
  305. package/src/benchmark/parser.bench.ts +103 -0
  306. package/src/benchmark/resilience.bench.ts +193 -0
  307. package/src/benchmark/store.bench.ts +147 -0
  308. package/src/benchmark/utils.ts +230 -0
  309. package/src/cli.ts +313 -0
  310. package/src/errors/errors.test.ts +234 -0
  311. package/src/errors/index.ts +223 -0
  312. package/src/execution/execution.test.ts +307 -0
  313. package/src/execution/index.ts +21 -0
  314. package/src/execution/state.ts +207 -0
  315. package/src/execution/store.ts +188 -0
  316. package/src/index.ts +169 -0
  317. package/src/integration.test.ts +192 -0
  318. package/src/interpreter/context.ts +57 -0
  319. package/src/interpreter/evaluator.test.ts +796 -0
  320. package/src/interpreter/evaluator.ts +245 -0
  321. package/src/interpreter/executor.ts +946 -0
  322. package/src/interpreter/fetch-handler.ts +302 -0
  323. package/src/interpreter/http.test.ts +423 -0
  324. package/src/interpreter/http.ts +308 -0
  325. package/src/interpreter/index.ts +32 -0
  326. package/src/interpreter/pagination.ts +207 -0
  327. package/src/interpreter/progress.test.ts +276 -0
  328. package/src/interpreter/schema-matcher.test.ts +160 -0
  329. package/src/interpreter/schema-matcher.ts +168 -0
  330. package/src/interpreter/signals.ts +73 -0
  331. package/src/interpreter/step-handlers/for-handler.ts +65 -0
  332. package/src/interpreter/step-handlers/index.ts +17 -0
  333. package/src/interpreter/step-handlers/map-handler.ts +24 -0
  334. package/src/interpreter/step-handlers/match-handler.ts +101 -0
  335. package/src/interpreter/step-handlers/store-handler.ts +78 -0
  336. package/src/interpreter/step-handlers/types.ts +17 -0
  337. package/src/interpreter/step-handlers/validate-handler.ts +30 -0
  338. package/src/interpreter/step-handlers/webhook-handler.ts +142 -0
  339. package/src/lexer/index.ts +18 -0
  340. package/src/lexer/lexer.test.ts +316 -0
  341. package/src/lexer/tokens.ts +179 -0
  342. package/src/loader/index.ts +288 -0
  343. package/src/loader/loader.test.ts +360 -0
  344. package/src/oas/index.ts +4 -0
  345. package/src/oas/loader.ts +126 -0
  346. package/src/oas/oas.test.ts +254 -0
  347. package/src/oas/validator.ts +299 -0
  348. package/src/parser/base.ts +124 -0
  349. package/src/parser/expressions.test.ts +525 -0
  350. package/src/parser/expressions.ts +314 -0
  351. package/src/parser/index.ts +3 -0
  352. package/src/parser/match.test.ts +296 -0
  353. package/src/parser/parser.test.ts +739 -0
  354. package/src/parser/parser.ts +1469 -0
  355. package/src/parser/schedule.test.ts +287 -0
  356. package/src/parser/webhook.test.ts +248 -0
  357. package/src/plugin.ts +83 -0
  358. package/src/scheduler/cron-parser.test.ts +236 -0
  359. package/src/scheduler/cron-parser.ts +236 -0
  360. package/src/scheduler/index.ts +10 -0
  361. package/src/scheduler/scheduler.ts +443 -0
  362. package/src/scheduler/types.ts +71 -0
  363. package/src/stores/factory.ts +104 -0
  364. package/src/stores/file.test.ts +276 -0
  365. package/src/stores/file.ts +211 -0
  366. package/src/stores/index.ts +6 -0
  367. package/src/stores/memory.test.ts +238 -0
  368. package/src/stores/memory.ts +63 -0
  369. package/src/stores/postgrest.test.ts +488 -0
  370. package/src/stores/postgrest.ts +263 -0
  371. package/src/stores/stores.test.ts +197 -0
  372. package/src/stores/types.ts +58 -0
  373. package/src/sync/index.ts +16 -0
  374. package/src/sync/state.ts +126 -0
  375. package/src/sync/store.ts +139 -0
  376. package/src/sync/sync.test.ts +271 -0
  377. package/src/utils/async.ts +10 -0
  378. package/src/utils/file.ts +106 -0
  379. package/src/utils/index.ts +14 -0
  380. package/src/utils/logger.ts +53 -0
  381. package/src/utils/path.ts +47 -0
  382. package/src/webhook/index.ts +15 -0
  383. package/src/webhook/server.test.ts +253 -0
  384. package/src/webhook/server.ts +389 -0
  385. package/src/webhook/store.ts +239 -0
  386. package/src/webhook/types.ts +93 -0
  387. package/tsconfig.json +17 -0
  388. package/vitest.config.ts +39 -0
@@ -0,0 +1,173 @@
1
+ import { writeFile } from 'node:fs/promises';
2
+ import { writeFileSync, existsSync } from 'node:fs';
3
+ import { join, dirname } from 'node:path';
4
+ import { applyStoreFilter } from './types.js';
5
+ import { ensureDirectory, readJsonFile, serialize, } from '../utils/file.js';
6
+ const DEFAULT_OPTIONS = {
7
+ baseDir: '.reqon-data',
8
+ persist: 'immediate',
9
+ pretty: true,
10
+ debounceMs: 100,
11
+ };
12
+ /**
13
+ * File-based JSON store for local development
14
+ * Persists data to .reqon-data/{name}.json
15
+ */
16
+ export class FileStore {
17
+ data = new Map();
18
+ filePath;
19
+ options;
20
+ dirty = false;
21
+ initialized;
22
+ debounceTimer = null;
23
+ pendingWrite = null;
24
+ constructor(name, options = {}) {
25
+ this.options = { ...DEFAULT_OPTIONS, ...options };
26
+ this.filePath = join(this.options.baseDir, `${name}.json`);
27
+ this.initialized = this.init();
28
+ }
29
+ async init() {
30
+ const dir = dirname(this.filePath);
31
+ await ensureDirectory(dir);
32
+ // Create .gitignore if it doesn't exist
33
+ const gitignorePath = join(dir, '.gitignore');
34
+ if (!existsSync(gitignorePath)) {
35
+ await writeFile(gitignorePath, '# Reqon local data\n*.json\n', 'utf-8');
36
+ }
37
+ await this.load();
38
+ }
39
+ async load() {
40
+ const parsed = await readJsonFile(this.filePath);
41
+ if (parsed) {
42
+ this.data = new Map(Object.entries(parsed));
43
+ }
44
+ }
45
+ async persist() {
46
+ if (this.options.persist === 'batch') {
47
+ this.dirty = true;
48
+ return;
49
+ }
50
+ if (this.options.persist === 'debounce') {
51
+ this.dirty = true;
52
+ this.scheduleDebouncedWrite();
53
+ return;
54
+ }
55
+ await this.writeToDisk();
56
+ }
57
+ scheduleDebouncedWrite() {
58
+ // Clear existing timer if any
59
+ if (this.debounceTimer) {
60
+ clearTimeout(this.debounceTimer);
61
+ }
62
+ // Schedule new write
63
+ this.debounceTimer = setTimeout(() => {
64
+ this.debounceTimer = null;
65
+ if (this.dirty && !this.pendingWrite) {
66
+ this.pendingWrite = this.writeToDisk().finally(() => {
67
+ this.pendingWrite = null;
68
+ });
69
+ }
70
+ }, this.options.debounceMs);
71
+ }
72
+ async writeToDisk() {
73
+ const obj = Object.fromEntries(this.data);
74
+ const content = serialize(obj, this.options.pretty);
75
+ await writeFile(this.filePath, content, 'utf-8');
76
+ this.dirty = false;
77
+ }
78
+ /** Synchronous write for flush/close operations */
79
+ writeToDiskSync() {
80
+ const obj = Object.fromEntries(this.data);
81
+ const content = serialize(obj, this.options.pretty);
82
+ writeFileSync(this.filePath, content, 'utf-8');
83
+ this.dirty = false;
84
+ }
85
+ async get(key) {
86
+ await this.initialized;
87
+ return this.data.get(key) ?? null;
88
+ }
89
+ async set(key, value) {
90
+ await this.initialized;
91
+ this.data.set(key, { ...value });
92
+ await this.persist();
93
+ }
94
+ async bulkSet(records) {
95
+ await this.initialized;
96
+ // Set all records in memory first (no disk I/O per record)
97
+ for (const { key, value } of records) {
98
+ this.data.set(key, { ...value });
99
+ }
100
+ // Single persist operation for all records
101
+ await this.persist();
102
+ }
103
+ async update(key, value) {
104
+ await this.initialized;
105
+ const existing = this.data.get(key);
106
+ if (existing) {
107
+ this.data.set(key, { ...existing, ...value });
108
+ }
109
+ else {
110
+ this.data.set(key, value);
111
+ }
112
+ await this.persist();
113
+ }
114
+ async delete(key) {
115
+ await this.initialized;
116
+ this.data.delete(key);
117
+ await this.persist();
118
+ }
119
+ async list(filter) {
120
+ await this.initialized;
121
+ return applyStoreFilter(Array.from(this.data.values()), filter);
122
+ }
123
+ async count(filter) {
124
+ await this.initialized;
125
+ // Apply only the where clause for counting (ignore limit/offset)
126
+ const filtered = applyStoreFilter(Array.from(this.data.values()), {
127
+ where: filter?.where,
128
+ });
129
+ return filtered.length;
130
+ }
131
+ async clear() {
132
+ await this.initialized;
133
+ this.data.clear();
134
+ await this.persist();
135
+ }
136
+ /**
137
+ * Flush pending changes to disk (needed in 'batch' or 'debounce' mode)
138
+ * Uses synchronous I/O to ensure data is written before process exits
139
+ */
140
+ flush() {
141
+ // Cancel any pending debounce timer
142
+ if (this.debounceTimer) {
143
+ clearTimeout(this.debounceTimer);
144
+ this.debounceTimer = null;
145
+ }
146
+ if (this.dirty) {
147
+ this.writeToDiskSync();
148
+ }
149
+ }
150
+ /**
151
+ * Close the store, ensuring all pending changes are written to disk.
152
+ * Should be called before the process exits to prevent data loss in batch mode.
153
+ */
154
+ close() {
155
+ this.flush();
156
+ }
157
+ /**
158
+ * Reload data from disk (useful if file was modified externally)
159
+ */
160
+ async reload() {
161
+ await this.load();
162
+ }
163
+ // For debugging
164
+ size() {
165
+ return this.data.size;
166
+ }
167
+ dump() {
168
+ return Array.from(this.data.values());
169
+ }
170
+ getFilePath() {
171
+ return this.filePath;
172
+ }
173
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,165 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import { existsSync, rmSync, readFileSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { FileStore } from './file.js';
5
+ describe('FileStore', () => {
6
+ const TEST_DIR = '.reqon-test-stores';
7
+ beforeEach(() => {
8
+ if (existsSync(TEST_DIR)) {
9
+ rmSync(TEST_DIR, { recursive: true, force: true });
10
+ }
11
+ });
12
+ afterEach(() => {
13
+ if (existsSync(TEST_DIR)) {
14
+ rmSync(TEST_DIR, { recursive: true, force: true });
15
+ }
16
+ });
17
+ it('should create directory and file on first write', async () => {
18
+ const store = new FileStore('test-store', { baseDir: TEST_DIR });
19
+ await store.set('1', { id: '1', name: 'Alice' });
20
+ expect(existsSync(join(TEST_DIR, 'test-store.json'))).toBe(true);
21
+ });
22
+ it('should create .gitignore in data directory', () => {
23
+ new FileStore('test-store', { baseDir: TEST_DIR });
24
+ const gitignorePath = join(TEST_DIR, '.gitignore');
25
+ expect(existsSync(gitignorePath)).toBe(true);
26
+ const content = readFileSync(gitignorePath, 'utf-8');
27
+ expect(content).toContain('*.json');
28
+ });
29
+ it('should get a record by key', async () => {
30
+ const store = new FileStore('test-store', { baseDir: TEST_DIR });
31
+ await store.set('1', { id: '1', name: 'Alice' });
32
+ const record = await store.get('1');
33
+ expect(record).toEqual({ id: '1', name: 'Alice' });
34
+ });
35
+ it('should return null for non-existent key', async () => {
36
+ const store = new FileStore('test-store', { baseDir: TEST_DIR });
37
+ const record = await store.get('nonexistent');
38
+ expect(record).toBeNull();
39
+ });
40
+ it('should update existing record', async () => {
41
+ const store = new FileStore('test-store', { baseDir: TEST_DIR });
42
+ await store.set('1', { id: '1', name: 'Alice', age: 30 });
43
+ await store.update('1', { age: 31 });
44
+ const record = await store.get('1');
45
+ expect(record).toEqual({ id: '1', name: 'Alice', age: 31 });
46
+ });
47
+ it('should create record on update if not exists', async () => {
48
+ const store = new FileStore('test-store', { baseDir: TEST_DIR });
49
+ await store.update('1', { name: 'Alice' });
50
+ const record = await store.get('1');
51
+ expect(record).toEqual({ name: 'Alice' });
52
+ });
53
+ it('should delete a record', async () => {
54
+ const store = new FileStore('test-store', { baseDir: TEST_DIR });
55
+ await store.set('1', { id: '1', name: 'Alice' });
56
+ await store.delete('1');
57
+ const record = await store.get('1');
58
+ expect(record).toBeNull();
59
+ });
60
+ it('should list all records', async () => {
61
+ const store = new FileStore('test-store', { baseDir: TEST_DIR });
62
+ await store.set('1', { id: '1', name: 'Alice' });
63
+ await store.set('2', { id: '2', name: 'Bob' });
64
+ const records = await store.list();
65
+ expect(records).toHaveLength(2);
66
+ });
67
+ it('should filter records with where clause', async () => {
68
+ const store = new FileStore('test-store', { baseDir: TEST_DIR });
69
+ await store.set('1', { id: '1', name: 'Alice', active: true });
70
+ await store.set('2', { id: '2', name: 'Bob', active: false });
71
+ await store.set('3', { id: '3', name: 'Charlie', active: true });
72
+ const records = await store.list({ where: { active: true } });
73
+ expect(records).toHaveLength(2);
74
+ expect(records.map(r => r.name)).toContain('Alice');
75
+ expect(records.map(r => r.name)).toContain('Charlie');
76
+ });
77
+ it('should support limit and offset', async () => {
78
+ const store = new FileStore('test-store', { baseDir: TEST_DIR });
79
+ await store.set('1', { id: '1', name: 'Alice' });
80
+ await store.set('2', { id: '2', name: 'Bob' });
81
+ await store.set('3', { id: '3', name: 'Charlie' });
82
+ const records = await store.list({ offset: 1, limit: 1 });
83
+ expect(records).toHaveLength(1);
84
+ });
85
+ it('should clear all records', async () => {
86
+ const store = new FileStore('test-store', { baseDir: TEST_DIR });
87
+ await store.set('1', { id: '1', name: 'Alice' });
88
+ await store.set('2', { id: '2', name: 'Bob' });
89
+ await store.clear();
90
+ const records = await store.list();
91
+ expect(records).toHaveLength(0);
92
+ });
93
+ describe('batch mode', () => {
94
+ it('should not write to disk until flush in batch mode', async () => {
95
+ const store = new FileStore('test-store', { baseDir: TEST_DIR, persist: 'batch' });
96
+ const filePath = store.getFilePath();
97
+ await store.set('1', { id: '1', name: 'Alice' });
98
+ // File exists but should be empty or not contain data yet
99
+ const contentBefore = existsSync(filePath) ? readFileSync(filePath, 'utf-8') : '{}';
100
+ const parsedBefore = JSON.parse(contentBefore);
101
+ expect(Object.keys(parsedBefore)).toHaveLength(0);
102
+ // After flush, data should be persisted
103
+ store.flush();
104
+ const contentAfter = readFileSync(filePath, 'utf-8');
105
+ const parsedAfter = JSON.parse(contentAfter);
106
+ expect(parsedAfter['1']).toEqual({ id: '1', name: 'Alice' });
107
+ });
108
+ });
109
+ describe('persistence', () => {
110
+ it('should persist data across store instances', async () => {
111
+ const store1 = new FileStore('test-store', { baseDir: TEST_DIR });
112
+ await store1.set('1', { id: '1', name: 'Alice' });
113
+ const store2 = new FileStore('test-store', { baseDir: TEST_DIR });
114
+ const record = await store2.get('1');
115
+ expect(record).toEqual({ id: '1', name: 'Alice' });
116
+ });
117
+ it('should reload data from disk', async () => {
118
+ const store = new FileStore('test-store', { baseDir: TEST_DIR });
119
+ await store.set('1', { id: '1', name: 'Alice' });
120
+ // Manually modify the file
121
+ const filePath = store.getFilePath();
122
+ const content = JSON.parse(readFileSync(filePath, 'utf-8'));
123
+ content['1'].name = 'Modified';
124
+ require('fs').writeFileSync(filePath, JSON.stringify(content));
125
+ // Reload and check
126
+ store.reload();
127
+ const record = await store.get('1');
128
+ expect(record?.name).toBe('Modified');
129
+ });
130
+ });
131
+ describe('pretty printing', () => {
132
+ it('should pretty-print JSON by default', async () => {
133
+ const store = new FileStore('test-store', { baseDir: TEST_DIR });
134
+ await store.set('1', { id: '1', name: 'Alice' });
135
+ const content = readFileSync(store.getFilePath(), 'utf-8');
136
+ expect(content).toContain('\n'); // Pretty printed has newlines
137
+ });
138
+ it('should compact JSON when pretty is false', async () => {
139
+ const store = new FileStore('test-store', { baseDir: TEST_DIR, pretty: false });
140
+ await store.set('1', { id: '1', name: 'Alice' });
141
+ const content = readFileSync(store.getFilePath(), 'utf-8');
142
+ expect(content).not.toContain('\n'); // Compact has no newlines
143
+ });
144
+ });
145
+ describe('utilities', () => {
146
+ it('should report size correctly', async () => {
147
+ const store = new FileStore('test-store', { baseDir: TEST_DIR });
148
+ expect(store.size()).toBe(0);
149
+ await store.set('1', { id: '1' });
150
+ await store.set('2', { id: '2' });
151
+ expect(store.size()).toBe(2);
152
+ });
153
+ it('should dump all records', async () => {
154
+ const store = new FileStore('test-store', { baseDir: TEST_DIR });
155
+ await store.set('1', { id: '1', name: 'Alice' });
156
+ await store.set('2', { id: '2', name: 'Bob' });
157
+ const dump = store.dump();
158
+ expect(dump).toHaveLength(2);
159
+ });
160
+ it('should return file path', () => {
161
+ const store = new FileStore('test-store', { baseDir: TEST_DIR });
162
+ expect(store.getFilePath()).toBe(join(TEST_DIR, 'test-store.json'));
163
+ });
164
+ });
165
+ });
@@ -0,0 +1,6 @@
1
+ export type { StoreAdapter, StoreFilter, StoreConfig } from './types.js';
2
+ export { applyStoreFilter } from './types.js';
3
+ export { MemoryStore } from './memory.js';
4
+ export { FileStore, type FileStoreOptions } from './file.js';
5
+ export { PostgRESTStore, PostgRESTError, type PostgRESTOptions } from './postgrest.js';
6
+ export { createStore, resolveStoreType, type StoreType, type CreateStoreOptions } from './factory.js';
@@ -0,0 +1,5 @@
1
+ export { applyStoreFilter } from './types.js';
2
+ export { MemoryStore } from './memory.js';
3
+ export { FileStore } from './file.js';
4
+ export { PostgRESTStore, PostgRESTError } from './postgrest.js';
5
+ export { createStore, resolveStoreType } from './factory.js';
@@ -0,0 +1,19 @@
1
+ import type { StoreAdapter, StoreFilter } from './types.js';
2
+ export declare class MemoryStore implements StoreAdapter {
3
+ private data;
4
+ private name;
5
+ constructor(name: string);
6
+ get(key: string): Promise<Record<string, unknown> | null>;
7
+ set(key: string, value: Record<string, unknown>): Promise<void>;
8
+ bulkSet(records: Array<{
9
+ key: string;
10
+ value: Record<string, unknown>;
11
+ }>): Promise<void>;
12
+ update(key: string, value: Partial<Record<string, unknown>>): Promise<void>;
13
+ delete(key: string): Promise<void>;
14
+ list(filter?: StoreFilter): Promise<Record<string, unknown>[]>;
15
+ count(filter?: StoreFilter): Promise<number>;
16
+ clear(): Promise<void>;
17
+ size(): number;
18
+ dump(): Record<string, unknown>[];
19
+ }
@@ -0,0 +1,51 @@
1
+ import { applyStoreFilter } from './types.js';
2
+ export class MemoryStore {
3
+ data = new Map();
4
+ name;
5
+ constructor(name) {
6
+ this.name = name;
7
+ }
8
+ async get(key) {
9
+ return this.data.get(key) ?? null;
10
+ }
11
+ async set(key, value) {
12
+ this.data.set(key, { ...value });
13
+ }
14
+ async bulkSet(records) {
15
+ for (const { key, value } of records) {
16
+ this.data.set(key, { ...value });
17
+ }
18
+ }
19
+ async update(key, value) {
20
+ const existing = this.data.get(key);
21
+ if (existing) {
22
+ this.data.set(key, { ...existing, ...value });
23
+ }
24
+ else {
25
+ this.data.set(key, value);
26
+ }
27
+ }
28
+ async delete(key) {
29
+ this.data.delete(key);
30
+ }
31
+ async list(filter) {
32
+ return applyStoreFilter(Array.from(this.data.values()), filter);
33
+ }
34
+ async count(filter) {
35
+ // Apply only the where clause for counting (ignore limit/offset)
36
+ const filtered = applyStoreFilter(Array.from(this.data.values()), {
37
+ where: filter?.where,
38
+ });
39
+ return filtered.length;
40
+ }
41
+ async clear() {
42
+ this.data.clear();
43
+ }
44
+ // For debugging
45
+ size() {
46
+ return this.data.size;
47
+ }
48
+ dump() {
49
+ return Array.from(this.data.values());
50
+ }
51
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,157 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest';
2
+ import { MemoryStore } from './memory.js';
3
+ describe('MemoryStore', () => {
4
+ let store;
5
+ beforeEach(() => {
6
+ store = new MemoryStore('test');
7
+ });
8
+ describe('set and get', () => {
9
+ it('stores and retrieves a value', async () => {
10
+ await store.set('key1', { id: '1', name: 'Test' });
11
+ const result = await store.get('key1');
12
+ expect(result).toEqual({ id: '1', name: 'Test' });
13
+ });
14
+ it('returns null for non-existent key', async () => {
15
+ const result = await store.get('non-existent');
16
+ expect(result).toBeNull();
17
+ });
18
+ it('stores a copy of the value (not reference)', async () => {
19
+ const original = { id: '1', name: 'Test' };
20
+ await store.set('key1', original);
21
+ original.name = 'Modified';
22
+ const result = await store.get('key1');
23
+ expect(result?.name).toBe('Test');
24
+ });
25
+ it('overwrites existing value', async () => {
26
+ await store.set('key1', { id: '1', name: 'First' });
27
+ await store.set('key1', { id: '1', name: 'Second' });
28
+ const result = await store.get('key1');
29
+ expect(result?.name).toBe('Second');
30
+ });
31
+ });
32
+ describe('update', () => {
33
+ it('merges partial values with existing', async () => {
34
+ await store.set('key1', { id: '1', name: 'Test', status: 'active' });
35
+ await store.update('key1', { status: 'inactive' });
36
+ const result = await store.get('key1');
37
+ expect(result).toEqual({ id: '1', name: 'Test', status: 'inactive' });
38
+ });
39
+ it('creates new record if key does not exist', async () => {
40
+ await store.update('new-key', { id: '1', name: 'New' });
41
+ const result = await store.get('new-key');
42
+ expect(result).toEqual({ id: '1', name: 'New' });
43
+ });
44
+ it('preserves unmodified fields', async () => {
45
+ await store.set('key1', { a: 1, b: 2, c: 3 });
46
+ await store.update('key1', { b: 20 });
47
+ const result = await store.get('key1');
48
+ expect(result).toEqual({ a: 1, b: 20, c: 3 });
49
+ });
50
+ });
51
+ describe('delete', () => {
52
+ it('removes a record', async () => {
53
+ await store.set('key1', { id: '1' });
54
+ await store.delete('key1');
55
+ const result = await store.get('key1');
56
+ expect(result).toBeNull();
57
+ });
58
+ it('handles deleting non-existent key', async () => {
59
+ // Should not throw
60
+ await expect(store.delete('non-existent')).resolves.not.toThrow();
61
+ });
62
+ });
63
+ describe('list', () => {
64
+ beforeEach(async () => {
65
+ await store.set('1', { id: '1', name: 'Alice', age: 30, status: 'active' });
66
+ await store.set('2', { id: '2', name: 'Bob', age: 25, status: 'inactive' });
67
+ await store.set('3', { id: '3', name: 'Charlie', age: 35, status: 'active' });
68
+ await store.set('4', { id: '4', name: 'Diana', age: 28, status: 'active' });
69
+ await store.set('5', { id: '5', name: 'Eve', age: 32, status: 'inactive' });
70
+ });
71
+ it('returns all records when no filter', async () => {
72
+ const results = await store.list();
73
+ expect(results).toHaveLength(5);
74
+ });
75
+ it('filters by where clause', async () => {
76
+ const results = await store.list({ where: { status: 'active' } });
77
+ expect(results).toHaveLength(3);
78
+ expect(results.every((r) => r.status === 'active')).toBe(true);
79
+ });
80
+ it('filters by multiple where conditions', async () => {
81
+ const results = await store.list({
82
+ where: { status: 'active', age: 30 },
83
+ });
84
+ expect(results).toHaveLength(1);
85
+ expect(results[0].name).toBe('Alice');
86
+ });
87
+ it('applies limit', async () => {
88
+ const results = await store.list({ limit: 3 });
89
+ expect(results).toHaveLength(3);
90
+ });
91
+ it('applies offset', async () => {
92
+ const allResults = await store.list();
93
+ const offsetResults = await store.list({ offset: 2 });
94
+ expect(offsetResults).toHaveLength(3);
95
+ expect(offsetResults[0]).toEqual(allResults[2]);
96
+ });
97
+ it('applies limit and offset together', async () => {
98
+ const allResults = await store.list();
99
+ const results = await store.list({ offset: 1, limit: 2 });
100
+ expect(results).toHaveLength(2);
101
+ expect(results[0]).toEqual(allResults[1]);
102
+ expect(results[1]).toEqual(allResults[2]);
103
+ });
104
+ it('combines where, limit, and offset', async () => {
105
+ const results = await store.list({
106
+ where: { status: 'active' },
107
+ offset: 1,
108
+ limit: 1,
109
+ });
110
+ expect(results).toHaveLength(1);
111
+ expect(results[0].status).toBe('active');
112
+ });
113
+ it('returns empty array when where matches nothing', async () => {
114
+ const results = await store.list({ where: { status: 'unknown' } });
115
+ expect(results).toEqual([]);
116
+ });
117
+ });
118
+ describe('clear', () => {
119
+ it('removes all records', async () => {
120
+ await store.set('1', { id: '1' });
121
+ await store.set('2', { id: '2' });
122
+ await store.set('3', { id: '3' });
123
+ await store.clear();
124
+ const results = await store.list();
125
+ expect(results).toHaveLength(0);
126
+ });
127
+ });
128
+ describe('size', () => {
129
+ it('returns 0 for empty store', () => {
130
+ expect(store.size()).toBe(0);
131
+ });
132
+ it('returns correct count', async () => {
133
+ await store.set('1', { id: '1' });
134
+ await store.set('2', { id: '2' });
135
+ expect(store.size()).toBe(2);
136
+ });
137
+ it('updates after delete', async () => {
138
+ await store.set('1', { id: '1' });
139
+ await store.set('2', { id: '2' });
140
+ await store.delete('1');
141
+ expect(store.size()).toBe(1);
142
+ });
143
+ });
144
+ describe('dump', () => {
145
+ it('returns all values as array', async () => {
146
+ await store.set('1', { id: '1', name: 'First' });
147
+ await store.set('2', { id: '2', name: 'Second' });
148
+ const dump = store.dump();
149
+ expect(dump).toHaveLength(2);
150
+ expect(dump).toContainEqual({ id: '1', name: 'First' });
151
+ expect(dump).toContainEqual({ id: '2', name: 'Second' });
152
+ });
153
+ it('returns empty array for empty store', () => {
154
+ expect(store.dump()).toEqual([]);
155
+ });
156
+ });
157
+ });
@@ -0,0 +1,55 @@
1
+ import type { StoreAdapter, StoreFilter } from './types.js';
2
+ export interface PostgRESTOptions {
3
+ /** Base URL for the PostgREST API (e.g., https://xxx.supabase.co/rest/v1) */
4
+ url: string;
5
+ /** API key for authentication */
6
+ apiKey: string;
7
+ /** Table name */
8
+ table: string;
9
+ /** Primary key column (default: 'id') */
10
+ primaryKey?: string;
11
+ /** Optional schema (for Supabase, typically 'public') */
12
+ schema?: string;
13
+ }
14
+ /**
15
+ * PostgREST-compatible store adapter.
16
+ * Works with Supabase, standalone PostgREST, or any PostgREST-compatible API.
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * const store = new PostgRESTStore({
21
+ * url: 'https://xxx.supabase.co/rest/v1',
22
+ * apiKey: process.env.SUPABASE_ANON_KEY,
23
+ * table: 'users',
24
+ * });
25
+ * ```
26
+ */
27
+ export declare class PostgRESTStore implements StoreAdapter {
28
+ private options;
29
+ private baseUrl;
30
+ private headers;
31
+ private primaryKey;
32
+ constructor(options: PostgRESTOptions);
33
+ get(key: string): Promise<Record<string, unknown> | null>;
34
+ set(key: string, value: Record<string, unknown>): Promise<void>;
35
+ update(key: string, value: Partial<Record<string, unknown>>): Promise<void>;
36
+ delete(key: string): Promise<void>;
37
+ list(filter?: StoreFilter): Promise<Record<string, unknown>[]>;
38
+ clear(): Promise<void>;
39
+ /**
40
+ * Bulk insert records (more efficient than individual sets)
41
+ */
42
+ bulkInsert(records: Record<string, unknown>[]): Promise<void>;
43
+ /**
44
+ * Count records matching a filter
45
+ */
46
+ count(filter?: StoreFilter): Promise<number>;
47
+ private parseError;
48
+ }
49
+ /**
50
+ * Error class for PostgREST operations
51
+ */
52
+ export declare class PostgRESTError extends Error {
53
+ statusCode: number;
54
+ constructor(message: string, statusCode: number);
55
+ }