@hatk/hatk 0.0.1-alpha.11 → 0.0.1-alpha.111

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 (224) hide show
  1. package/dist/adapter.d.ts +19 -0
  2. package/dist/adapter.d.ts.map +1 -0
  3. package/dist/adapter.js +113 -0
  4. package/dist/backfill.d.ts +36 -2
  5. package/dist/backfill.d.ts.map +1 -1
  6. package/dist/backfill.js +153 -37
  7. package/dist/car.js +1 -1
  8. package/dist/classifiers.d.ts +228 -0
  9. package/dist/classifiers.d.ts.map +1 -0
  10. package/dist/classifiers.js +688 -0
  11. package/dist/cli.js +248 -1043
  12. package/dist/config.d.ts +179 -1
  13. package/dist/config.d.ts.map +1 -1
  14. package/dist/config.js +78 -8
  15. package/dist/database/adapter-factory.d.ts +6 -0
  16. package/dist/database/adapter-factory.d.ts.map +1 -0
  17. package/dist/database/adapter-factory.js +20 -0
  18. package/dist/database/adapters/duckdb-search.d.ts +12 -0
  19. package/dist/database/adapters/duckdb-search.d.ts.map +1 -0
  20. package/dist/database/adapters/duckdb-search.js +27 -0
  21. package/dist/database/adapters/duckdb.d.ts +25 -0
  22. package/dist/database/adapters/duckdb.d.ts.map +1 -0
  23. package/dist/database/adapters/duckdb.js +161 -0
  24. package/dist/database/adapters/sqlite-search.d.ts +16 -0
  25. package/dist/database/adapters/sqlite-search.d.ts.map +1 -0
  26. package/dist/database/adapters/sqlite-search.js +76 -0
  27. package/dist/database/adapters/sqlite.d.ts +18 -0
  28. package/dist/database/adapters/sqlite.d.ts.map +1 -0
  29. package/dist/database/adapters/sqlite.js +88 -0
  30. package/dist/database/db.d.ts +290 -0
  31. package/dist/database/db.d.ts.map +1 -0
  32. package/dist/database/db.js +2073 -0
  33. package/dist/database/dialect.d.ts +45 -0
  34. package/dist/database/dialect.d.ts.map +1 -0
  35. package/dist/database/dialect.js +72 -0
  36. package/dist/{fts.d.ts → database/fts.d.ts} +7 -0
  37. package/dist/database/fts.d.ts.map +1 -0
  38. package/dist/{fts.js → database/fts.js} +122 -34
  39. package/dist/database/index.d.ts +7 -0
  40. package/dist/database/index.d.ts.map +1 -0
  41. package/dist/database/index.js +6 -0
  42. package/dist/database/ports.d.ts +50 -0
  43. package/dist/database/ports.d.ts.map +1 -0
  44. package/dist/database/ports.js +1 -0
  45. package/dist/database/schema.d.ts +91 -0
  46. package/dist/database/schema.d.ts.map +1 -0
  47. package/dist/{schema.js → database/schema.js} +153 -54
  48. package/dist/dev-entry.d.ts +8 -0
  49. package/dist/dev-entry.d.ts.map +1 -0
  50. package/dist/dev-entry.js +131 -0
  51. package/dist/feeds.d.ts +26 -8
  52. package/dist/feeds.d.ts.map +1 -1
  53. package/dist/feeds.js +72 -16
  54. package/dist/hooks.d.ts +102 -0
  55. package/dist/hooks.d.ts.map +1 -0
  56. package/dist/hooks.js +186 -0
  57. package/dist/hydrate.d.ts +55 -6
  58. package/dist/hydrate.d.ts.map +1 -1
  59. package/dist/hydrate.js +21 -20
  60. package/dist/indexer.d.ts +194 -3
  61. package/dist/indexer.d.ts.map +1 -1
  62. package/dist/indexer.js +627 -92
  63. package/dist/jetstream.d.ts +118 -0
  64. package/dist/jetstream.d.ts.map +1 -0
  65. package/dist/jetstream.js +238 -0
  66. package/dist/labels.d.ts +36 -0
  67. package/dist/labels.d.ts.map +1 -1
  68. package/dist/labels.js +71 -6
  69. package/dist/lex-types.d.ts +2 -0
  70. package/dist/lex-types.d.ts.map +1 -1
  71. package/dist/lexicon-resolve.d.ts.map +1 -1
  72. package/dist/lexicon-resolve.js +27 -112
  73. package/dist/lexicons/com/atproto/label/defs.json +75 -0
  74. package/dist/lexicons/com/atproto/moderation/defs.json +30 -0
  75. package/dist/lexicons/com/atproto/repo/strongRef.json +21 -0
  76. package/dist/lexicons/dev/hatk/applyWrites.json +87 -0
  77. package/dist/lexicons/dev/hatk/createRecord.json +40 -0
  78. package/dist/lexicons/dev/hatk/createReport.json +48 -0
  79. package/dist/lexicons/dev/hatk/deleteRecord.json +25 -0
  80. package/dist/lexicons/dev/hatk/describeCollections.json +41 -0
  81. package/dist/lexicons/dev/hatk/describeFeeds.json +29 -0
  82. package/dist/lexicons/dev/hatk/describeLabels.json +45 -0
  83. package/dist/lexicons/dev/hatk/getFeed.json +30 -0
  84. package/dist/lexicons/dev/hatk/getPreferences.json +19 -0
  85. package/dist/lexicons/dev/hatk/getRecord.json +26 -0
  86. package/dist/lexicons/dev/hatk/getRecords.json +32 -0
  87. package/dist/lexicons/dev/hatk/putPreference.json +28 -0
  88. package/dist/lexicons/dev/hatk/putRecord.json +41 -0
  89. package/dist/lexicons/dev/hatk/searchRecords.json +32 -0
  90. package/dist/lexicons/dev/hatk/uploadBlob.json +23 -0
  91. package/dist/logger.d.ts +29 -0
  92. package/dist/logger.d.ts.map +1 -1
  93. package/dist/logger.js +29 -0
  94. package/dist/main.js +212 -75
  95. package/dist/mst.d.ts +15 -0
  96. package/dist/mst.d.ts.map +1 -1
  97. package/dist/mst.js +13 -0
  98. package/dist/oauth/db.d.ts +27 -1
  99. package/dist/oauth/db.d.ts.map +1 -1
  100. package/dist/oauth/db.js +80 -19
  101. package/dist/oauth/discovery.d.ts +10 -0
  102. package/dist/oauth/discovery.d.ts.map +1 -1
  103. package/dist/oauth/discovery.js +54 -2
  104. package/dist/oauth/server.d.ts +131 -1
  105. package/dist/oauth/server.d.ts.map +1 -1
  106. package/dist/oauth/server.js +504 -39
  107. package/dist/oauth/service-describe.d.ts +42 -0
  108. package/dist/oauth/service-describe.d.ts.map +1 -0
  109. package/dist/oauth/service-describe.js +108 -0
  110. package/dist/oauth/session.d.ts +32 -0
  111. package/dist/oauth/session.d.ts.map +1 -0
  112. package/dist/oauth/session.js +131 -0
  113. package/dist/opengraph.d.ts +10 -0
  114. package/dist/opengraph.d.ts.map +1 -1
  115. package/dist/opengraph.js +94 -42
  116. package/dist/pds-proxy.d.ts +92 -0
  117. package/dist/pds-proxy.d.ts.map +1 -0
  118. package/dist/pds-proxy.js +396 -0
  119. package/dist/private-collections.d.ts +13 -0
  120. package/dist/private-collections.d.ts.map +1 -0
  121. package/dist/private-collections.js +27 -0
  122. package/dist/push.d.ts +54 -0
  123. package/dist/push.d.ts.map +1 -0
  124. package/dist/push.js +367 -0
  125. package/dist/renderer.d.ts +27 -0
  126. package/dist/renderer.d.ts.map +1 -0
  127. package/dist/renderer.js +46 -0
  128. package/dist/resolve-hatk.d.ts +6 -0
  129. package/dist/resolve-hatk.d.ts.map +1 -0
  130. package/dist/resolve-hatk.js +20 -0
  131. package/dist/response.d.ts +16 -0
  132. package/dist/response.d.ts.map +1 -0
  133. package/dist/response.js +69 -0
  134. package/dist/scanner.d.ts +22 -0
  135. package/dist/scanner.d.ts.map +1 -0
  136. package/dist/scanner.js +92 -0
  137. package/dist/seed.d.ts +19 -0
  138. package/dist/seed.d.ts.map +1 -1
  139. package/dist/seed.js +43 -4
  140. package/dist/server-init.d.ts +8 -0
  141. package/dist/server-init.d.ts.map +1 -0
  142. package/dist/server-init.js +75 -0
  143. package/dist/server.d.ts +46 -3
  144. package/dist/server.d.ts.map +1 -1
  145. package/dist/server.js +1137 -649
  146. package/dist/setup.d.ts +28 -1
  147. package/dist/setup.d.ts.map +1 -1
  148. package/dist/setup.js +50 -3
  149. package/dist/spaces/blob.d.ts +112 -0
  150. package/dist/spaces/blob.d.ts.map +1 -0
  151. package/dist/spaces/blob.js +460 -0
  152. package/dist/spaces/credential.d.ts +82 -0
  153. package/dist/spaces/credential.d.ts.map +1 -0
  154. package/dist/spaces/credential.js +220 -0
  155. package/dist/spaces/engine.d.ts +97 -0
  156. package/dist/spaces/engine.d.ts.map +1 -0
  157. package/dist/spaces/engine.js +563 -0
  158. package/dist/spaces/guard.d.ts +41 -0
  159. package/dist/spaces/guard.d.ts.map +1 -0
  160. package/dist/spaces/guard.js +79 -0
  161. package/dist/spaces/identity.d.ts +37 -0
  162. package/dist/spaces/identity.d.ts.map +1 -0
  163. package/dist/spaces/identity.js +93 -0
  164. package/dist/spaces/index.d.ts +56 -0
  165. package/dist/spaces/index.d.ts.map +1 -0
  166. package/dist/spaces/index.js +134 -0
  167. package/dist/spaces/notify.d.ts +60 -0
  168. package/dist/spaces/notify.d.ts.map +1 -0
  169. package/dist/spaces/notify.js +128 -0
  170. package/dist/spaces/records.d.ts +37 -0
  171. package/dist/spaces/records.d.ts.map +1 -0
  172. package/dist/spaces/records.js +63 -0
  173. package/dist/spaces/signature.d.ts +36 -0
  174. package/dist/spaces/signature.d.ts.map +1 -0
  175. package/dist/spaces/signature.js +83 -0
  176. package/dist/spaces/store.d.ts +51 -0
  177. package/dist/spaces/store.d.ts.map +1 -0
  178. package/dist/spaces/store.js +99 -0
  179. package/dist/spaces/uri.d.ts +60 -0
  180. package/dist/spaces/uri.d.ts.map +1 -0
  181. package/dist/spaces/uri.js +97 -0
  182. package/dist/spaces/verify.d.ts +46 -0
  183. package/dist/spaces/verify.d.ts.map +1 -0
  184. package/dist/spaces/verify.js +94 -0
  185. package/dist/spaces/viewer.d.ts +51 -0
  186. package/dist/spaces/viewer.d.ts.map +1 -0
  187. package/dist/spaces/viewer.js +167 -0
  188. package/dist/spaces/visibility.d.ts +57 -0
  189. package/dist/spaces/visibility.d.ts.map +1 -0
  190. package/dist/spaces/visibility.js +81 -0
  191. package/dist/templates/feed.tpl +18 -0
  192. package/dist/templates/hook.tpl +5 -0
  193. package/dist/templates/label.tpl +15 -0
  194. package/dist/templates/og.tpl +17 -0
  195. package/dist/templates/seed.tpl +11 -0
  196. package/dist/templates/setup.tpl +5 -0
  197. package/dist/templates/test-feed.tpl +19 -0
  198. package/dist/templates/test-xrpc.tpl +19 -0
  199. package/dist/templates/xrpc.tpl +53 -0
  200. package/dist/test.d.ts +3 -1
  201. package/dist/test.d.ts.map +1 -1
  202. package/dist/test.js +60 -36
  203. package/dist/views.js +1 -1
  204. package/dist/vite-plugin.d.ts +1 -1
  205. package/dist/vite-plugin.d.ts.map +1 -1
  206. package/dist/vite-plugin.js +254 -66
  207. package/dist/xrpc.d.ts +143 -12
  208. package/dist/xrpc.d.ts.map +1 -1
  209. package/dist/xrpc.js +282 -55
  210. package/package.json +29 -9
  211. package/public/admin-auth.js +271 -306
  212. package/public/admin.html +652 -61
  213. package/dist/db.d.ts +0 -135
  214. package/dist/db.d.ts.map +0 -1
  215. package/dist/db.js +0 -1365
  216. package/dist/fts.d.ts.map +0 -1
  217. package/dist/oauth/hooks.d.ts +0 -10
  218. package/dist/oauth/hooks.d.ts.map +0 -1
  219. package/dist/oauth/hooks.js +0 -40
  220. package/dist/schema.d.ts +0 -51
  221. package/dist/schema.d.ts.map +0 -1
  222. package/dist/test-browser.d.ts +0 -14
  223. package/dist/test-browser.d.ts.map +0 -1
  224. package/dist/test-browser.js +0 -26
