@undefineds.co/drizzle-solid 0.2.13 → 0.3.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.
- package/README.md +141 -250
- package/dist/core/ast-to-sparql.d.ts +1 -1
- package/dist/core/ast-to-sparql.js +1 -1
- package/dist/core/comunica-patch.d.ts +1 -6
- package/dist/core/comunica-patch.d.ts.map +1 -1
- package/dist/core/comunica-patch.js +16 -19
- package/dist/core/comunica-patch.js.map +1 -1
- package/dist/core/execution/sparql-strategy.d.ts +9 -37
- package/dist/core/execution/sparql-strategy.d.ts.map +1 -1
- package/dist/core/execution/sparql-strategy.js +44 -148
- package/dist/core/execution/sparql-strategy.js.map +1 -1
- package/dist/core/execution/strategy-factory.d.ts +7 -20
- package/dist/core/execution/strategy-factory.d.ts.map +1 -1
- package/dist/core/execution/strategy-factory.js +2 -17
- package/dist/core/execution/strategy-factory.js.map +1 -1
- package/dist/core/pod-database.d.ts +3 -3
- package/dist/core/pod-database.d.ts.map +1 -1
- package/dist/core/pod-database.js +37 -37
- package/dist/core/pod-database.js.map +1 -1
- package/dist/core/pod-dialect.d.ts +2 -0
- package/dist/core/pod-dialect.d.ts.map +1 -1
- package/dist/core/pod-dialect.js +4 -2
- package/dist/core/pod-dialect.js.map +1 -1
- package/dist/core/query-builders/select-query-builder.d.ts +2 -2
- package/dist/core/query-builders/select-query-builder.d.ts.map +1 -1
- package/dist/core/query-builders/select-query-builder.js +10 -10
- package/dist/core/query-builders/select-query-builder.js.map +1 -1
- package/dist/core/schema/columns.d.ts +6 -6
- package/dist/core/schema/columns.d.ts.map +1 -1
- package/dist/core/schema/columns.js +18 -18
- package/dist/core/schema/columns.js.map +1 -1
- package/dist/core/schema/defs.d.ts +4 -4
- package/dist/core/schema/defs.d.ts.map +1 -1
- package/dist/core/schema/pod-table.js +2 -2
- package/dist/core/schema/pod-table.js.map +1 -1
- package/dist/core/services/pod-services.d.ts +7 -5
- package/dist/core/services/pod-services.d.ts.map +1 -1
- package/dist/core/services/pod-services.js +7 -5
- package/dist/core/services/pod-services.js.map +1 -1
- package/dist/core/shape/generator.js +3 -3
- package/dist/core/shape/generator.js.map +1 -1
- package/dist/core/shape/manager.js +5 -5
- package/dist/core/shape/manager.js.map +1 -1
- package/dist/core/sparql/builder/expression-builder.d.ts +5 -5
- package/dist/core/sparql/builder/expression-builder.d.ts.map +1 -1
- package/dist/core/sparql/builder/expression-builder.js +15 -15
- package/dist/core/sparql/builder/expression-builder.js.map +1 -1
- package/dist/core/sparql/builder/select-builder.d.ts +1 -1
- package/dist/core/sparql/builder/select-builder.js +1 -1
- package/dist/core/sparql/builder/update-builder.d.ts +1 -1
- package/dist/core/sparql/builder/update-builder.d.ts.map +1 -1
- package/dist/core/sparql/builder/update-builder.js +4 -4
- package/dist/core/sparql/builder/update-builder.js.map +1 -1
- package/dist/core/sparql/helpers.js +3 -3
- package/dist/core/sparql/helpers.js.map +1 -1
- package/dist/core/sparql-engine.d.ts +16 -0
- package/dist/core/sparql-engine.d.ts.map +1 -0
- package/dist/core/sparql-engine.js +128 -0
- package/dist/core/sparql-engine.js.map +1 -0
- package/dist/core/sparql-executor.d.ts +3 -1
- package/dist/core/sparql-executor.d.ts.map +1 -1
- package/dist/core/sparql-executor.js +3 -4
- package/dist/core/sparql-executor.js.map +1 -1
- package/dist/core/triple/handlers/inverse.js +4 -4
- package/dist/core/triple/handlers/inverse.js.map +1 -1
- package/dist/core/triple/handlers/uri.d.ts +1 -1
- package/dist/core/triple/handlers/uri.js +3 -3
- package/dist/core/triple/handlers/uri.js.map +1 -1
- package/dist/core/uri/resolver.d.ts +9 -9
- package/dist/core/uri/resolver.d.ts.map +1 -1
- package/dist/core/uri/resolver.js +51 -51
- package/dist/core/uri/resolver.js.map +1 -1
- package/dist/core/uri/types.d.ts +10 -10
- package/dist/core/uri/types.d.ts.map +1 -1
- package/dist/driver.d.ts +10 -1
- package/dist/driver.d.ts.map +1 -1
- package/dist/driver.js +1 -5
- package/dist/driver.js.map +1 -1
- package/dist/esm/core/ast-to-sparql.d.ts +1 -1
- package/dist/esm/core/ast-to-sparql.js +1 -1
- package/dist/esm/core/comunica-patch.d.ts +1 -6
- package/dist/esm/core/comunica-patch.d.ts.map +1 -1
- package/dist/esm/core/comunica-patch.js +13 -20
- package/dist/esm/core/comunica-patch.js.map +1 -1
- package/dist/esm/core/execution/sparql-strategy.d.ts +9 -37
- package/dist/esm/core/execution/sparql-strategy.d.ts.map +1 -1
- package/dist/esm/core/execution/sparql-strategy.js +44 -148
- package/dist/esm/core/execution/sparql-strategy.js.map +1 -1
- package/dist/esm/core/execution/strategy-factory.d.ts +7 -20
- package/dist/esm/core/execution/strategy-factory.d.ts.map +1 -1
- package/dist/esm/core/execution/strategy-factory.js +2 -17
- package/dist/esm/core/execution/strategy-factory.js.map +1 -1
- package/dist/esm/core/pod-database.d.ts +3 -3
- package/dist/esm/core/pod-database.d.ts.map +1 -1
- package/dist/esm/core/pod-database.js +37 -37
- package/dist/esm/core/pod-database.js.map +1 -1
- package/dist/esm/core/pod-dialect.d.ts +2 -0
- package/dist/esm/core/pod-dialect.d.ts.map +1 -1
- package/dist/esm/core/pod-dialect.js +4 -2
- package/dist/esm/core/pod-dialect.js.map +1 -1
- package/dist/esm/core/query-builders/select-query-builder.d.ts +2 -2
- package/dist/esm/core/query-builders/select-query-builder.d.ts.map +1 -1
- package/dist/esm/core/query-builders/select-query-builder.js +10 -10
- package/dist/esm/core/query-builders/select-query-builder.js.map +1 -1
- package/dist/esm/core/schema/columns.d.ts +6 -6
- package/dist/esm/core/schema/columns.d.ts.map +1 -1
- package/dist/esm/core/schema/columns.js +18 -18
- package/dist/esm/core/schema/columns.js.map +1 -1
- package/dist/esm/core/schema/defs.d.ts +4 -4
- package/dist/esm/core/schema/defs.d.ts.map +1 -1
- package/dist/esm/core/schema/pod-table.js +2 -2
- package/dist/esm/core/schema/pod-table.js.map +1 -1
- package/dist/esm/core/services/pod-services.d.ts +7 -5
- package/dist/esm/core/services/pod-services.d.ts.map +1 -1
- package/dist/esm/core/services/pod-services.js +7 -5
- package/dist/esm/core/services/pod-services.js.map +1 -1
- package/dist/esm/core/shape/generator.js +3 -3
- package/dist/esm/core/shape/generator.js.map +1 -1
- package/dist/esm/core/shape/manager.js +5 -5
- package/dist/esm/core/shape/manager.js.map +1 -1
- package/dist/esm/core/sparql/builder/expression-builder.d.ts +5 -5
- package/dist/esm/core/sparql/builder/expression-builder.d.ts.map +1 -1
- package/dist/esm/core/sparql/builder/expression-builder.js +15 -15
- package/dist/esm/core/sparql/builder/expression-builder.js.map +1 -1
- package/dist/esm/core/sparql/builder/select-builder.d.ts +1 -1
- package/dist/esm/core/sparql/builder/select-builder.js +1 -1
- package/dist/esm/core/sparql/builder/update-builder.d.ts +1 -1
- package/dist/esm/core/sparql/builder/update-builder.d.ts.map +1 -1
- package/dist/esm/core/sparql/builder/update-builder.js +4 -4
- package/dist/esm/core/sparql/builder/update-builder.js.map +1 -1
- package/dist/esm/core/sparql/helpers.js +3 -3
- package/dist/esm/core/sparql/helpers.js.map +1 -1
- package/dist/esm/core/sparql-engine.d.ts +16 -0
- package/dist/esm/core/sparql-engine.d.ts.map +1 -0
- package/dist/esm/core/sparql-engine.js +89 -0
- package/dist/esm/core/sparql-engine.js.map +1 -0
- package/dist/esm/core/sparql-executor.d.ts +3 -1
- package/dist/esm/core/sparql-executor.d.ts.map +1 -1
- package/dist/esm/core/sparql-executor.js +3 -4
- package/dist/esm/core/sparql-executor.js.map +1 -1
- package/dist/esm/core/triple/handlers/inverse.js +4 -4
- package/dist/esm/core/triple/handlers/inverse.js.map +1 -1
- package/dist/esm/core/triple/handlers/uri.d.ts +1 -1
- package/dist/esm/core/triple/handlers/uri.js +3 -3
- package/dist/esm/core/triple/handlers/uri.js.map +1 -1
- package/dist/esm/core/uri/resolver.d.ts +9 -9
- package/dist/esm/core/uri/resolver.d.ts.map +1 -1
- package/dist/esm/core/uri/resolver.js +51 -51
- package/dist/esm/core/uri/resolver.js.map +1 -1
- package/dist/esm/core/uri/types.d.ts +10 -10
- package/dist/esm/core/uri/types.d.ts.map +1 -1
- package/dist/esm/driver.d.ts +10 -1
- package/dist/esm/driver.d.ts.map +1 -1
- package/dist/esm/driver.js +1 -5
- package/dist/esm/driver.js.map +1 -1
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +3 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/pod.d.ts +68 -0
- package/dist/esm/pod.d.ts.map +1 -0
- package/dist/esm/pod.js +146 -0
- package/dist/esm/pod.js.map +1 -0
- package/dist/esm/utils/webid-resolver.d.ts +1 -2
- package/dist/esm/utils/webid-resolver.d.ts.map +1 -1
- package/dist/esm/utils/webid-resolver.js +36 -30
- package/dist/esm/utils/webid-resolver.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -4
- package/dist/index.js.map +1 -1
- package/dist/pod.d.ts +68 -0
- package/dist/pod.d.ts.map +1 -0
- package/dist/pod.js +153 -0
- package/dist/pod.js.map +1 -0
- package/dist/utils/webid-resolver.d.ts +1 -2
- package/dist/utils/webid-resolver.d.ts.map +1 -1
- package/dist/utils/webid-resolver.js +36 -30
- package/dist/utils/webid-resolver.js.map +1 -1
- package/package.json +11 -3
package/README.md
CHANGED
|
@@ -1,123 +1,101 @@
|
|
|
1
1
|
# drizzle-solid
|
|
2
2
|
|
|
3
|
-
`drizzle-solid` is a Drizzle-aligned
|
|
3
|
+
`drizzle-solid` is a Drizzle-aligned data layer for application-owned data in Solid Pods.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
这次变化的重点不是强迫你把构造函数名字从 `drizzle()` 换成别的东西,而是把语义讲清楚:
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
- `table` 更接近 **resource layout / collection definition**
|
|
8
|
+
- `link` 字段更接近 **RDF link / IRI link**
|
|
9
|
+
- 实体最终身份更接近 **IRI / @id**,而不只是本地主键
|
|
10
|
+
- 读取和写入不再默认共享 SQL 式语义
|
|
8
11
|
|
|
9
|
-
##
|
|
12
|
+
## Scope
|
|
10
13
|
|
|
11
|
-
|
|
12
|
-
- Mapping fields to RDF predicates and resource layouts
|
|
13
|
-
- Using a Drizzle-style CRUD/query builder over Pod data
|
|
14
|
-
- Performing exact entity operations through full IRIs
|
|
15
|
-
- Building discovery, federation, and notification flows on top of Solid-native storage
|
|
14
|
+
Current v1 solves one problem well:
|
|
16
15
|
|
|
17
|
-
|
|
16
|
+
- persist application-owned data into Solid Pods with typed schemas
|
|
17
|
+
- keep Pod layout explicit through `base` and `subjectTemplate`
|
|
18
|
+
- make full IRIs part of the public API instead of hiding them behind local-only IDs
|
|
19
|
+
- give Drizzle users a migration-friendly surface without pretending Solid is a SQL database
|
|
20
|
+
- support adjacent Solid flows such as discovery, notifications, and federation where they fit the Pod model
|
|
18
21
|
|
|
19
|
-
|
|
20
|
-
- **IRI-native**: treat IRIs as first-class identity, not as an afterthought over local IDs
|
|
21
|
-
- **Pod-aware**: keep resource layout, links, network, and access constraints explicit
|
|
22
|
-
- **Drizzle-aligned**: stay close to Drizzle's builder surface and ergonomics where it makes sense
|
|
23
|
-
- **SPARQL-native**: the native query model is SPARQL/Solid, not SQL emulation
|
|
22
|
+
Not in v1:
|
|
24
23
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
30
|
-
|
|
31
|
-
## What it isn't
|
|
32
|
-
|
|
33
|
-
- Not a general-purpose SQL ORM
|
|
34
|
-
- Not a universal abstraction over arbitrary RDF on the open web
|
|
35
|
-
- Not a graph database or SPARQL engine replacement
|
|
36
|
-
- Not a black box that hides Pod boundaries, permissions, or network behavior
|
|
37
|
-
- Not a promise that every SQL/driver-specific Drizzle feature maps cleanly to Solid
|
|
38
|
-
|
|
39
|
-
## Core concepts
|
|
40
|
-
|
|
41
|
-
### Resource
|
|
42
|
-
|
|
43
|
-
A resource is the persisted unit in a Pod, usually a Turtle or JSON-LD document addressed by URL.
|
|
44
|
-
|
|
45
|
-
### Document
|
|
46
|
-
|
|
47
|
-
A document may contain one entity or many entities. Document layout is part of application design, not an implementation detail.
|
|
48
|
-
|
|
49
|
-
### Entity
|
|
50
|
-
|
|
51
|
-
An entity is a typed RDF subject stored in a document. It is closer to a linked-data subject than to a relational row.
|
|
52
|
-
|
|
53
|
-
### IRI
|
|
24
|
+
- full SQL/database feature parity
|
|
25
|
+
- raw SQL as the primary abstraction
|
|
26
|
+
- hiding Pod boundaries, permissions, or network behavior
|
|
27
|
+
- implicit scan-based `updateMany/deleteMany` on backends that do not support set-based mutation
|
|
28
|
+
- universal querying over arbitrary open-world RDF as the main goal
|
|
54
29
|
|
|
55
|
-
|
|
30
|
+
## Constructor Choice
|
|
56
31
|
|
|
57
|
-
|
|
32
|
+
仓库文档与 examples 默认先用 `pod(session, config?)` 讲主线语义:
|
|
58
33
|
|
|
59
|
-
|
|
34
|
+
- `pod(session, config?)`
|
|
35
|
+
- `drizzle(session, config?)`
|
|
60
36
|
|
|
61
|
-
|
|
37
|
+
建议这样理解:
|
|
62
38
|
|
|
63
|
-
|
|
39
|
+
- `pod()` 是语义优先的正式入口,方便把 API 组织成 `collection()` / `entity()` / `bind()`
|
|
40
|
+
- `drizzle()` 是同一运行时上的 Drizzle-aligned 入口,适合迁移或保留 builder/query 代码形状
|
|
41
|
+
- 重要的不是“入口名”,而是下面这些语义:`base`、`subjectTemplate`、`IRI`、exact-target mutation
|
|
64
42
|
|
|
65
|
-
|
|
66
|
-
- `solidSchema(...)` + `db.createTable(...)`: reusable schema separated from where data is stored
|
|
43
|
+
## Public Surface
|
|
67
44
|
|
|
68
|
-
|
|
45
|
+
当前公开表面可以分成两层:
|
|
69
46
|
|
|
70
|
-
|
|
47
|
+
### Semantic-first surface
|
|
71
48
|
|
|
72
|
-
|
|
49
|
+
- `pod(session, config?)`
|
|
50
|
+
- `client.bind(schema, options)`
|
|
51
|
+
- `client.collection(table)`
|
|
52
|
+
- `client.entity(table, iri)`
|
|
53
|
+
- `client.sparql(query)`
|
|
73
54
|
|
|
74
|
-
|
|
55
|
+
### Drizzle-aligned surface
|
|
75
56
|
|
|
76
|
-
|
|
77
|
-
|
|
57
|
+
- `drizzle(session, config?)`
|
|
58
|
+
- `podTable(name, columns, config)`
|
|
59
|
+
- `solidSchema(...)`
|
|
60
|
+
- `select / insert / update / delete`
|
|
61
|
+
- `db.query.*` read facade
|
|
78
62
|
|
|
79
|
-
|
|
63
|
+
## Install
|
|
80
64
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
65
|
+
```bash
|
|
66
|
+
yarn add @undefineds.co/drizzle-solid drizzle-orm
|
|
67
|
+
# optional, when your app wants the default SPARQL client engine
|
|
68
|
+
yarn add @comunica/query-sparql-solid
|
|
69
|
+
```
|
|
84
70
|
|
|
85
|
-
|
|
71
|
+
```bash
|
|
72
|
+
npm install @undefineds.co/drizzle-solid drizzle-orm
|
|
73
|
+
npm install @comunica/query-sparql-solid
|
|
74
|
+
```
|
|
86
75
|
|
|
87
|
-
|
|
76
|
+
`@comunica/query-sparql-solid` is an optional peer dependency.
|
|
88
77
|
|
|
89
|
-
|
|
78
|
+
Install it in the consuming app when you need built-in SPARQL query execution, LDP-backed query fallback, or direct SPARQL workflows.
|
|
90
79
|
|
|
91
|
-
|
|
80
|
+
Current public compatibility stance:
|
|
92
81
|
|
|
93
|
-
-
|
|
94
|
-
-
|
|
95
|
-
- which resources should be public, private, or shared
|
|
96
|
-
- how links should work across documents and Pods
|
|
82
|
+
- officially supported: `@comunica/query-sparql-solid` `4.x`
|
|
83
|
+
- not currently in the supported matrix: `3.x`
|
|
97
84
|
|
|
98
|
-
|
|
85
|
+
If another in-process runtime already carries Comunica (for example `xpod`), inject that engine instead of forcing a second copy. See `docs/api/README.md` and `docs/guides/installation.md`.
|
|
99
86
|
|
|
100
87
|
## Quick start
|
|
101
88
|
|
|
102
|
-
|
|
89
|
+
仓库里的主线 quick start 默认使用 `pod(session)`,因为它会把 `collection()` / `entity()` / `bind()` 语义直接写在代码里。
|
|
103
90
|
|
|
104
|
-
|
|
105
|
-
yarn add @undefineds.co/drizzle-solid
|
|
106
|
-
# or
|
|
107
|
-
npm install @undefineds.co/drizzle-solid
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
> In this repository, the examples import `drizzle-solid` through a local TypeScript path alias. In external applications, import the published package name: `@undefineds.co/drizzle-solid`.
|
|
111
|
-
|
|
112
|
-
### Define a model and run CRUD
|
|
91
|
+
如果你已有大量 Drizzle 风格代码,`drizzle(session)` 仍然是正式可用入口。
|
|
113
92
|
|
|
114
93
|
```ts
|
|
115
94
|
import {
|
|
116
|
-
|
|
95
|
+
pod,
|
|
117
96
|
podTable,
|
|
118
97
|
string,
|
|
119
98
|
datetime,
|
|
120
|
-
eq,
|
|
121
99
|
} from '@undefineds.co/drizzle-solid';
|
|
122
100
|
|
|
123
101
|
const posts = podTable('posts', {
|
|
@@ -131,115 +109,84 @@ const posts = podTable('posts', {
|
|
|
131
109
|
type: 'http://schema.org/CreativeWork',
|
|
132
110
|
});
|
|
133
111
|
|
|
134
|
-
const
|
|
135
|
-
await
|
|
112
|
+
const client = pod(session);
|
|
113
|
+
await client.init(posts);
|
|
114
|
+
|
|
115
|
+
const postsCollection = client.collection(posts);
|
|
136
116
|
|
|
137
|
-
await
|
|
117
|
+
const created = await postsCollection.create({
|
|
138
118
|
id: 'post-1',
|
|
139
119
|
title: 'Hello Solid',
|
|
140
120
|
content: 'Stored as RDF in a Pod document.',
|
|
141
121
|
createdAt: new Date(),
|
|
142
122
|
});
|
|
143
123
|
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
124
|
+
const first = await postsCollection.first({
|
|
125
|
+
where: { id: 'post-1' },
|
|
126
|
+
});
|
|
147
127
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
128
|
+
const postIri = created?.['@id'] ?? postsCollection.iriFor({
|
|
129
|
+
id: 'post-1',
|
|
130
|
+
title: 'Hello Solid',
|
|
131
|
+
content: 'Stored as RDF in a Pod document.',
|
|
132
|
+
createdAt: new Date(),
|
|
133
|
+
});
|
|
151
134
|
|
|
152
|
-
|
|
153
|
-
|
|
135
|
+
const post = client.entity(posts, postIri);
|
|
136
|
+
await post.update({ title: 'Updated title' });
|
|
137
|
+
await post.delete();
|
|
154
138
|
```
|
|
155
139
|
|
|
156
|
-
|
|
140
|
+
> 如果你保持 `drizzle(session)` 作为入口,语义并不会变:`base`、`subjectTemplate`、`IRI`、精确写目标这些约束仍然完全成立。
|
|
157
141
|
|
|
158
|
-
|
|
159
|
-
import {
|
|
160
|
-
drizzle,
|
|
161
|
-
solidSchema,
|
|
162
|
-
id,
|
|
163
|
-
string,
|
|
164
|
-
} from '@undefineds.co/drizzle-solid';
|
|
142
|
+
## Reusable schema + runtime binding
|
|
165
143
|
|
|
166
|
-
|
|
167
|
-
id: id(),
|
|
168
|
-
name: string('name').predicate('http://xmlns.com/foaf/0.1/name'),
|
|
169
|
-
bio: string('bio').predicate('http://schema.org/description'),
|
|
170
|
-
}, {
|
|
171
|
-
type: 'http://xmlns.com/foaf/0.1/Person',
|
|
172
|
-
});
|
|
144
|
+
Use `solidSchema(...)` when you want to separate the reusable data shape from where that data lives in a Pod.
|
|
173
145
|
|
|
174
|
-
|
|
146
|
+
如果你使用 `pod()` façade:
|
|
175
147
|
|
|
176
|
-
|
|
148
|
+
```ts
|
|
149
|
+
const client = pod(session);
|
|
150
|
+
const profileTable = client.bind(profileSchema, {
|
|
177
151
|
base: 'https://alice.example/profile/card',
|
|
178
152
|
});
|
|
179
153
|
```
|
|
180
154
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
For detail-page flows, remote links, shared resources, or exact mutation targets, use the IRI APIs:
|
|
184
|
-
|
|
185
|
-
- `db.findByIri(table, iri)`
|
|
186
|
-
- `db.updateByIri(table, iri, data)`
|
|
187
|
-
- `db.deleteByIri(table, iri)`
|
|
188
|
-
- `db.subscribeByIri(table, iri, options)`
|
|
155
|
+
如果你保持 `drizzle()` 入口:
|
|
189
156
|
|
|
190
157
|
```ts
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
'https://alice.example/data/agents.ttl#assistant',
|
|
196
|
-
{ description: 'Updated through explicit IRI' },
|
|
197
|
-
);
|
|
198
|
-
|
|
199
|
-
await db.deleteByIri(
|
|
200
|
-
agentTable,
|
|
201
|
-
'https://alice.example/data/agents.ttl#assistant',
|
|
202
|
-
);
|
|
158
|
+
const db = drizzle(session);
|
|
159
|
+
const profileTable = db.createTable(profileSchema, {
|
|
160
|
+
base: 'https://alice.example/profile/card',
|
|
161
|
+
});
|
|
203
162
|
```
|
|
204
163
|
|
|
205
|
-
##
|
|
164
|
+
## Identity and placement
|
|
206
165
|
|
|
207
|
-
`subjectTemplate`
|
|
166
|
+
`subjectTemplate` defines how application identity maps onto Pod resources.
|
|
208
167
|
|
|
209
168
|
Common patterns:
|
|
210
169
|
|
|
211
|
-
- `#{id}`:
|
|
212
|
-
- `{id}.ttl`:
|
|
213
|
-
- `{id}.ttl#it`: document
|
|
214
|
-
- `{chatId}/
|
|
170
|
+
- `#{id}`: many entities inside one document
|
|
171
|
+
- `{id}.ttl`: one document per entity
|
|
172
|
+
- `{id}.ttl#it`: one document per entity with stable in-document fragment
|
|
173
|
+
- `{chatId}/messages.ttl#{id}`: multi-variable layouts and partitioned resources
|
|
215
174
|
|
|
216
|
-
|
|
175
|
+
这不是命名细节,而是持久化语义本身。
|
|
217
176
|
|
|
218
|
-
|
|
219
|
-
const Message = podTable('Message', {
|
|
220
|
-
id: string('id').primaryKey(),
|
|
221
|
-
chatId: string('chatId').predicate('http://example.org/chatId'),
|
|
222
|
-
content: string('content').predicate('http://schema.org/text'),
|
|
223
|
-
timestamp: datetime('timestamp').predicate('http://schema.org/dateCreated'),
|
|
224
|
-
}, {
|
|
225
|
-
base: 'https://alice.example/data/chats/',
|
|
226
|
-
subjectTemplate: '{chatId}/messages.ttl#{id}',
|
|
227
|
-
type: 'http://schema.org/Message',
|
|
228
|
-
});
|
|
229
|
-
```
|
|
177
|
+
## Exact-target mutation semantics
|
|
230
178
|
|
|
231
|
-
|
|
179
|
+
Reads and writes do not intentionally behave the same way.
|
|
232
180
|
|
|
233
|
-
-
|
|
234
|
-
-
|
|
235
|
-
-
|
|
181
|
+
- list / filter reads can stay collection-oriented
|
|
182
|
+
- writes should prefer exact-target semantics
|
|
183
|
+
- incomplete `where(...)` information should not silently degrade into scan + mutate
|
|
184
|
+
- if a subject can only be resolved by multiple template variables, mutation should use an explicit IRI or provide all required variables
|
|
236
185
|
|
|
237
|
-
|
|
186
|
+
所以最重要的变化不是“要不要换成 `pod()`”,而是:
|
|
238
187
|
|
|
239
|
-
-
|
|
240
|
-
-
|
|
241
|
-
- **No implicit scan-based `updateMany/deleteMany` fallback** is performed on backends that do not natively support set-based mutation.
|
|
242
|
-
- **Capability-based enhancement**: if a backend explicitly supports filter-based multi-row mutation in the future (for example, an xpod capability), `drizzle-solid` can expose that path there without pretending every Solid server supports it.
|
|
188
|
+
- 什么时候你只是在做集合读取
|
|
189
|
+
- 什么时候你已经需要一个确定的实体目标
|
|
243
190
|
|
|
244
191
|
## Server support
|
|
245
192
|
|
|
@@ -247,118 +194,62 @@ Read-path and write-path semantics are intentionally different:
|
|
|
247
194
|
|
|
248
195
|
| Capability | Community Solid Server | xpod |
|
|
249
196
|
| --- | --- | --- |
|
|
250
|
-
| Basic CRUD | ✅
|
|
251
|
-
|
|
|
252
|
-
|
|
|
253
|
-
|
|
|
197
|
+
| Basic CRUD | ✅ | ✅ |
|
|
198
|
+
| Document notifications | ✅ | ✅ |
|
|
199
|
+
| Drizzle-style read facade | ✅ | ✅ |
|
|
200
|
+
| SPARQL pushdown | ⚠️ Limited / often client-assisted | ✅ Better in-process support |
|
|
201
|
+
| Filter / aggregation pushdown | ❌ Mostly fallback execution | ✅ Better server-side support |
|
|
254
202
|
| Federated queries | ⚠️ Client-side federation | ⚠️ Client-side federation |
|
|
255
|
-
|
|
|
256
|
-
| In-process test runtime | ⚠️ External CSS setup | ✅ via `@undefineds.co/xpod` |
|
|
257
|
-
|
|
258
|
-
### Community Solid Server (CSS)
|
|
259
|
-
|
|
260
|
-
On plain CSS, `drizzle-solid` keeps working, but many queries are resolved through client-side execution and Solid-aware fallbacks. This is the compatibility baseline.
|
|
261
|
-
|
|
262
|
-
### xpod
|
|
203
|
+
| In-process local runtime | ⚠️ External setup | ✅ via `@undefineds.co/xpod` |
|
|
263
204
|
|
|
264
|
-
`xpod`
|
|
265
|
-
|
|
266
|
-
See `docs/xpod-features.md` for more detail.
|
|
205
|
+
If `xpod` already ships its own Comunica stack, `drizzle-solid` can reuse that copy instead of requiring another app-level install.
|
|
267
206
|
|
|
268
207
|
## Verified examples
|
|
269
208
|
|
|
270
|
-
The canonical examples in `examples/` are
|
|
271
|
-
|
|
272
|
-
- `examples/01-quick-start.ts` — CRUD quick start
|
|
273
|
-
- `examples/02-relational-query.ts` — relational query API surface
|
|
274
|
-
- `examples/03-zero-config-discovery.ts` — zero-config discovery flow
|
|
275
|
-
- `examples/04-notifications.ts` — Solid notifications
|
|
276
|
-
- `examples/05-data-discovery.ts` — discovery API
|
|
277
|
-
- `examples/06-federated-query.ts` — federated query across Pods
|
|
278
|
-
- `examples/07-hooks-and-profile.ts` — hooks and profile management
|
|
279
|
-
- `examples/08-iri-based-operations.ts` — explicit IRI operations
|
|
280
|
-
- `examples/08-schema-inheritance.ts` — schema inheritance
|
|
281
|
-
- `examples/09-multi-variable-templates.ts` — multi-variable subject templates
|
|
282
|
-
|
|
283
|
-
The example manifest lives at `examples/manifest.json`, and the integration verification lives at `tests/integration/css/examples-verification.test.ts`.
|
|
284
|
-
|
|
285
|
-
## Query surface and SQL scope
|
|
286
|
-
|
|
287
|
-
`drizzle-solid` aims to stay close to Drizzle's public builder surface where that maps cleanly to Solid.
|
|
209
|
+
The canonical examples in `examples/` are part of the real integration verification flow:
|
|
288
210
|
|
|
289
|
-
|
|
211
|
+
- `examples/01-quick-start.ts`
|
|
212
|
+
- `examples/02-relational-query.ts`
|
|
213
|
+
- `examples/03-zero-config-discovery.ts`
|
|
214
|
+
- `examples/04-notifications.ts`
|
|
215
|
+
- `examples/05-data-discovery.ts`
|
|
216
|
+
- `examples/06-federated-query.ts`
|
|
217
|
+
- `examples/07-hooks-and-profile.ts`
|
|
218
|
+
- `examples/08-iri-based-operations.ts`
|
|
219
|
+
- `examples/08-schema-inheritance.ts`
|
|
220
|
+
- `examples/09-multi-variable-templates.ts`
|
|
290
221
|
|
|
291
|
-
|
|
292
|
-
- Read-oriented query facade: `db.query.*.findMany`, `findFirst`, `findById`, `findByIRI`, `count`
|
|
293
|
-
- Conditions: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`, `like`, `ilike`, `inArray`, `exists`, `and`, `or`, `not`
|
|
294
|
-
- Query features: joins, ordering, limits, offsets, distinct
|
|
295
|
-
- Aggregations: `count`, `sum`, `avg`, `min`, `max`
|
|
296
|
-
- Batch and `returning()` support where implemented by the dialect
|
|
297
|
-
- Raw **SPARQL** workflows where you need explicit graph-native control
|
|
298
|
-
|
|
299
|
-
Not the mainline contract:
|
|
300
|
-
|
|
301
|
-
- raw SQL as the primary abstraction
|
|
302
|
-
- transaction semantics identical to SQL databases
|
|
303
|
-
- implicit scan-based `updateMany/deleteMany` on backends that do not support set-based mutation
|
|
304
|
-
- driver-specific DDL, auto-increment, foreign keys, or SQL engine behaviors
|
|
222
|
+
See `examples/manifest.json` and `tests/integration/css/examples-verification.test.ts`.
|
|
305
223
|
|
|
306
|
-
|
|
224
|
+
## Migration
|
|
307
225
|
|
|
308
|
-
|
|
226
|
+
If you already know `drizzle-orm`, start here:
|
|
309
227
|
|
|
310
|
-
|
|
228
|
+
- `docs/guides/migrating-from-drizzle-orm.md`
|
|
311
229
|
|
|
312
|
-
|
|
313
|
-
- TypeIndex-driven location discovery
|
|
314
|
-
- shape-aware table generation
|
|
315
|
-
- federated queries across discovered Pod locations
|
|
316
|
-
- notifications for resource/entity updates
|
|
230
|
+
那份指南重点解释的是:
|
|
317
231
|
|
|
318
|
-
|
|
232
|
+
- `table/row` → `resource/document/entity/IRI`
|
|
233
|
+
- `link` / relation fields → RDF link / IRI link
|
|
234
|
+
- `where`-style mutation → exact-target mutation
|
|
235
|
+
- `toSQL()` / raw SQL 主心智 → `toSPARQL()` / SPARQL 主心智
|
|
319
236
|
|
|
320
|
-
|
|
321
|
-
- `docs/guides/notifications.md`
|
|
322
|
-
- `docs/guides/css-notifications.md`
|
|
323
|
-
- `docs/federated-queries.md`
|
|
324
|
-
- `docs/guides/multi-variable-templates.md`
|
|
237
|
+
而不是要求你第一步先改掉构造函数名。
|
|
325
238
|
|
|
326
239
|
## Documentation map
|
|
327
240
|
|
|
328
|
-
- `
|
|
329
|
-
- `docs/
|
|
241
|
+
- `docs/api/README.md` — current public API and constructor positioning
|
|
242
|
+
- `docs/guides/installation.md` — installation and SPARQL engine setup
|
|
243
|
+
- `docs/guides/migrating-from-drizzle-orm.md` — migration guide for Drizzle users
|
|
244
|
+
- `examples/README.md` — curated runnable examples
|
|
330
245
|
- `docs/guides/data-discovery.md` — discovery workflows
|
|
331
|
-
- `docs/guides/
|
|
332
|
-
- `docs/
|
|
333
|
-
- `ACTION-PLAN.md` —
|
|
334
|
-
|
|
335
|
-
## Planned scope
|
|
336
|
-
|
|
337
|
-
### Current direction
|
|
338
|
-
|
|
339
|
-
The current focus is:
|
|
340
|
-
|
|
341
|
-
- typed models for application-owned Pod data
|
|
342
|
-
- explicit document and subject placement
|
|
343
|
-
- Drizzle-aligned query ergonomics
|
|
344
|
-
- practical CRUD, discovery, federation, and notification flows
|
|
345
|
-
|
|
346
|
-
### Likely next steps
|
|
347
|
-
|
|
348
|
-
- better modeling ergonomics around reusable schemas and links
|
|
349
|
-
- clearer guidance around multi-resource layouts and identity strategies
|
|
350
|
-
- continued Drizzle parity on Solid-relevant behaviors
|
|
351
|
-
- stronger documentation around Solid-native tradeoffs and migration paths
|
|
352
|
-
|
|
353
|
-
### Out of scope
|
|
354
|
-
|
|
355
|
-
- hiding Solid behind a fake SQL database mental model
|
|
356
|
-
- universal querying over arbitrary open-world RDF as the primary goal
|
|
357
|
-
- promising full relational/database feature parity where Solid semantics differ
|
|
246
|
+
- `docs/guides/notifications.md` — notification flows
|
|
247
|
+
- `docs/xpod-features.md` — xpod runtime notes
|
|
248
|
+
- `ACTION-PLAN.md` — parity and implementation plan
|
|
358
249
|
|
|
359
250
|
## Contributing
|
|
360
251
|
|
|
361
|
-
Before
|
|
252
|
+
Before pushing:
|
|
362
253
|
|
|
363
254
|
```bash
|
|
364
255
|
yarn build
|
|
@@ -23,7 +23,7 @@ export declare class ASTToSPARQLConverter {
|
|
|
23
23
|
private prefixes;
|
|
24
24
|
constructor(podUrl: string, webId?: string | undefined, uriResolver?: UriResolver);
|
|
25
25
|
/**
|
|
26
|
-
* Set table registry for URI
|
|
26
|
+
* Set table registry for URI link resolution
|
|
27
27
|
*/
|
|
28
28
|
setTableRegistry(tableRegistry: Map<string, PodTable[]>, tableNameRegistry: Map<string, PodTable>, baseUri?: string): void;
|
|
29
29
|
convertSelect(ast: any, table: PodTable, targetGraph?: string, fromSources?: string[], allowGraphVariable?: boolean): SPARQLQuery;
|
|
@@ -26,7 +26,7 @@ class ASTToSPARQLConverter {
|
|
|
26
26
|
this.expressionBuilder = new expression_builder_1.ExpressionBuilder(this.uriResolver);
|
|
27
27
|
}
|
|
28
28
|
/**
|
|
29
|
-
* Set table registry for URI
|
|
29
|
+
* Set table registry for URI link resolution
|
|
30
30
|
*/
|
|
31
31
|
setTableRegistry(tableRegistry, tableNameRegistry, baseUri) {
|
|
32
32
|
this.tableContext = { tableRegistry, tableNameRegistry, baseUri };
|
|
@@ -1,7 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
declare const resolveCreateRequire: () => NodeCreateRequire | null;
|
|
3
|
-
declare const createRequireFn: NodeCreateRequire | null;
|
|
4
|
-
declare const moduleFilename: string;
|
|
5
|
-
declare const requireModule: NodeRequire | null;
|
|
6
|
-
declare const patchActionObserverHttp: (moduleName: string) => boolean;
|
|
1
|
+
export declare const applyComunicaPatches: (requireModule?: NodeRequire | null) => boolean;
|
|
7
2
|
//# sourceMappingURL=comunica-patch.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"comunica-patch.d.ts","sourceRoot":"","sources":["../../src/core/comunica-patch.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"comunica-patch.d.ts","sourceRoot":"","sources":["../../src/core/comunica-patch.ts"],"names":[],"mappings":"AAkEA,eAAO,MAAM,oBAAoB,GAAI,gBAAe,WAAW,GAAG,IAA2B,KAAG,OAe/F,CAAC"}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
// Comunica 兼容性补丁
|
|
3
3
|
// 修复 ActionObserverHttp 中 observedActors 未定义的问题
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.applyComunicaPatches = void 0;
|
|
4
6
|
const resolveCreateRequire = () => {
|
|
5
7
|
if (typeof window !== 'undefined') {
|
|
6
8
|
return null;
|
|
@@ -26,11 +28,10 @@ const moduleFilename = typeof __filename === 'string'
|
|
|
26
28
|
: (typeof process !== 'undefined' && typeof process.cwd === 'function'
|
|
27
29
|
? process.cwd()
|
|
28
30
|
: '/');
|
|
29
|
-
const
|
|
31
|
+
const defaultRequireModule = createRequireFn
|
|
30
32
|
? createRequireFn(moduleFilename)
|
|
31
33
|
: null;
|
|
32
|
-
|
|
33
|
-
const patchActionObserverHttp = (moduleName) => {
|
|
34
|
+
const patchActionObserverHttp = (requireModule, moduleName) => {
|
|
34
35
|
if (!requireModule) {
|
|
35
36
|
return false;
|
|
36
37
|
}
|
|
@@ -39,15 +40,9 @@ const patchActionObserverHttp = (moduleName) => {
|
|
|
39
40
|
if (comunica && comunica.ActionObserverHttp) {
|
|
40
41
|
const originalOnRun = comunica.ActionObserverHttp.prototype.onRun;
|
|
41
42
|
comunica.ActionObserverHttp.prototype.onRun = function (actor, _action, _output) {
|
|
42
|
-
|
|
43
|
-
if (!this.observedActors) {
|
|
43
|
+
if (!this.observedActors || !Array.isArray(this.observedActors)) {
|
|
44
44
|
this.observedActors = [];
|
|
45
45
|
}
|
|
46
|
-
// observedActors 应该是一个数组,如果不是则初始化为空数组
|
|
47
|
-
if (!Array.isArray(this.observedActors)) {
|
|
48
|
-
this.observedActors = [];
|
|
49
|
-
}
|
|
50
|
-
// 调用原始方法
|
|
51
46
|
return originalOnRun.call(this, actor, _action, _output);
|
|
52
47
|
};
|
|
53
48
|
console.log(`[Patch] Successfully patched ActionObserverHttp.onRun in ${moduleName}`);
|
|
@@ -55,22 +50,24 @@ const patchActionObserverHttp = (moduleName) => {
|
|
|
55
50
|
}
|
|
56
51
|
return false;
|
|
57
52
|
}
|
|
58
|
-
catch
|
|
59
|
-
// 静默忽略模块不存在的错误
|
|
53
|
+
catch {
|
|
60
54
|
return false;
|
|
61
55
|
}
|
|
62
56
|
};
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
57
|
+
const applyComunicaPatches = (requireModule = defaultRequireModule) => {
|
|
58
|
+
if (!requireModule) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
66
61
|
const modules = [
|
|
67
|
-
// 顶层模块
|
|
68
62
|
'@comunica/actor-query-result-serialize-sparql-json',
|
|
69
63
|
'@comunica/actor-query-result-serialize-stats',
|
|
70
|
-
// 嵌套在 @comunica/query-sparql-solid 下的模块 (Comunica v4)
|
|
71
64
|
'@comunica/query-sparql-solid/node_modules/@comunica/actor-query-result-serialize-sparql-json',
|
|
72
65
|
'@comunica/query-sparql-solid/node_modules/@comunica/actor-query-result-serialize-stats'
|
|
73
66
|
];
|
|
74
|
-
modules
|
|
75
|
-
|
|
67
|
+
return modules
|
|
68
|
+
.map((moduleName) => patchActionObserverHttp(requireModule, moduleName))
|
|
69
|
+
.some(Boolean);
|
|
70
|
+
};
|
|
71
|
+
exports.applyComunicaPatches = applyComunicaPatches;
|
|
72
|
+
(0, exports.applyComunicaPatches)();
|
|
76
73
|
//# sourceMappingURL=comunica-patch.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"comunica-patch.js","sourceRoot":"","sources":["../../src/core/comunica-patch.ts"],"names":[],"mappings":";AAAA,iBAAiB;AACjB,gDAAgD
|
|
1
|
+
{"version":3,"file":"comunica-patch.js","sourceRoot":"","sources":["../../src/core/comunica-patch.ts"],"names":[],"mappings":";AAAA,iBAAiB;AACjB,gDAAgD;;;AAIhD,MAAM,oBAAoB,GAAG,GAA6B,EAAE;IAC1D,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;QAClC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,CAA4B,CAAC;QAC/D,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,SAAS,GAAG,WAAW,CAAC,QAAQ,CAAsD,CAAC;QAC7F,IAAI,CAAC,SAAS,IAAI,OAAO,SAAS,CAAC,aAAa,KAAK,UAAU,EAAE,CAAC;YAChE,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,SAAS,CAAC,aAAa,CAAC;IACjC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,eAAe,GAAG,oBAAoB,EAAE,CAAC;AAC/C,MAAM,cAAc,GAAG,OAAO,UAAU,KAAK,QAAQ;IACnD,CAAC,CAAC,UAAU;IACZ,CAAC,CAAC,CAAC,OAAO,OAAO,KAAK,WAAW,IAAI,OAAO,OAAO,CAAC,GAAG,KAAK,UAAU;QAClE,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE;QACf,CAAC,CAAC,GAAG,CAAC,CAAC;AAEb,MAAM,oBAAoB,GAAG,eAAe;IAC1C,CAAC,CAAC,eAAe,CAAC,cAAc,CAAC;IACjC,CAAC,CAAC,IAAI,CAAC;AAET,MAAM,uBAAuB,GAAG,CAAC,aAAiC,EAAE,UAAkB,EAAE,EAAE;IACxF,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,aAAa,CAAC,UAAU,CAAC,CAAC;QAE3C,IAAI,QAAQ,IAAI,QAAQ,CAAC,kBAAkB,EAAE,CAAC;YAC5C,MAAM,aAAa,GAAG,QAAQ,CAAC,kBAAkB,CAAC,SAAS,CAAC,KAAK,CAAC;YAElE,QAAQ,CAAC,kBAAkB,CAAC,SAAS,CAAC,KAAK,GAAG,UAAS,KAAU,EAAE,OAAY,EAAE,OAAY;gBAC3F,IAAI,CAAC,IAAI,CAAC,cAAc,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,EAAE,CAAC;oBAChE,IAAI,CAAC,cAAc,GAAG,EAAE,CAAC;gBAC3B,CAAC;gBAED,OAAO,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;YAC3D,CAAC,CAAC;YAEF,OAAO,CAAC,GAAG,CAAC,4DAA4D,UAAU,EAAE,CAAC,CAAC;YACtF,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC,CAAC;AAEK,MAAM,oBAAoB,GAAG,CAAC,gBAAoC,oBAAoB,EAAW,EAAE;IACxG,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,OAAO,GAAG;QACd,oDAAoD;QACpD,8CAA8C;QAC9C,8FAA8F;QAC9F,wFAAwF;KACzF,CAAC;IAEF,OAAO,OAAO;SACX,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,uBAAuB,CAAC,aAAa,EAAE,UAAU,CAAC,CAAC;SACvE,IAAI,CAAC,OAAO,CAAC,CAAC;AACnB,CAAC,CAAC;AAfW,QAAA,oBAAoB,wBAe/B;AAEF,IAAA,4BAAoB,GAAE,CAAC"}
|