@omnitron-dev/titan-lumen 0.0.0-stage → 0.4.6

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 (47) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +250 -2
  3. package/dist/access/access-filter.service.d.ts +25 -0
  4. package/dist/access/access-filter.service.d.ts.map +1 -0
  5. package/dist/access/access-filter.service.js +61 -0
  6. package/dist/access/access-filter.service.js.map +1 -0
  7. package/dist/constants.d.ts +3 -0
  8. package/dist/constants.d.ts.map +1 -0
  9. package/dist/constants.js +3 -0
  10. package/dist/constants.js.map +1 -0
  11. package/dist/corpus-files.d.ts +12 -0
  12. package/dist/corpus-files.d.ts.map +1 -0
  13. package/dist/corpus-files.js +114 -0
  14. package/dist/corpus-files.js.map +1 -0
  15. package/dist/index.d.ts +10 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +8 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/live/live-edit.service.d.ts +47 -0
  20. package/dist/live/live-edit.service.d.ts.map +1 -0
  21. package/dist/live/live-edit.service.js +253 -0
  22. package/dist/live/live-edit.service.js.map +1 -0
  23. package/dist/live/migration.d.ts +4 -0
  24. package/dist/live/migration.d.ts.map +1 -0
  25. package/dist/live/migration.js +38 -0
  26. package/dist/live/migration.js.map +1 -0
  27. package/dist/live/schema.d.ts +19 -0
  28. package/dist/live/schema.d.ts.map +1 -0
  29. package/dist/live/schema.js +2 -0
  30. package/dist/live/schema.js.map +1 -0
  31. package/dist/lumen.module.d.ts +8 -0
  32. package/dist/lumen.module.d.ts.map +1 -0
  33. package/dist/lumen.module.js +58 -0
  34. package/dist/lumen.module.js.map +1 -0
  35. package/dist/lumen.service.d.ts +32 -0
  36. package/dist/lumen.service.d.ts.map +1 -0
  37. package/dist/lumen.service.js +710 -0
  38. package/dist/lumen.service.js.map +1 -0
  39. package/dist/mcp.d.ts +16 -0
  40. package/dist/mcp.d.ts.map +1 -0
  41. package/dist/mcp.js +52 -0
  42. package/dist/mcp.js.map +1 -0
  43. package/dist/types.d.ts +119 -0
  44. package/dist/types.d.ts.map +1 -0
  45. package/dist/types.js +2 -0
  46. package/dist/types.js.map +1 -0
  47. package/package.json +54 -3
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 LuxQuant
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,251 @@
1
- # Temporary Holding Version
1
+ # @omnitron-dev/titan-lumen
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Lumen's Titan integration provides document reads, search, theorem graphs and
4
+ controlled authoring through Netron. The AST compiler remains in
5
+ `@omnitron-dev/lumen-ast`; transport authentication and invocation isolation
6
+ belong to Titan's Netron core.
7
+
8
+ ## Integrating with an existing Titan application
9
+
10
+ Register `LumenModule.forRoot({ docsDir: '/absolute/docs' })` in the application's
11
+ modules, or use `LumenModule.forRootAsync({ useFactory, inject })` for configuration
12
+ provided by DI. Resolve `LumenService` and expose it on the application's
13
+ Netron peer using `peer.exposeService(app.resolve(LumenService))`. The application owns its
14
+ listeners and authentication adapter; Lumen does not need a second HTTP auth
15
+ middleware.
16
+
17
+ The equivalent explicit composition is:
18
+
19
+ ```ts
20
+ import { LumenService } from '@omnitron-dev/titan-lumen';
21
+
22
+ const documents = new LumenService({
23
+ docsDir: '/absolute/docs',
24
+ enablePlayground: false,
25
+ });
26
+ await netron.peer.exposeService(documents);
27
+ ```
28
+
29
+ Configure authentication on the host Netron before opening its listeners. Use the
30
+ same application authentication/session provider as other Titan services. Never
31
+ accept a role list or `valid: true` in a request as proof of identity.
32
+
33
+ ## Published RPC surface
34
+
35
+ The service name is `Lumen`. `LumenServiceContract` describes its network methods:
36
+
37
+ | Method | Result | Authorization |
38
+ | --- | --- | --- |
39
+ | `search(query, options?)` | Ranked document hits | Public projection or verified caller's document access |
40
+ | `getDocument(path)` | Authorized Markdown or `null` | Document and node access |
41
+ | `getEditableDocument(path)` | Whole document, revision, hash and access or `null` | `lumen.documents.write` and access to every section |
42
+ | `getSymbolContract(symbol)` | Symbol contract or `null` | Configured source access, internal by default |
43
+ | `getTheoremGraph()` | Theorem nodes and edges | Authorized document projection |
44
+ | `executePlayground(code)` | Execution result or error | `lumen.playground.execute` |
45
+ | `saveLiveEdit(payload)` | Revision or conflict | `lumen.documents.write` |
46
+
47
+ Grant mutation permissions through the host application's role/permission system.
48
+ They are independent: an editor can receive document write access without code
49
+ execution access. The same requirements apply to HTTP and WebSocket; missing
50
+ identity returns 401 and insufficient permissions return 403.
51
+
52
+ Netron supplies the admitted identity through `getCurrentInvocationContext()`.
53
+ `session` is not part of the wire contract. Extra arguments cannot impersonate an
54
+ administrator. Search filters cannot override the verified caller's roles.
55
+ `getOptions()` and `getAccessFilterService()` are internal service APIs and are
56
+ not published RPC methods.
57
+
58
+ Direct in-process reads outside an admitted Netron invocation are anonymous.
59
+ Mutations require the corresponding admitted service method and a verified
60
+ identity. There is one service and one contract; there are no session arguments,
61
+ controller wrappers or alternate service tokens. Titan resolves the service and
62
+ its `AccessFilterService` and `LiveEditService` collaborators by their classes.
63
+ `LUMEN_OPTIONS_TOKEN` is the configuration token.
64
+
65
+ An empty corpus returns empty results. A missing configured corpus reports an
66
+ error on document, search and graph reads; it never substitutes demonstration
67
+ documents or symbol contracts.
68
+
69
+ ## Durable authoring
70
+
71
+ Without `liveEdit`, Lumen reads the filesystem and refuses edits. Enable authoring
72
+ with `liveEdit: { corpusId: 'product-docs', connectionName: 'default' }` and the
73
+ application's `TitanDatabaseModule`. Both synchronous and asynchronous Lumen
74
+ module providers resolve that module's `DATABASE_MANAGER`; they do not create
75
+ another pool. The connection must use PostgreSQL or file-backed SQLite with its
76
+ native driver installed. Choose a stable `corpusId` shared by every instance that
77
+ serves the same corpus. Different corpus IDs have independent document identities.
78
+
79
+ Apply the exported migration through the application's explicit migration workflow
80
+ before enabling authoring. Runtime dependency injection never creates tables:
81
+
82
+ ```ts
83
+ import { MigrationRunner } from '@omnitron-dev/titan-database/migration';
84
+ import { lumenAuthoringMigration, type LumenAuthoringDatabase } from '@omnitron-dev/titan-lumen';
85
+
86
+ const db = await databaseManager.getConnection<LumenAuthoringDatabase>('default');
87
+ await new MigrationRunner(db, [lumenAuthoringMigration], {
88
+ useTransactions: true,
89
+ enforceChecksums: true,
90
+ }).up();
91
+ ```
92
+
93
+ An unavailable database or missing migration is an error for enabled authoring,
94
+ including document reads, search and graph operations. No in-memory store or
95
+ stale filesystem substitute is used.
96
+
97
+ Editors first call `getEditableDocument('guides/start.pdx')`, then send its exact
98
+ `revision` and SHA-256 `hash` as `baseRevision` and `baseHash` with each edit:
99
+
100
+ ```ts
101
+ const document = await lumen.getEditableDocument('guides/start.pdx');
102
+ if (document) {
103
+ const saved = await lumen.saveLiveEdit({
104
+ docId: document.docId,
105
+ baseRevision: document.revision,
106
+ baseHash: document.hash,
107
+ mode: 'replace',
108
+ patch: updatedMarkdown,
109
+ });
110
+ if (saved.conflict) {
111
+ // Reload the current revision and let the editor reconcile its changes.
112
+ }
113
+ }
114
+ ```
115
+
116
+ Paths include their `.pdx`, `.md` or `.mdx` extension and must be canonical relative
117
+ paths. A disk document starts at revision zero. Its first successful edit stores
118
+ that exact seed, provenance and hash as history revision zero and the edit as
119
+ revision one, in the same transaction. The database then owns that path: later
120
+ filesystem changes cannot replace it. A new document uses revision zero and the
121
+ SHA-256 of the empty string as its base. `append`, `replace` and one-based
122
+ `line-range` edits preserve submitted whitespace.
123
+
124
+ Current content and append-only revision history commit atomically. A database
125
+ compare-and-swap checks both revision and hash. Competing editors receive a
126
+ conflict; SQLite snapshot conflicts and PostgreSQL serialization/deadlock errors
127
+ use TitanDatabase's bounded whole-transaction retry. Connection failures are
128
+ reported. Success and `live-edit` notifications occur only after commit. The
129
+ author comes from the verified Netron invocation; client `author` and `access`
130
+ fields are rejected. Existing content is authorized before conflict details are
131
+ returned; every section of edited content is authorized before commit. Reads and
132
+ edits verify stored revision numbers, UTF-8 byte lengths and content hashes;
133
+ corruption is an explicit error.
134
+
135
+ Accepted documents are never evicted. Writes default to 256 KiB per patch and
136
+ 2 MiB per document, configurable up to 8 MiB. `readCorpus` checks entry and byte
137
+ metadata in one consistent database snapshot before reading content, then the
138
+ service merges unowned filesystem documents within the remaining corpus budget.
139
+ Document reads, search and graph therefore share the same durable source.
140
+ Internal `getHistory` requires an authenticated actor and access to both the
141
+ current document and each returned revision; it defaults to 50 versions and
142
+ 8 MiB per page, with hard bounds of 100 versions and 64 MiB. History is not an
143
+ additional public RPC.
144
+
145
+ ## MCP on the existing Netron HTTP host
146
+
147
+ `LumenMcpAdapter` uses the official MCP SDK for protocol negotiation, schemas and
148
+ errors. It translates tool/resource calls into the host's native Netron HTTP
149
+ entry point in process. Authorization headers and the host-supplied connection
150
+ address enter the same authentication, permission and rate-limit path as RPC.
151
+ The adapter has no listener, authentication callback or mutable caller session.
152
+
153
+ ```ts
154
+ import { HttpNativeServer } from '@omnitron-dev/titan/netron/transport/http';
155
+ import { LumenMcpAdapter } from '@omnitron-dev/titan-lumen';
156
+
157
+ let mcp: LumenMcpAdapter;
158
+ const http = new HttpNativeServer({
159
+ host: '127.0.0.1',
160
+ port: 8081,
161
+ customRoutes: [async (request, { remoteAddress }) =>
162
+ new URL(request.url).pathname === '/mcp'
163
+ ? mcp.handleRequest(request, remoteAddress)
164
+ : null],
165
+ });
166
+ http.setPeer(netron.peer);
167
+ mcp = new LumenMcpAdapter({
168
+ http,
169
+ resources: ['guides/start.pdx'],
170
+ allowedHostnames: ['localhost', '127.0.0.1', '[::1]'],
171
+ allowedOriginHostnames: ['localhost', '127.0.0.1', '[::1]'],
172
+ });
173
+ await http.listen();
174
+ ```
175
+
176
+ The adapter publishes `search_corpus`, `get_document`, `lumen_inspect_ast` and
177
+ `lumen://corpus/{path}` resources. Resource lists contain only documents readable
178
+ by the caller; every read re-enters the service, so no independent content store
179
+ can bypass document access. Tool schemas reject undeclared role/session fields.
180
+
181
+ The endpoint uses stateless Streamable HTTP with JSON responses. POST is the
182
+ only accepted method; it does not open persistent SSE sessions. The default
183
+ body limit is 1 MiB. Configure host/origin hostname allowlists for the actual
184
+ deployment; they default to loopback names. Set `rpcPath` when the host changes
185
+ its native invocation route. The host owns TLS, authentication and shutdown.
186
+ See the [official TypeScript MCP SDK](https://github.com/modelcontextprotocol/typescript-sdk).
187
+
188
+ ## Qualification status
189
+
190
+ Server playground calls use the shared Lumen WASM interpreter in a fresh Node
191
+ Worker with an empty environment. Netron checks `lumen.playground.execute`
192
+ before admitting a call. At most four workers run per service; excess calls
193
+ receive 429, and capacity is released only after worker termination. The outer
194
+ five-second timer includes initialization. Execution, memory, source and result
195
+ limits are enforced by `@omnitron-dev/lumen-ast/execution`; no `node:vm` or host
196
+ JavaScript fallback is used. Keep `enablePlayground: false` when execution is
197
+ unnecessary; public read access never grants execution permission.
198
+
199
+ The 0.4.0 audit is in progress. Do not infer that a test with a PDF header,
200
+ hydration flag or mock request establishes production readiness. The repository's
201
+ `audit/LUMEN-0.4.0.md` records release qualification work. Durable authoring is
202
+ covered by real SQLite and PostgreSQL transactions, independent connection races, commit failure,
203
+ restart, native RPC and shared document/search/graph tests. The isolated execution engine has
204
+ local negative tests through actual Netron, including memory exhaustion,
205
+ constructor escapes and concurrent admission. Browser and final package checks
206
+ remain part of the release gate. Nothing in this package's audit
207
+ activates changes on running Omnitron controllers or applications.
208
+
209
+ ## Corpus bounds and source visibility
210
+
211
+ Filesystem documents use canonical relative paths. Symlinks, hidden paths,
212
+ `node_modules` and generated `dist` trees are excluded. A missing document is
213
+ `null`; a missing configured directory, invalid UTF-8 or failed read is an error.
214
+ Search and theorem graphs consume the same complete, deterministic inventory.
215
+ The default inventory budget is 10,000 entries, 64 MiB and 32 directory levels;
216
+ `corpusLimits` configures these bounds. Documents are limited to 5 MiB and source
217
+ files to 2 MiB. Exceeding a bound fails the request rather than returning a
218
+ successful partial corpus. Search accepts limits from 0 to 100 and queries of
219
+ at most 4 KiB.
220
+
221
+ Each service admits at most four concurrent corpus reads, independently of its
222
+ four playground workers. Read admission covers the complete operation through
223
+ authorization and projection; excess calls receive 429 and can retry after the
224
+ current operations finish. An overload is an error, not an empty search result.
225
+
226
+ ## PostgreSQL qualification
227
+
228
+ The dedicated `test:postgres` target exercises the built package on a real
229
+ PostgreSQL 16 container through TitanDatabase and Kysera. Build the SDK first,
230
+ then set `LUMEN_POSTGRES_DATA_ROOT` to an existing absolute directory reserved
231
+ for disposable test data. On this development Mac, use the mounted AGI workspace.
232
+ The regular unit target excludes this container suite; the explicit PostgreSQL
233
+ target fails if Docker, PostgreSQL or test storage is unavailable.
234
+
235
+ The suite creates its own Docker network, loopback port and data directory.
236
+ The existing DockerTestManager enforces 1 CPU, 512 MiB of memory and 128 processes
237
+ from container creation; tests inspect the actual Docker configuration. Cleanup
238
+ owns only these resources, and the global orphan reaper is disabled for this run.
239
+ No configured application database is used.
240
+
241
+ Qualification covers migration and checksum enforcement, races between eight
242
+ independent PostgreSQL connections, rollback when a deferred constraint rejects
243
+ COMMIT, canonical transaction retry after SQLSTATE `40001`, and history/source
244
+ hashes after restarting the isolated PostgreSQL server. PostgreSQL durability
245
+ settings remain enabled throughout the run.
246
+
247
+ `sourceAccess` and `knowledgeBaseAccess` default to `internal`, independently of
248
+ public Markdown. Set either to `public` only when that entire configured source
249
+ is intended for anonymous readers. Per-entry and section restrictions still
250
+ apply to knowledge-base search results. A failed KB query or source scan is
251
+ reported explicitly; it does not silently fall back to a different corpus.
@@ -0,0 +1,25 @@
1
+ import { type DocumentAccessOptions, type LumenDocumentAst } from '@omnitron-dev/lumen-ast';
2
+ export declare function extractDocumentMetadata(content: string): {
3
+ id?: string;
4
+ title?: string;
5
+ access: string;
6
+ };
7
+ export declare function extractDocumentAccess(content: string): string;
8
+ export declare class AccessFilterService {
9
+ private readonly options;
10
+ constructor(options?: DocumentAccessOptions);
11
+ isAllowed(userRoles: string[], accessLevel?: unknown): boolean;
12
+ filterSections<T extends {
13
+ access?: string;
14
+ level?: string;
15
+ }>(sections: T[], userRoles: string[]): T[];
16
+ projectContent(content: string, userRoles: string[], explicitAccess?: string): {
17
+ allowed: boolean;
18
+ ast: LumenDocumentAst;
19
+ strippedAny: boolean;
20
+ content: string | null;
21
+ };
22
+ filterRawMarkdownContent(content: string, userRoles: string[], explicitAccess?: string): string | null;
23
+ filterAst(ast: LumenDocumentAst, userRoles: string[]): LumenDocumentAst;
24
+ }
25
+ //# sourceMappingURL=access-filter.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"access-filter.service.d.ts","sourceRoot":"","sources":["../../src/access/access-filter.service.ts"],"names":[],"mappings":"AACA,OAAO,EAML,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACtB,MAAM,yBAAyB,CAAC;AAEjC,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAUxG;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAE7D;AAED,qBACa,mBAAmB;IAClB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAApC,YAA6B,OAAO,GAAE,qBAA0B,EAAI;IAEpE,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,WAAW,CAAC,EAAE,OAAO,GAAG,OAAO,CAE7D;IAED,cAAc,CAAC,CAAC,SAAS;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,CAGrG;IAED,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,cAAc,CAAC,EAAE,MAAM;;;;;MAc3E;IAED,wBAAwB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAErG;IAED,SAAS,CAAC,GAAG,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,gBAAgB,CAEtE;CACF"}
@@ -0,0 +1,61 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ import { Injectable } from '@omnitron-dev/titan';
11
+ import { createDocumentAccessPredicate, projectDocumentAccess, parseDocumentSync, parseSafeFrontmatter, toCleanMarkdown, } from '@omnitron-dev/lumen-ast';
12
+ export function extractDocumentMetadata(content) {
13
+ const parsed = parseSafeFrontmatter(content);
14
+ const { id, title, access } = parsed.frontmatter;
15
+ return {
16
+ id,
17
+ title,
18
+ access: parsed.diagnostics.some((diagnostic) => diagnostic.severity === 'error')
19
+ ? 'restricted'
20
+ : (access ?? 'public'),
21
+ };
22
+ }
23
+ export function extractDocumentAccess(content) {
24
+ return extractDocumentMetadata(content).access;
25
+ }
26
+ let AccessFilterService = class AccessFilterService {
27
+ constructor(options = {}) {
28
+ this.options = options;
29
+ }
30
+ isAllowed(userRoles, accessLevel) {
31
+ return createDocumentAccessPredicate(userRoles, this.options)(accessLevel);
32
+ }
33
+ filterSections(sections, userRoles) {
34
+ const allows = createDocumentAccessPredicate(userRoles, this.options);
35
+ return sections.filter((section) => allows(Object.hasOwn(section, 'access') ? section.access : section.level));
36
+ }
37
+ projectContent(content, userRoles, explicitAccess) {
38
+ const allows = createDocumentAccessPredicate(userRoles, this.options);
39
+ const result = projectDocumentAccess(parseDocumentSync(content), (access) => allows(explicitAccess) && allows(access));
40
+ return {
41
+ ...result,
42
+ content: result.allowed
43
+ ? result.strippedAny
44
+ ? toCleanMarkdown(result.ast, { includeFrontmatter: true })
45
+ : content
46
+ : null,
47
+ };
48
+ }
49
+ filterRawMarkdownContent(content, userRoles, explicitAccess) {
50
+ return this.projectContent(content, userRoles, explicitAccess).content;
51
+ }
52
+ filterAst(ast, userRoles) {
53
+ return projectDocumentAccess(ast, createDocumentAccessPredicate(userRoles, this.options)).ast;
54
+ }
55
+ };
56
+ AccessFilterService = __decorate([
57
+ Injectable(),
58
+ __metadata("design:paramtypes", [Object])
59
+ ], AccessFilterService);
60
+ export { AccessFilterService };
61
+ //# sourceMappingURL=access-filter.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"access-filter.service.js","sourceRoot":"","sources":["../../src/access/access-filter.service.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EACL,6BAA6B,EAC7B,qBAAqB,EACrB,iBAAiB,EACjB,oBAAoB,EACpB,eAAe,GAGhB,MAAM,yBAAyB,CAAC;AAEjC,MAAM,UAAU,uBAAuB,CAAC,OAAe;IACrD,MAAM,MAAM,GAAG,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAC7C,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC,WAAW,CAAC;IACjD,OAAO;QACL,EAAE;QACF,KAAK;QACL,MAAM,EAAE,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,QAAQ,KAAK,OAAO,CAAC;YAC9E,CAAC,CAAC,YAAY;YACd,CAAC,CAAC,CAAC,MAAM,IAAI,QAAQ,CAAC;KACzB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,OAAe;IACnD,OAAO,uBAAuB,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC;AACjD,CAAC;AAGM,IAAM,mBAAmB,GAAzB,MAAM,mBAAmB;IAC9B,YAA6B,OAAO,GAA0B,EAAE;uBAAnC,OAAO;IAA+B,CAAC;IAEpE,SAAS,CAAC,SAAmB,EAAE,WAAqB;QAClD,OAAO,6BAA6B,CAAC,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,WAAW,CAAC,CAAC;IAC7E,CAAC;IAED,cAAc,CAAgD,QAAa,EAAE,SAAmB;QAC9F,MAAM,MAAM,GAAG,6BAA6B,CAAC,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QACtE,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;IACjH,CAAC;IAED,cAAc,CAAC,OAAe,EAAE,SAAmB,EAAE,cAAuB;QAC1E,MAAM,MAAM,GAAG,6BAA6B,CAAC,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QACtE,MAAM,MAAM,GAAG,qBAAqB,CAClC,iBAAiB,CAAC,OAAO,CAAC,EAC1B,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,cAAc,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,CACrD,CAAC;QACF,OAAO;YACL,GAAG,MAAM;YACT,OAAO,EAAE,MAAM,CAAC,OAAO;gBACrB,CAAC,CAAC,MAAM,CAAC,WAAW;oBAClB,CAAC,CAAC,eAAe,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,kBAAkB,EAAE,IAAI,EAAE,CAAC;oBAC3D,CAAC,CAAC,OAAO;gBACX,CAAC,CAAC,IAAI;SACT,CAAC;IACJ,CAAC;IAED,wBAAwB,CAAC,OAAe,EAAE,SAAmB,EAAE,cAAuB;QACpF,OAAO,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,SAAS,EAAE,cAAc,CAAC,CAAC,OAAO,CAAC;IACzE,CAAC;IAED,SAAS,CAAC,GAAqB,EAAE,SAAmB;QAClD,OAAO,qBAAqB,CAAC,GAAG,EAAE,6BAA6B,CAAC,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;IAChG,CAAC;CACF,CAAA;AAnCY,mBAAmB;IAD/B,UAAU,EAAE;;GACA,mBAAmB,CAmC/B"}
@@ -0,0 +1,3 @@
1
+ export declare const LUMEN_OPTIONS_TOKEN: unique symbol;
2
+ export declare const DEFAULT_DOCS_DIR = "docs";
3
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,mBAAmB,eAAoC,CAAC;AACrE,eAAO,MAAM,gBAAgB,SAAS,CAAC"}
@@ -0,0 +1,3 @@
1
+ export const LUMEN_OPTIONS_TOKEN = Symbol.for('titan:lumen:options');
2
+ export const DEFAULT_DOCS_DIR = 'docs';
3
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;AACrE,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CAAC"}
@@ -0,0 +1,12 @@
1
+ export interface CorpusScanLimits {
2
+ maxEntries?: number;
3
+ maxBytes?: number;
4
+ maxDepth?: number;
5
+ }
6
+ export declare function isCanonicalDocumentPath(path: unknown, extensions: ReadonlySet<string>): path is string;
7
+ export declare function readCorpusFile(root: string, path: string, maxBytes: number): Promise<string | null>;
8
+ export declare function scanCorpusFiles(root: string, extensions: ReadonlySet<string>, maxFileBytes: number, options?: CorpusScanLimits, ownedPaths?: ReadonlySet<string>): Promise<Array<{
9
+ path: string;
10
+ content: string;
11
+ }>>;
12
+ //# sourceMappingURL=corpus-files.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"corpus-files.d.ts","sourceRoot":"","sources":["../src/corpus-files.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,gBAAgB;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAOD,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,IAAI,IAAI,MAAM,CAMtG;AAGD,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAsCzG;AAGD,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,WAAW,CAAC,MAAM,CAAC,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,EACnG,UAAU,GAAE,WAAW,CAAC,MAAM,CAAa,GAC1C,OAAO,CAAC,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC,CAiCnD"}
@@ -0,0 +1,114 @@
1
+ import { constants } from 'node:fs';
2
+ import { lstat, open, opendir, realpath } from 'node:fs/promises';
3
+ import { extname, isAbsolute, relative, resolve, sep } from 'node:path';
4
+ function inside(root, target) {
5
+ const path = relative(root, target);
6
+ return path !== '' && path !== '..' && !path.startsWith(`..${sep}`) && !isAbsolute(path);
7
+ }
8
+ export function isCanonicalDocumentPath(path, extensions) {
9
+ if (typeof path !== 'string' || !path || Buffer.byteLength(path) > 1024 || /[\\%:?#]/.test(path) || isAbsolute(path))
10
+ return false;
11
+ if ([...path].some(char => char.charCodeAt(0) < 32 || char.charCodeAt(0) === 127))
12
+ return false;
13
+ if (path.split('/').some((part) => !part || part.startsWith('.') || ['~', 'node_modules', 'dist'].includes(part)))
14
+ return false;
15
+ const extension = extname(path).toLowerCase();
16
+ return !extension || extensions.has(extension);
17
+ }
18
+ export async function readCorpusFile(root, path, maxBytes) {
19
+ if (!Number.isSafeInteger(maxBytes) || maxBytes < 0)
20
+ throw new TypeError('Corpus byte budget must be a non-negative safe integer');
21
+ const target = resolve(root, path);
22
+ if (!inside(root, target))
23
+ return null;
24
+ let current = root;
25
+ for (const component of relative(root, target).split(sep)) {
26
+ current = resolve(current, component);
27
+ try {
28
+ if ((await lstat(current)).isSymbolicLink())
29
+ return null;
30
+ }
31
+ catch (error) {
32
+ if (error.code === 'ENOENT')
33
+ return null;
34
+ throw error;
35
+ }
36
+ }
37
+ const before = await lstat(target);
38
+ if (!before.isFile())
39
+ return null;
40
+ if (before.size > maxBytes)
41
+ throw new RangeError(`Corpus file exceeds ${maxBytes} bytes`);
42
+ const canonical = await realpath(target);
43
+ if (!inside(root, canonical))
44
+ return null;
45
+ const file = await open(canonical, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
46
+ try {
47
+ const opened = await file.stat();
48
+ if (!opened.isFile() || opened.dev !== before.dev || opened.ino !== before.ino)
49
+ throw new Error('Corpus file changed before reading');
50
+ const buffer = Buffer.alloc(Math.min(opened.size + 1, maxBytes + 1));
51
+ let offset = 0;
52
+ while (offset < buffer.length) {
53
+ const { bytesRead } = await file.read(buffer, offset, buffer.length - offset, offset);
54
+ if (!bytesRead)
55
+ break;
56
+ offset += bytesRead;
57
+ }
58
+ const after = await file.stat();
59
+ const currentPath = await lstat(target);
60
+ if (offset > maxBytes || after.size > maxBytes)
61
+ throw new RangeError(`Corpus file exceeds ${maxBytes} bytes`);
62
+ if (after.size !== offset || after.mtimeMs !== opened.mtimeMs || after.ctimeMs !== opened.ctimeMs ||
63
+ currentPath.ino !== opened.ino || currentPath.dev !== opened.dev || currentPath.isSymbolicLink() ||
64
+ await realpath(target) !== canonical)
65
+ throw new Error('Corpus file changed while reading');
66
+ return new TextDecoder('utf-8', { fatal: true, ignoreBOM: true }).decode(buffer.subarray(0, offset));
67
+ }
68
+ finally {
69
+ await file.close();
70
+ }
71
+ }
72
+ export async function scanCorpusFiles(root, extensions, maxFileBytes, options = {}, ownedPaths = new Set()) {
73
+ const maxEntries = options.maxEntries ?? 10_000;
74
+ const maxBytes = options.maxBytes ?? 64 * 1024 * 1024;
75
+ const maxDepth = options.maxDepth ?? 32;
76
+ if (!Number.isSafeInteger(maxFileBytes) || maxFileBytes < 1)
77
+ throw new TypeError('Corpus file limit must be a positive safe integer');
78
+ if ([maxEntries, maxDepth].some((value) => !Number.isSafeInteger(value) || value < 1) ||
79
+ !Number.isSafeInteger(maxBytes) || maxBytes < 0) {
80
+ throw new TypeError('Corpus entry/depth limits must be positive and byte budget non-negative safe integers');
81
+ }
82
+ const files = [];
83
+ const directories = [{ path: root, depth: 0 }];
84
+ let entries = 0;
85
+ let bytes = 0;
86
+ while (directories.length) {
87
+ const directory = directories.pop();
88
+ if (directory.depth > maxDepth)
89
+ throw new RangeError(`Corpus directory depth exceeds ${maxDepth}`);
90
+ if (await realpath(directory.path) !== directory.path)
91
+ throw new Error('Corpus directory changed during scan');
92
+ for await (const entry of await opendir(directory.path)) {
93
+ if (++entries > maxEntries)
94
+ throw new RangeError(`Corpus entries exceed ${maxEntries}`);
95
+ if (entry.name.startsWith('.') || ['node_modules', 'dist'].includes(entry.name) || entry.isSymbolicLink())
96
+ continue;
97
+ const target = resolve(directory.path, entry.name);
98
+ if (entry.isDirectory())
99
+ directories.push({ path: target, depth: directory.depth + 1 });
100
+ else if (entry.isFile() && extensions.has(extname(entry.name).toLowerCase())) {
101
+ const path = relative(root, target).split(sep).join('/');
102
+ if (ownedPaths.has(path))
103
+ continue;
104
+ const content = await readCorpusFile(root, path, Math.min(maxFileBytes, maxBytes - bytes));
105
+ if (content === null)
106
+ throw new Error('Corpus file changed during scan');
107
+ bytes += Buffer.byteLength(content);
108
+ files.push({ path, content });
109
+ }
110
+ }
111
+ }
112
+ return files.sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
113
+ }
114
+ //# sourceMappingURL=corpus-files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"corpus-files.js","sourceRoot":"","sources":["../src/corpus-files.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ export { LumenModule } from './lumen.module.js';
2
+ export { LumenService } from './lumen.service.js';
3
+ export { LUMEN_OPTIONS_TOKEN, DEFAULT_DOCS_DIR } from './constants.js';
4
+ export { AccessFilterService, extractDocumentAccess, extractDocumentMetadata, } from './access/access-filter.service.js';
5
+ export { LiveEditService, DEFAULT_MAX_PATCH_BYTES, DEFAULT_MAX_DOCUMENT_BYTES, type DocumentState, type DocumentRevision, } from './live/live-edit.service.js';
6
+ export { lumenAuthoringMigration } from './live/migration.js';
7
+ export type { LumenAuthoringDatabase } from './live/schema.js';
8
+ export { LumenMcpAdapter, type LumenMcpOptions } from './mcp.js';
9
+ export type * from './types.js';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACvE,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,uBAAuB,GACxB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EACL,eAAe,EACf,uBAAuB,EACvB,0BAA0B,EAC1B,KAAK,aAAa,EAClB,KAAK,gBAAgB,GACtB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,YAAY,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,KAAK,eAAe,EAAE,MAAM,UAAU,CAAC;AACjE,mBAAmB,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ export { LumenModule } from './lumen.module.js';
2
+ export { LumenService } from './lumen.service.js';
3
+ export { LUMEN_OPTIONS_TOKEN, DEFAULT_DOCS_DIR } from './constants.js';
4
+ export { AccessFilterService, extractDocumentAccess, extractDocumentMetadata, } from './access/access-filter.service.js';
5
+ export { LiveEditService, DEFAULT_MAX_PATCH_BYTES, DEFAULT_MAX_DOCUMENT_BYTES, } from './live/live-edit.service.js';
6
+ export { lumenAuthoringMigration } from './live/migration.js';
7
+ export { LumenMcpAdapter } from './mcp.js';
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACvE,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,uBAAuB,GACxB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EACL,eAAe,EACf,uBAAuB,EACvB,0BAA0B,GAG3B,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAE9D,OAAO,EAAE,eAAe,EAAwB,MAAM,UAAU,CAAC"}
@@ -0,0 +1,47 @@
1
+ import { EventEmitter } from 'node:events';
2
+ import { type DatabaseManager } from '@omnitron-dev/titan-database';
3
+ import { AccessFilterService } from '../access/access-filter.service.js';
4
+ import type { LiveEditActor, LiveEditPayload, LiveEditResult, LiveEditServiceOptions } from '../types.js';
5
+ export declare const DEFAULT_MAX_PATCH_BYTES = 262144;
6
+ export declare const DEFAULT_MAX_DOCUMENT_BYTES = 2097152;
7
+ export interface DocumentState {
8
+ docId: string;
9
+ content: string;
10
+ revision: number;
11
+ hash: string;
12
+ updatedAt: number;
13
+ access: string;
14
+ byteLength: number;
15
+ author: string;
16
+ origin: 'disk' | 'created';
17
+ seedHash: string;
18
+ }
19
+ export type DocumentRevision = Omit<DocumentState, 'origin' | 'seedHash'>;
20
+ export declare class LiveEditService extends EventEmitter {
21
+ private readonly database?;
22
+ private readonly options?;
23
+ private readonly maxPatchBytes;
24
+ private readonly maxDocumentBytes;
25
+ private readonly accessFilter;
26
+ constructor(options?: LiveEditServiceOptions, database?: DatabaseManager | undefined, accessFilter?: AccessFilterService);
27
+ isEnabled(): boolean;
28
+ private connection;
29
+ validatePayload(payload: unknown): {
30
+ valid: boolean;
31
+ error?: string;
32
+ };
33
+ private authorize;
34
+ getDocument(docId: string): Promise<DocumentState | undefined>;
35
+ readCorpus(limits?: {
36
+ maxEntries?: number;
37
+ maxBytes?: number;
38
+ }): Promise<DocumentState[]>;
39
+ getHistory(docId: string, actor: LiveEditActor, options?: {
40
+ beforeRevision?: number;
41
+ limit?: number;
42
+ maxBytes?: number;
43
+ }): Promise<DocumentRevision[]>;
44
+ private conflict;
45
+ applyEdit(payload: LiveEditPayload, actor: LiveEditActor, initialContent?: string): Promise<LiveEditResult>;
46
+ }
47
+ //# sourceMappingURL=live-edit.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"live-edit.service.d.ts","sourceRoot":"","sources":["../../src/live/live-edit.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAK3C,OAAO,EAAwB,KAAK,eAAe,EAAiC,MAAM,8BAA8B,CAAC;AACzH,OAAO,EAAE,mBAAmB,EAAyB,MAAM,oCAAoC,CAAC;AAChG,OAAO,KAAK,EAAE,aAAa,EAA+B,eAAe,EAAE,cAAc,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAIvI,eAAO,MAAM,uBAAuB,SAAU,CAAC;AAC/C,eAAO,MAAM,0BAA0B,UAAY,CAAC;AAQpD,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;CAClB;AACD,MAAM,MAAM,gBAAgB,GAAG,IAAI,CAAC,aAAa,EAAE,QAAQ,GAAG,UAAU,CAAC,CAAC;AA2B1E,qBACa,eAAgB,SAAQ,YAAY;IAMD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC;IALxE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAmC;IAC5D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAsB;IAEnD,YAAY,OAAO,CAAC,EAAE,sBAAsB,EAAmB,QAAQ,CAAC,EAAE,eAAe,YAAA,EAAE,YAAY,CAAC,EAAE,mBAAmB,EAQ5H;IAED,SAAS,IAAI,OAAO,CAAuC;YAE7C,UAAU;IAYxB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAgBpE;IAED,OAAO,CAAC,SAAS;IAWX,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,CAMnE;IAGK,UAAU,CAAC,MAAM,GAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAgBlG;IAEK,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,OAAO,GAAE;QAAE,cAAc,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CA4B/J;YAEa,QAAQ;IAOhB,SAAS,CAAC,OAAO,EAAE,eAAe,EAAE,KAAK,EAAE,aAAa,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CA0DhH;CACF"}