package/dist/config.d.ts CHANGED
@@ -20,28 +20,206 @@ export interface OAuthConfig {
20
20
  issuer: string;
21
21
  scopes: string[];
22
22
  clients: OAuthClientConfig[];
23
+ cookieName?: string;
24
+ /**
25
+ * Scopes requested only from PDSes that serve `whenMethod`, discovered
26
+ * through `community.lexicon.service.describe` before the authorization
27
+ * request is pushed.
28
+ *
29
+ * For optional protocol features — permissioned spaces (proposal 0016) being
30
+ * the first — where asking every PDS for the scope would put a permission on
31
+ * the consent screen that most servers cannot honor.
32
+ */
33
+ conditionalScopes?: ConditionalScopeConfig[];
34
+ }
35
+ export interface ConditionalScopeConfig {
36
+ /** An XRPC method whose presence means the PDS implements the feature. */
37
+ whenMethod: string;
38
+ scopes: string[];
39
+ }
40
+ /**
41
+ * A record that names another repo worth tracking.
42
+ *
43
+ * Backfill and the stream find repos by what they write into the signal
44
+ * collections. Some repos matter because a record points at them instead: a
45
+ * community's roster names its members, and a member's own repo — their
46
+ * profile first of all — is what puts a name and a face on everything they
47
+ * write. Each reference says which collection carries such a record and which
48
+ * field holds the DID.
49
+ */
50
+ export interface RepoReference {
51
+ collection: string;
52
+ /** Dot path to the DID inside the record, or `$rkey` when the record key is the DID. */
53
+ field: string;
23
54
  }
24
55
  export interface BackfillConfig {
25
56
  signalCollections?: string[];
26
57
  repos?: string[];
58
+ /** Records that name repos to track; see {@link RepoReference}. */
59
+ references?: RepoReference[];
27
60
  fullNetwork: boolean;
28
61
  parallelism: number;
29
62
  fetchTimeout: number;
30
63
  maxRetries: number;
64
+ /**
65
+ * PDS hosts whose repos are never imported. A repo hosted on one is marked
66
+ * active with its handle and no records, so nothing queues it again, and
67
+ * whatever it writes from then on still arrives through the stream. For a
68
+ * bridge such as Bridgy Fed (`atproto.brid.gy`), whose repos mirror another
69
+ * network and can run to tens of thousands of records apiece.
70
+ */
71
+ skipPds?: string[];
72
+ }
73
+ export interface ApnsPushConfig {
74
+ keyFile: string;
75
+ keyId: string;
76
+ teamId: string;
77
+ bundleId: string;
78
+ production?: boolean;
79
+ }
80
+ /**
81
+ * Firebase Cloud Messaging, for Android devices.
82
+ *
83
+ * `keyFile` is a Google service-account JSON carrying the Firebase Messaging
84
+ * role, resolved relative to the config file like the APNs key. The project id
85
+ * comes from that file unless it's overridden here.
86
+ */
87
+ export interface FcmPushConfig {
88
+ keyFile: string;
89
+ projectId?: string;
90
+ }
91
+ /** Either transport may stand alone; a platform without one simply isn't sent to. */
92
+ export interface PushConfig {
93
+ apns?: ApnsPushConfig;
94
+ fcm?: FcmPushConfig;
95
+ }
96
+ export interface CdnConfig {
97
+ url: string;
98
+ key: string;
99
+ salt: string;
100
+ }
101
+ /**
102
+ * Indexing permissioned spaces (proposal 0016).
103
+ *
104
+ * A space's records never reach a firehose, so they are read from the
105
+ * authority and each writer's own host instead. Doing that at all requires
106
+ * `oauth`: hatk is a member of nothing and borrows a signed-in member's
107
+ * delegation to get a credential, so with no sessions there is no way in.
108
+ *
109
+ * Absent by default. An instance that does not set this indexes nothing from a
110
+ * space and serves no space rows, which is what every existing deployment does
111
+ * today.
112
+ */
113
+ export interface SpacesConfig {
114
+ /**
115
+ * Space type NSIDs to follow. A space of any other type is refused rather
116
+ * than indexed, so a lexicon vendored for reference cannot quietly become a
117
+ * subscription.
118
+ */
119
+ types: string[];
120
+ /** Space refs to follow at boot, for the spaces an instance always wants. */
121
+ watch?: string[];
122
+ /**
123
+ * Seconds between reconcile sweeps. Write notices are best-effort — the
124
+ * reference host forwards each once and logs the failure — so the sweep is
125
+ * what makes sync correct rather than merely prompt.
126
+ */
127
+ reconcileInterval?: number;
128
+ /**
129
+ * This instance's own DID, for receiving write notices — e.g.
130
+ * `did:web:appview.example.com`. hatk serves the matching document at
131
+ * `/.well-known/did.json`, so the DID has to resolve to this origin.
132
+ *
133
+ * Without it the sweep is the only thing that notices a write, which is
134
+ * correct but no faster than the interval. The DID publishes no signing key:
135
+ * notices are only ever verified here, never signed.
136
+ */
137
+ serviceDid?: string;
138
+ /**
139
+ * The service entry notices are delivered to, named in the DID document and
140
+ * in every registration. Its own fragment rather than the bare DID, so a
141
+ * notice cannot be confused with one addressed to an account.
142
+ */
143
+ serviceFragment?: string;
144
+ }
145
+ export interface JetstreamConfig {
146
+ /** Instance base URL, e.g. `wss://jetstream.us-east.bsky.network`. */
147
+ url: string;
31
148
  }
32
149
  export interface HatkConfig {
33
150
  relay: string;
151
+ /**
152
+ * Additional `subscribeRepos` sources tailed alongside `relay`, each with
153
+ * its own cursor. For repos whose PDS is not behind the relay — a dev stack
154
+ * with more than one PDS, a self-hosted network — tail the PDS directly
155
+ * instead of standing up a relay to merge the streams. Ignored when
156
+ * `jetstream` is set (Jetstream is the sole source then).
157
+ */
158
+ relays: string[];
159
+ /**
160
+ * Consume the stream from a Jetstream v2 instance instead of `relay`.
161
+ *
162
+ * Jetstream filters server-side and delivers records as decoded JSON, so an
163
+ * AppView tracking a few collections stops paying to decode the whole
164
+ * network. Not every deployment has one — a local PDS or self-hosted relay
165
+ * won't — so `relay` stays the default.
166
+ */
167
+ jetstream: JetstreamConfig | null;
34
168
  plc: string;
35
169
  port: number;
170
+ cdn: CdnConfig | null;
171
+ databaseEngine: 'duckdb' | 'sqlite';
36
172
  database: string;
37
173
  publicDir: string | null;
38
174
  collections: string[];
175
+ privateCollections: string[];
39
176
  backfill: BackfillConfig;
40
177
  ftsRebuildInterval: number;
41
178
  oauth: OAuthConfig | null;
179
+ push: PushConfig | null;
42
180
  admins: string[];
181
+ adminToken: {
182
+ token: string;
183
+ actor: string;
184
+ } | null;
185
+ spaces: SpacesConfig | null;
186
+ clef: ClefConfig | null;
187
+ }
188
+ /**
189
+ * Clef access for the classifier system, through Workers AI. Without it,
190
+ * classifiers load but no scan can run. The account and token are read from
191
+ * `CLOUDFLARE_ACCOUNT_ID` and `CLOUDFLARE_AI_TOKEN` in preference to the config
192
+ * file, so neither need be committed. The token needs Workers AI access and
193
+ * nothing else; `CLOUDFLARE_API_TOKEN` is deliberately not read, since on a host
194
+ * that also manages DNS it is the token with zone write.
195
+ */
196
+ export interface ClefConfig {
197
+ accountId: string;
198
+ apiToken: string;
199
+ /** `clef` or `clef-flash`. Thresholds tuned on one do not carry to the other. */
200
+ model: 'clef' | 'clef-flash';
201
+ /**
202
+ * Score new content as the indexer writes it. Only content new since this was
203
+ * first switched on is scored; the library that existed before then is left
204
+ * to a manual scan.
205
+ */
206
+ scoreOnIndex: boolean;
43
207
  }
208
+ /** Input type for defineConfig — fields that have defaults are optional. */
209
+ export type HatkConfigInput = Partial<Omit<HatkConfig, 'oauth' | 'backfill' | 'push' | 'cdn' | 'jetstream' | 'spaces' | 'clef' | 'adminToken'>> & {
210
+ cdn?: CdnConfig | null;
211
+ oauth?: (Partial<OAuthConfig> & {
212
+ clients: OAuthClientConfig[];
213
+ }) | null;
214
+ backfill?: Partial<BackfillConfig>;
215
+ push?: PushConfig | null;
216
+ jetstream?: JetstreamConfig | null;
217
+ spaces?: SpacesConfig | null;
218
+ clef?: Partial<ClefConfig> | null;
219
+ };
220
+ /** Identity function that provides type inference for hatk config files. */
221
+ export declare function defineConfig(config: HatkConfigInput): HatkConfigInput;
44
222
  /** Derive HTTP URL from relay WebSocket URL (ws://host → http://host) */
45
223
  export declare function relayHttpUrl(relay: string): string;
46
- export declare function loadConfig(configPath: string): HatkConfig;
224
+ export declare function loadConfig(configPath: string): Promise<HatkConfig>;
47
225
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAA;IACrC,KAAK,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,CAAA;IACnC,cAAc,EAAE,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAA;IAC1C,OAAO,CAAC,EAAE,WAAW,EAAE,CAAA;CACxB;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,aAAa,EAAE,MAAM,EAAE,CAAA;IACvB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,OAAO,EAAE,iBAAiB,EAAE,CAAA;CAC7B;AAED,MAAM,WAAW,cAAc;IAC7B,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC5B,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;IAChB,WAAW,EAAE,OAAO,CAAA;IACpB,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,EAAE,MAAM,CAAA;IACpB,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,WAAW,EAAE,MAAM,EAAE,CAAA;IACrB,QAAQ,EAAE,cAAc,CAAA;IACxB,kBAAkB,EAAE,MAAM,CAAA;IAC1B,KAAK,EAAE,WAAW,GAAG,IAAI,CAAA;IACzB,MAAM,EAAE,MAAM,EAAE,CAAA;CACjB;AAED,yEAAyE;AACzE,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,UAAU,CAyCzD"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
package/dist/config.js CHANGED
@@ -1,35 +1,104 @@
1
- import { readFileSync } from 'node:fs';
1
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
+ });
6
+ }
7
+ return path;
8
+ };
2
9
  import { resolve, dirname } from 'node:path';
3
- import YAML from 'yaml';
10
+ import { existsSync } from 'node:fs';
11
+ /** Identity function that provides type inference for hatk config files. */
12
+ export function defineConfig(config) {
13
+ return config;
14
+ }
4
15
  /** Derive HTTP URL from relay WebSocket URL (ws://host → http://host) */
5
16
  export function relayHttpUrl(relay) {
6
17
  return relay.replace(/^ws(s?):\/\//, 'http$1://');
7
18
  }
8
- export function loadConfig(configPath) {
9
- const raw = readFileSync(configPath, 'utf-8');
10
- const parsed = YAML.parse(raw);
11
- const configDir = dirname(resolve(configPath));
19
+ export async function loadConfig(configPath) {
20
+ const resolved = resolve(configPath);
21
+ if (!existsSync(resolved)) {
22
+ console.error(`Config file not found: ${resolved}`);
23
+ console.error(`hatk now uses hatk.config.ts instead of config.yaml.`);
24
+ console.error(`Create a hatk.config.ts file or run 'hatk new' to scaffold a project.`);
25
+ process.exit(1);
26
+ }
27
+ const configDir = dirname(resolved);
28
+ let mod;
29
+ try {
30
+ mod = await import(__rewriteRelativeImportExtension(/* @vite-ignore */ resolved));
31
+ }
32
+ catch (err) {
33
+ console.error(`Failed to load config file: ${resolved}`);
34
+ console.error(err.message || err);
35
+ process.exit(1);
36
+ }
37
+ const parsed = mod.default || {};
12
38
  const backfillRaw = parsed.backfill || {};
13
39
  const env = process.env;
14
40
  const database = env.DATABASE || parsed.database;
15
41
  const config = {
16
42
  relay: env.RELAY || parsed.relay || 'ws://localhost:2583',
43
+ relays: env.RELAYS
44
+ ? env.RELAYS.split(',')
45
+ .map((s) => s.trim())
46
+ .filter(Boolean)
47
+ : parsed.relays || [],
48
+ jetstream: env.JETSTREAM_URL ? { url: env.JETSTREAM_URL } : parsed.jetstream || null,
17
49
  plc: env.DID_PLC_URL || parsed.plc || 'https://plc.directory',
18
50
  port: parseInt(env.PORT || '') || parsed.port || 3000,
51
+ databaseEngine: (env.DATABASE_ENGINE || parsed.databaseEngine || 'sqlite'),
19
52
  database: database ? resolve(configDir, database) : ':memory:',
20
- publicDir: parsed.public === false ? null : resolve(configDir, parsed.public || './public'),
53
+ publicDir: parsed.publicDir === null ? null : resolve(configDir, parsed.publicDir || './public'),
21
54
  collections: parsed.collections || [],
55
+ privateCollections: parsed.privateCollections || [],
22
56
  backfill: {
23
57
  signalCollections: backfillRaw.signalCollections || undefined,
58
+ references: backfillRaw.references || undefined,
24
59
  repos: env.BACKFILL_REPOS ? env.BACKFILL_REPOS.split(',').map((s) => s.trim()) : backfillRaw.repos || undefined,
25
60
  fullNetwork: env.BACKFILL_FULL_NETWORK ? env.BACKFILL_FULL_NETWORK === 'true' : backfillRaw.fullNetwork || false,
26
61
  parallelism: parseInt(env.BACKFILL_PARALLELISM || '') || backfillRaw.parallelism || 3,
27
62
  fetchTimeout: parseInt(env.BACKFILL_FETCH_TIMEOUT || '') || backfillRaw.fetchTimeout || 300,
28
63
  maxRetries: parseInt(env.BACKFILL_MAX_RETRIES || '') || backfillRaw.maxRetries || 5,
64
+ skipPds: backfillRaw.skipPds || undefined,
29
65
  },
30
- ftsRebuildInterval: parseInt(env.FTS_REBUILD_INTERVAL || '') || parsed.ftsRebuildInterval || 500,
66
+ ftsRebuildInterval: parseInt(env.FTS_REBUILD_INTERVAL || '') || parsed.ftsRebuildInterval || 5000,
67
+ cdn: env.CDN_URL && env.CDN_KEY && env.CDN_SALT
68
+ ? { url: env.CDN_URL, key: env.CDN_KEY, salt: env.CDN_SALT }
69
+ : parsed.cdn || null,
31
70
  oauth: null,
71
+ push: parsed.push || null,
32
72
  admins: env.ADMINS ? env.ADMINS.split(',').map((s) => s.trim()) : parsed.admins || [],
73
+ // Read from the environment only: it is a secret, and has no place in a
74
+ // committed config file.
75
+ adminToken: env.ADMIN_API_TOKEN ? { token: env.ADMIN_API_TOKEN, actor: env.ADMIN_API_ACTOR || 'admin-api' } : null,
76
+ clef: (() => {
77
+ const raw = parsed.clef;
78
+ const accountId = env.CLOUDFLARE_ACCOUNT_ID || raw?.accountId;
79
+ const apiToken = env.CLOUDFLARE_AI_TOKEN || raw?.apiToken;
80
+ if (!accountId || !apiToken)
81
+ return null;
82
+ return {
83
+ accountId,
84
+ apiToken,
85
+ model: env.CLEF_MODEL || raw?.model || 'clef',
86
+ scoreOnIndex: env.CLEF_SCORE_ON_INDEX ? env.CLEF_SCORE_ON_INDEX === 'true' : (raw?.scoreOnIndex ?? false),
87
+ };
88
+ })(),
89
+ spaces: parsed.spaces
90
+ ? {
91
+ types: parsed.spaces.types || [],
92
+ watch: env.SPACES_WATCH
93
+ ? env.SPACES_WATCH.split(',')
94
+ .map((s) => s.trim())
95
+ .filter(Boolean)
96
+ : parsed.spaces.watch || [],
97
+ reconcileInterval: parseInt(env.SPACES_RECONCILE_INTERVAL || '') || parsed.spaces.reconcileInterval || 300,
98
+ serviceDid: env.SPACES_SERVICE_DID || parsed.spaces.serviceDid || undefined,
99
+ serviceFragment: parsed.spaces.serviceFragment || 'atproto_space_syncer',
100
+ }
101
+ : null,
33
102
  };
34
103
  const oauthRaw = parsed.oauth;
35
104
  if (oauthRaw) {
@@ -37,6 +106,7 @@ export function loadConfig(configPath) {
37
106
  issuer: process.env.OAUTH_ISSUER || oauthRaw.issuer || `http://127.0.0.1:${config.port}`,
38
107
  scopes: oauthRaw.scopes || ['atproto'],
39
108
  clients: oauthRaw.clients || [],
109
+ conditionalScopes: oauthRaw.conditionalScopes || [],
40
110
  };
41
111
  }
42
112
  return config;
@@ -0,0 +1,6 @@
1
+ import type { DatabasePort, SearchPort } from './ports.ts';
2
+ export declare function createAdapter(engine: 'duckdb' | 'sqlite'): Promise<{
3
+ adapter: DatabasePort;
4
+ searchPort: SearchPort | null;
5
+ }>;
6
+ //# sourceMappingURL=adapter-factory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter-factory.d.ts","sourceRoot":"","sources":["../../src/database/adapter-factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAE1D,wBAAsB,aAAa,CAAC,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC;IACxE,OAAO,EAAE,YAAY,CAAA;IACrB,UAAU,EAAE,UAAU,GAAG,IAAI,CAAA;CAC9B,CAAC,CAmBD"}
@@ -0,0 +1,20 @@
1
+ export async function createAdapter(engine) {
2
+ switch (engine) {
3
+ case 'duckdb': {
4
+ const { DuckDBAdapter } = await import("./adapters/duckdb.js");
5
+ const { DuckDBSearchPort } = await import("./adapters/duckdb-search.js");
6
+ const adapter = new DuckDBAdapter();
7
+ const searchPort = new DuckDBSearchPort(adapter);
8
+ return { adapter, searchPort };
9
+ }
10
+ case 'sqlite': {
11
+ const { SQLiteAdapter } = await import("./adapters/sqlite.js");
12
+ const { SQLiteSearchPort } = await import("./adapters/sqlite-search.js");
13
+ const adapter = new SQLiteAdapter();
14
+ const searchPort = new SQLiteSearchPort(adapter);
15
+ return { adapter, searchPort };
16
+ }
17
+ default:
18
+ throw new Error(`Unsupported database engine: ${engine}`);
19
+ }
20
+ }
@@ -0,0 +1,12 @@
1
+ import type { SearchPort } from '../ports.ts';
2
+ import type { DatabasePort } from '../ports.ts';
3
+ export declare class DuckDBSearchPort implements SearchPort {
4
+ private port;
5
+ constructor(port: DatabasePort);
6
+ buildIndex(shadowTable: string, sourceQuery: string, searchColumns: string[]): Promise<void>;
7
+ search(shadowTable: string, query: string, searchColumns: string[], limit: number, offset: number): Promise<Array<{
8
+ uri: string;
9
+ score: number;
10
+ }>>;
11
+ }
12
+ //# sourceMappingURL=duckdb-search.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duckdb-search.d.ts","sourceRoot":"","sources":["../../../src/database/adapters/duckdb-search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAC7C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE/C,qBAAa,gBAAiB,YAAW,UAAU;IACrC,OAAO,CAAC,IAAI;gBAAJ,IAAI,EAAE,YAAY;IAEhC,UAAU,CAAC,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAiB5F,MAAM,CACV,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,EACb,aAAa,EAAE,MAAM,EAAE,EACvB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CASlD"}
@@ -0,0 +1,27 @@
1
+ export class DuckDBSearchPort {
2
+ port;
3
+ constructor(port) {
4
+ this.port = port;
5
+ }
6
+ async buildIndex(shadowTable, sourceQuery, searchColumns) {
7
+ // Create shadow table
8
+ await this.port.execute(`CREATE OR REPLACE TABLE ${shadowTable} AS ${sourceQuery}`, []);
9
+ // Drop existing index
10
+ try {
11
+ await this.port.execute(`PRAGMA drop_fts_index('${shadowTable}')`, []);
12
+ }
13
+ catch { }
14
+ // Build FTS index
15
+ const colList = searchColumns.map((c) => `'${c}'`).join(', ');
16
+ await this.port.execute(`PRAGMA create_fts_index('${shadowTable}', 'uri', ${colList}, stemmer='porter', stopwords='english', strip_accents=1, lower=1, overwrite=1)`, []);
17
+ }
18
+ async search(shadowTable, query, searchColumns, limit, offset) {
19
+ const ftsSchema = `fts_main_${shadowTable}`;
20
+ const sql = `SELECT uri, ${ftsSchema}.match_bm25(uri, $1) AS score
21
+ FROM ${shadowTable}
22
+ WHERE score IS NOT NULL
23
+ ORDER BY score DESC
24
+ LIMIT $2 OFFSET $3`;
25
+ return this.port.query(sql, [query, limit, offset]);
26
+ }
27
+ }
@@ -0,0 +1,25 @@
1
+ import type { DatabasePort, BulkInserter, Dialect } from '../ports.ts';
2
+ export declare class DuckDBAdapter implements DatabasePort {
3
+ dialect: Dialect;
4
+ private instance;
5
+ private writeCon;
6
+ private readCon;
7
+ private writeQueue;
8
+ private readQueue;
9
+ open(path: string): Promise<void>;
10
+ close(): void;
11
+ query<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<T[]>;
12
+ execute(sql: string, params?: unknown[]): Promise<void>;
13
+ executeMultiple(sql: string): Promise<void>;
14
+ beginTransaction(): Promise<void>;
15
+ commit(): Promise<void>;
16
+ rollback(): Promise<void>;
17
+ createBulkInserter(table: string, _columns: string[], _options?: {
18
+ onConflict?: 'ignore' | 'replace';
19
+ batchSize?: number;
20
+ }): Promise<BulkInserter>;
21
+ /** Enqueue a read or write operation for serialization */
22
+ private enqueue;
23
+ private bindParams;
24
+ }
25
+ //# sourceMappingURL=duckdb.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duckdb.d.ts","sourceRoot":"","sources":["../../../src/database/adapters/duckdb.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAEtE,qBAAa,aAAc,YAAW,YAAY;IAChD,OAAO,EAAE,OAAO,CAAW;IAE3B,OAAO,CAAC,QAAQ,CAAiB;IACjC,OAAO,CAAC,QAAQ,CAAiD;IACjE,OAAO,CAAC,OAAO,CAAiD;IAChE,OAAO,CAAC,UAAU,CAAoB;IACtC,OAAO,CAAC,SAAS,CAAoB;IAE/B,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMvC,KAAK,IAAI,IAAI;IAYP,KAAK,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAE,OAAO,EAAO,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;IAarF,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,GAAE,OAAO,EAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAY3D,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ3C,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;IAMjC,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAMvB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAMzB,kBAAkB,CACtB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAAE,EAClB,QAAQ,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GACnE,OAAO,CAAC,YAAY,CAAC;IAqCxB,0DAA0D;IAC1D,OAAO,CAAC,OAAO;IAkBf,OAAO,CAAC,UAAU;CAyBnB"}
@@ -0,0 +1,161 @@
1
+ import { DuckDBInstance } from '@duckdb/node-api';
2
+ export class DuckDBAdapter {
3
+ dialect = 'duckdb';
4
+ instance;
5
+ writeCon;
6
+ readCon;
7
+ writeQueue = Promise.resolve();
8
+ readQueue = Promise.resolve();
9
+ async open(path) {
10
+ this.instance = await DuckDBInstance.create(path === ':memory:' ? undefined : path);
11
+ this.writeCon = await this.instance.connect();
12
+ this.readCon = await this.instance.connect();
13
+ }
14
+ close() {
15
+ try {
16
+ this.readCon?.closeSync();
17
+ }
18
+ catch { }
19
+ try {
20
+ this.writeCon?.closeSync();
21
+ }
22
+ catch { }
23
+ try {
24
+ this.instance?.closeSync();
25
+ }
26
+ catch { }
27
+ }
28
+ async query(sql, params = []) {
29
+ return this.enqueue('read', async () => {
30
+ if (params.length === 0) {
31
+ const reader = await this.readCon.runAndReadAll(sql);
32
+ return reader.getRowObjects();
33
+ }
34
+ const prepared = await this.readCon.prepare(sql);
35
+ this.bindParams(prepared, params);
36
+ const reader = await prepared.runAndReadAll();
37
+ return reader.getRowObjects();
38
+ });
39
+ }
40
+ async execute(sql, params = []) {
41
+ return this.enqueue('write', async () => {
42
+ if (params.length === 0) {
43
+ await this.writeCon.run(sql);
44
+ return;
45
+ }
46
+ const prepared = await this.writeCon.prepare(sql);
47
+ this.bindParams(prepared, params);
48
+ await prepared.run();
49
+ });
50
+ }
51
+ async executeMultiple(sql) {
52
+ return this.enqueue('write', async () => {
53
+ for (const statement of sql.split(';').filter((s) => s.trim())) {
54
+ await this.writeCon.run(statement);
55
+ }
56
+ });
57
+ }
58
+ async beginTransaction() {
59
+ return this.enqueue('write', async () => {
60
+ await this.writeCon.run('BEGIN TRANSACTION');
61
+ });
62
+ }
63
+ async commit() {
64
+ return this.enqueue('write', async () => {
65
+ await this.writeCon.run('COMMIT');
66
+ });
67
+ }
68
+ async rollback() {
69
+ return this.enqueue('write', async () => {
70
+ await this.writeCon.run('ROLLBACK');
71
+ });
72
+ }
73
+ async createBulkInserter(table, _columns, _options) {
74
+ const appender = await this.writeCon.createAppender(table.replace(/"/g, ''));
75
+ return {
76
+ append(values) {
77
+ for (const value of values) {
78
+ if (value === null || value === undefined) {
79
+ appender.appendNull();
80
+ }
81
+ else if (typeof value === 'string') {
82
+ appender.appendVarchar(value);
83
+ }
84
+ else if (typeof value === 'number') {
85
+ if (Number.isInteger(value)) {
86
+ appender.appendInteger(value);
87
+ }
88
+ else {
89
+ appender.appendDouble(value);
90
+ }
91
+ }
92
+ else if (typeof value === 'boolean') {
93
+ appender.appendBoolean(value);
94
+ }
95
+ else if (typeof value === 'bigint') {
96
+ appender.appendBigInt(value);
97
+ }
98
+ else if (value instanceof Uint8Array) {
99
+ appender.appendBlob(value);
100
+ }
101
+ else {
102
+ appender.appendVarchar(String(value));
103
+ }
104
+ }
105
+ appender.endRow();
106
+ },
107
+ async flush() {
108
+ appender.flushSync();
109
+ },
110
+ async close() {
111
+ appender.flushSync();
112
+ appender.closeSync();
113
+ },
114
+ };
115
+ }
116
+ /** Enqueue a read or write operation for serialization */
117
+ enqueue(queue, fn) {
118
+ if (queue === 'write') {
119
+ const p = this.writeQueue.then(fn);
120
+ this.writeQueue = p.then(() => { }, () => { });
121
+ return p;
122
+ }
123
+ else {
124
+ const p = this.readQueue.then(fn);
125
+ this.readQueue = p.then(() => { }, () => { });
126
+ return p;
127
+ }
128
+ }
129
+ bindParams(prepared, params) {
130
+ for (let i = 0; i < params.length; i++) {
131
+ const idx = i + 1;
132
+ const value = params[i];
133
+ if (value === null || value === undefined) {
134
+ prepared.bindNull(idx);
135
+ }
136
+ else if (typeof value === 'string') {
137
+ prepared.bindVarchar(idx, value);
138
+ }
139
+ else if (typeof value === 'number') {
140
+ if (Number.isInteger(value)) {
141
+ prepared.bindInteger(idx, value);
142
+ }
143
+ else {
144
+ prepared.bindDouble(idx, value);
145
+ }
146
+ }
147
+ else if (typeof value === 'boolean') {
148
+ prepared.bindBoolean(idx, value);
149
+ }
150
+ else if (typeof value === 'bigint') {
151
+ prepared.bindBigInt(idx, value);
152
+ }
153
+ else if (value instanceof Uint8Array) {
154
+ prepared.bindBlob(idx, value);
155
+ }
156
+ else {
157
+ prepared.bindVarchar(idx, String(value));
158
+ }
159
+ }
160
+ }
161
+ }
@@ -0,0 +1,16 @@
1
+ import type { SearchPort } from '../ports.ts';
2
+ import type { DatabasePort } from '../ports.ts';
3
+ export declare class SQLiteSearchPort implements SearchPort {
4
+ private port;
5
+ constructor(port: DatabasePort);
6
+ indexExists(shadowTable: string): Promise<boolean>;
7
+ buildIndex(shadowTable: string, sourceQuery: string, searchColumns: string[]): Promise<void>;
8
+ updateIndex(shadowTable: string, uri: string, row: Record<string, string | null>, searchColumns: string[]): Promise<void>;
9
+ deleteFromIndex(shadowTable: string, uri: string, searchColumns: string[]): Promise<void>;
10
+ private _deleteFromFts;
11
+ search(shadowTable: string, query: string, _searchColumns: string[], limit: number, offset: number): Promise<Array<{
12
+ uri: string;
13
+ score: number;
14
+ }>>;
15
+ }
16
+ //# sourceMappingURL=sqlite-search.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite-search.d.ts","sourceRoot":"","sources":["../../../src/database/adapters/sqlite-search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAC7C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAY/C,qBAAa,gBAAiB,YAAW,UAAU;IACrC,OAAO,CAAC,IAAI;gBAAJ,IAAI,EAAE,YAAY;IAEhC,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAQlD,UAAU,CAAC,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAuB5F,WAAW,CACf,WAAW,EAAE,MAAM,EACnB,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,EAClC,aAAa,EAAE,MAAM,EAAE,GACtB,OAAO,CAAC,IAAI,CAAC;IA2BV,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;YAKjF,cAAc;IAatB,MAAM,CACV,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,EACb,cAAc,EAAE,MAAM,EAAE,EACxB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAWlD"}