@undefineds.co/drizzle-solid 0.3.0 → 0.3.2
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/CHANGELOG-DRAFT.md +1 -1
- package/README.md +128 -144
- package/README.zh-CN.md +254 -0
- package/dist/core/ast-to-sparql.d.ts +31 -24
- package/dist/core/ast-to-sparql.d.ts.map +1 -1
- package/dist/core/ast-to-sparql.js.map +1 -1
- package/dist/core/compile-time-types.d.ts +10 -7
- package/dist/core/compile-time-types.d.ts.map +1 -1
- package/dist/core/compile-time-types.js +6 -4
- package/dist/core/compile-time-types.js.map +1 -1
- package/dist/core/comunica-patch.d.ts.map +1 -1
- package/dist/core/comunica-patch.js +0 -1
- package/dist/core/comunica-patch.js.map +1 -1
- package/dist/core/conflict-resolution.d.ts.map +1 -1
- package/dist/core/conflict-resolution.js +3 -2
- package/dist/core/conflict-resolution.js.map +1 -1
- package/dist/core/discovery/typeindex-discovery.d.ts.map +1 -1
- package/dist/core/discovery/typeindex-discovery.js +0 -2
- package/dist/core/discovery/typeindex-discovery.js.map +1 -1
- package/dist/core/execution/ldp-executor.d.ts.map +1 -1
- package/dist/core/execution/ldp-executor.js +2 -21
- package/dist/core/execution/ldp-executor.js.map +1 -1
- package/dist/core/execution/pod-executor.d.ts.map +1 -1
- package/dist/core/execution/pod-executor.js +2 -4
- package/dist/core/execution/pod-executor.js.map +1 -1
- package/dist/core/execution/sparql-strategy.d.ts +0 -4
- package/dist/core/execution/sparql-strategy.d.ts.map +1 -1
- package/dist/core/execution/sparql-strategy.js +6 -64
- package/dist/core/execution/sparql-strategy.js.map +1 -1
- package/dist/core/expressions.d.ts +10 -9
- package/dist/core/expressions.d.ts.map +1 -1
- package/dist/core/expressions.js.map +1 -1
- package/dist/core/pod-database.d.ts +59 -15
- package/dist/core/pod-database.d.ts.map +1 -1
- package/dist/core/pod-database.js +99 -35
- package/dist/core/pod-database.js.map +1 -1
- package/dist/core/pod-dialect.d.ts +5 -1
- package/dist/core/pod-dialect.d.ts.map +1 -1
- package/dist/core/pod-dialect.js +32 -27
- package/dist/core/pod-dialect.js.map +1 -1
- package/dist/core/pod-session.d.ts +10 -9
- package/dist/core/pod-session.d.ts.map +1 -1
- package/dist/core/pod-session.js.map +1 -1
- package/dist/core/query-builders/delete-query-builder.d.ts +2 -2
- package/dist/core/query-builders/delete-query-builder.d.ts.map +1 -1
- package/dist/core/query-builders/delete-query-builder.js +8 -6
- package/dist/core/query-builders/delete-query-builder.js.map +1 -1
- package/dist/core/query-builders/select-query-builder.d.ts +13 -4
- package/dist/core/query-builders/select-query-builder.d.ts.map +1 -1
- package/dist/core/query-builders/select-query-builder.js +207 -21
- package/dist/core/query-builders/select-query-builder.js.map +1 -1
- package/dist/core/query-builders/update-query-builder.d.ts +2 -2
- package/dist/core/query-builders/update-query-builder.d.ts.map +1 -1
- package/dist/core/query-builders/update-query-builder.js +7 -5
- package/dist/core/query-builders/update-query-builder.js.map +1 -1
- package/dist/core/query-conditions.d.ts +18 -11
- package/dist/core/query-conditions.d.ts.map +1 -1
- package/dist/core/query-conditions.js.map +1 -1
- package/dist/core/query-where-policy.d.ts +7 -0
- package/dist/core/query-where-policy.d.ts.map +1 -0
- package/dist/core/query-where-policy.js +83 -0
- package/dist/core/query-where-policy.js.map +1 -0
- package/dist/core/resource-resolver/base-resolver.d.ts +1 -0
- package/dist/core/resource-resolver/base-resolver.d.ts.map +1 -1
- package/dist/core/resource-resolver/base-resolver.js +33 -39
- package/dist/core/resource-resolver/base-resolver.js.map +1 -1
- package/dist/core/resource-resolver/document-resolver.d.ts +2 -1
- package/dist/core/resource-resolver/document-resolver.d.ts.map +1 -1
- package/dist/core/resource-resolver/document-resolver.js +17 -46
- package/dist/core/resource-resolver/document-resolver.js.map +1 -1
- package/dist/core/runtime/pod-runtime.d.ts.map +1 -1
- package/dist/core/runtime/pod-runtime.js +0 -6
- package/dist/core/runtime/pod-runtime.js.map +1 -1
- package/dist/core/select-plan.d.ts +9 -7
- package/dist/core/select-plan.d.ts.map +1 -1
- package/dist/core/solid-n3-client.d.ts.map +1 -1
- package/dist/core/solid-n3-client.js +20 -9
- package/dist/core/solid-n3-client.js.map +1 -1
- package/dist/core/sparql/builder/expression-builder.d.ts +9 -11
- package/dist/core/sparql/builder/expression-builder.d.ts.map +1 -1
- package/dist/core/sparql/builder/expression-builder.js +162 -59
- package/dist/core/sparql/builder/expression-builder.js.map +1 -1
- package/dist/core/sparql/builder/select-builder.d.ts.map +1 -1
- package/dist/core/sparql/builder/select-builder.js +18 -4
- package/dist/core/sparql/builder/select-builder.js.map +1 -1
- package/dist/core/sparql/builder/update-builder.d.ts.map +1 -1
- package/dist/core/sparql/builder/update-builder.js +0 -11
- package/dist/core/sparql/builder/update-builder.js.map +1 -1
- package/dist/core/sparql-engine.d.ts +1 -1
- package/dist/core/sparql-engine.d.ts.map +1 -1
- package/dist/core/sparql-executor.d.ts +9 -6
- package/dist/core/sparql-executor.d.ts.map +1 -1
- package/dist/core/sparql-executor.js +53 -43
- package/dist/core/sparql-executor.js.map +1 -1
- package/dist/core/typeindex-manager.d.ts.map +1 -1
- package/dist/core/typeindex-manager.js +3 -0
- package/dist/core/typeindex-manager.js.map +1 -1
- package/dist/core/types.d.ts +3 -3
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/zod-integration.d.ts +9 -7
- package/dist/core/zod-integration.d.ts.map +1 -1
- package/dist/core/zod-integration.js.map +1 -1
- package/dist/esm/core/ast-to-sparql.d.ts +31 -24
- package/dist/esm/core/ast-to-sparql.d.ts.map +1 -1
- package/dist/esm/core/ast-to-sparql.js.map +1 -1
- package/dist/esm/core/compile-time-types.d.ts +10 -7
- package/dist/esm/core/compile-time-types.d.ts.map +1 -1
- package/dist/esm/core/compile-time-types.js +6 -4
- package/dist/esm/core/compile-time-types.js.map +1 -1
- package/dist/esm/core/comunica-patch.d.ts.map +1 -1
- package/dist/esm/core/comunica-patch.js +0 -1
- package/dist/esm/core/comunica-patch.js.map +1 -1
- package/dist/esm/core/conflict-resolution.d.ts.map +1 -1
- package/dist/esm/core/conflict-resolution.js +3 -2
- package/dist/esm/core/conflict-resolution.js.map +1 -1
- package/dist/esm/core/discovery/typeindex-discovery.d.ts.map +1 -1
- package/dist/esm/core/discovery/typeindex-discovery.js +0 -2
- package/dist/esm/core/discovery/typeindex-discovery.js.map +1 -1
- package/dist/esm/core/execution/ldp-executor.d.ts.map +1 -1
- package/dist/esm/core/execution/ldp-executor.js +2 -21
- package/dist/esm/core/execution/ldp-executor.js.map +1 -1
- package/dist/esm/core/execution/pod-executor.d.ts.map +1 -1
- package/dist/esm/core/execution/pod-executor.js +2 -4
- package/dist/esm/core/execution/pod-executor.js.map +1 -1
- package/dist/esm/core/execution/sparql-strategy.d.ts +0 -4
- package/dist/esm/core/execution/sparql-strategy.d.ts.map +1 -1
- package/dist/esm/core/execution/sparql-strategy.js +7 -65
- package/dist/esm/core/execution/sparql-strategy.js.map +1 -1
- package/dist/esm/core/expressions.d.ts +10 -9
- package/dist/esm/core/expressions.d.ts.map +1 -1
- package/dist/esm/core/expressions.js.map +1 -1
- package/dist/esm/core/pod-database.d.ts +59 -15
- package/dist/esm/core/pod-database.d.ts.map +1 -1
- package/dist/esm/core/pod-database.js +99 -35
- package/dist/esm/core/pod-database.js.map +1 -1
- package/dist/esm/core/pod-dialect.d.ts +5 -1
- package/dist/esm/core/pod-dialect.d.ts.map +1 -1
- package/dist/esm/core/pod-dialect.js +33 -28
- package/dist/esm/core/pod-dialect.js.map +1 -1
- package/dist/esm/core/pod-session.d.ts +10 -9
- package/dist/esm/core/pod-session.d.ts.map +1 -1
- package/dist/esm/core/pod-session.js.map +1 -1
- package/dist/esm/core/query-builders/delete-query-builder.d.ts +2 -2
- package/dist/esm/core/query-builders/delete-query-builder.d.ts.map +1 -1
- package/dist/esm/core/query-builders/delete-query-builder.js +8 -6
- package/dist/esm/core/query-builders/delete-query-builder.js.map +1 -1
- package/dist/esm/core/query-builders/select-query-builder.d.ts +13 -4
- package/dist/esm/core/query-builders/select-query-builder.d.ts.map +1 -1
- package/dist/esm/core/query-builders/select-query-builder.js +208 -22
- package/dist/esm/core/query-builders/select-query-builder.js.map +1 -1
- package/dist/esm/core/query-builders/update-query-builder.d.ts +2 -2
- package/dist/esm/core/query-builders/update-query-builder.d.ts.map +1 -1
- package/dist/esm/core/query-builders/update-query-builder.js +7 -5
- package/dist/esm/core/query-builders/update-query-builder.js.map +1 -1
- package/dist/esm/core/query-conditions.d.ts +18 -11
- package/dist/esm/core/query-conditions.d.ts.map +1 -1
- package/dist/esm/core/query-conditions.js.map +1 -1
- package/dist/esm/core/query-where-policy.d.ts +7 -0
- package/dist/esm/core/query-where-policy.d.ts.map +1 -0
- package/dist/esm/core/query-where-policy.js +78 -0
- package/dist/esm/core/query-where-policy.js.map +1 -0
- package/dist/esm/core/resource-resolver/base-resolver.d.ts +1 -0
- package/dist/esm/core/resource-resolver/base-resolver.d.ts.map +1 -1
- package/dist/esm/core/resource-resolver/base-resolver.js +33 -39
- package/dist/esm/core/resource-resolver/base-resolver.js.map +1 -1
- package/dist/esm/core/resource-resolver/document-resolver.d.ts +2 -1
- package/dist/esm/core/resource-resolver/document-resolver.d.ts.map +1 -1
- package/dist/esm/core/resource-resolver/document-resolver.js +17 -46
- package/dist/esm/core/resource-resolver/document-resolver.js.map +1 -1
- package/dist/esm/core/runtime/pod-runtime.d.ts.map +1 -1
- package/dist/esm/core/runtime/pod-runtime.js +0 -6
- package/dist/esm/core/runtime/pod-runtime.js.map +1 -1
- package/dist/esm/core/select-plan.d.ts +9 -7
- package/dist/esm/core/select-plan.d.ts.map +1 -1
- package/dist/esm/core/solid-n3-client.d.ts.map +1 -1
- package/dist/esm/core/solid-n3-client.js +20 -9
- package/dist/esm/core/solid-n3-client.js.map +1 -1
- package/dist/esm/core/sparql/builder/expression-builder.d.ts +9 -11
- package/dist/esm/core/sparql/builder/expression-builder.d.ts.map +1 -1
- package/dist/esm/core/sparql/builder/expression-builder.js +162 -59
- package/dist/esm/core/sparql/builder/expression-builder.js.map +1 -1
- package/dist/esm/core/sparql/builder/select-builder.d.ts.map +1 -1
- package/dist/esm/core/sparql/builder/select-builder.js +18 -4
- package/dist/esm/core/sparql/builder/select-builder.js.map +1 -1
- package/dist/esm/core/sparql/builder/update-builder.d.ts.map +1 -1
- package/dist/esm/core/sparql/builder/update-builder.js +0 -11
- package/dist/esm/core/sparql/builder/update-builder.js.map +1 -1
- package/dist/esm/core/sparql-engine.d.ts +1 -1
- package/dist/esm/core/sparql-engine.d.ts.map +1 -1
- package/dist/esm/core/sparql-executor.d.ts +9 -6
- package/dist/esm/core/sparql-executor.d.ts.map +1 -1
- package/dist/esm/core/sparql-executor.js +53 -43
- package/dist/esm/core/sparql-executor.js.map +1 -1
- package/dist/esm/core/typeindex-manager.d.ts.map +1 -1
- package/dist/esm/core/typeindex-manager.js +3 -0
- package/dist/esm/core/typeindex-manager.js.map +1 -1
- package/dist/esm/core/types.d.ts +3 -3
- package/dist/esm/core/types.d.ts.map +1 -1
- package/dist/esm/core/zod-integration.d.ts +9 -7
- package/dist/esm/core/zod-integration.d.ts.map +1 -1
- package/dist/esm/core/zod-integration.js.map +1 -1
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/pod.d.ts +53 -3
- package/dist/esm/pod.d.ts.map +1 -1
- package/dist/esm/utils/find-by-iri.d.ts +4 -2
- package/dist/esm/utils/find-by-iri.d.ts.map +1 -1
- package/dist/esm/utils/find-by-iri.js +8 -8
- package/dist/esm/utils/find-by-iri.js.map +1 -1
- package/dist/esm/utils/helpers.d.ts +2 -2
- package/dist/esm/utils/helpers.d.ts.map +1 -1
- package/dist/esm/utils/rdf-helpers.d.ts +2 -2
- package/dist/esm/utils/rdf-helpers.d.ts.map +1 -1
- package/dist/esm/utils/rdf-helpers.js +9 -6
- package/dist/esm/utils/rdf-helpers.js.map +1 -1
- package/dist/esm/utils/rdf-validation.d.ts +5 -3
- package/dist/esm/utils/rdf-validation.d.ts.map +1 -1
- package/dist/esm/utils/rdf-validation.js +47 -8
- package/dist/esm/utils/rdf-validation.js.map +1 -1
- package/dist/esm/utils/webid-resolver.js +1 -1
- package/dist/esm/utils/webid-resolver.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/pod.d.ts +53 -3
- package/dist/pod.d.ts.map +1 -1
- package/dist/utils/find-by-iri.d.ts +4 -2
- package/dist/utils/find-by-iri.d.ts.map +1 -1
- package/dist/utils/find-by-iri.js +8 -8
- package/dist/utils/find-by-iri.js.map +1 -1
- package/dist/utils/helpers.d.ts +2 -2
- package/dist/utils/helpers.d.ts.map +1 -1
- package/dist/utils/rdf-helpers.d.ts +2 -2
- package/dist/utils/rdf-helpers.d.ts.map +1 -1
- package/dist/utils/rdf-helpers.js +9 -6
- package/dist/utils/rdf-helpers.js.map +1 -1
- package/dist/utils/rdf-validation.d.ts +5 -3
- package/dist/utils/rdf-validation.d.ts.map +1 -1
- package/dist/utils/rdf-validation.js +47 -8
- package/dist/utils/rdf-validation.js.map +1 -1
- package/dist/utils/webid-resolver.js +1 -1
- package/dist/utils/webid-resolver.js.map +1 -1
- package/package.json +3 -2
package/CHANGELOG-DRAFT.md
CHANGED
|
@@ -392,7 +392,7 @@ npm test
|
|
|
392
392
|
- [CSS Notifications](./docs/guides/css-notifications.md) - 实时通知支持(79KB)
|
|
393
393
|
|
|
394
394
|
### 开发指南
|
|
395
|
-
- [
|
|
395
|
+
- [测试指南](./docs/guides/testing.md) - 当前测试方法、分层和验证要求
|
|
396
396
|
- [快速开始](./docs/quick-start-local.md) - 本地开发入门
|
|
397
397
|
- [文档索引](./docs/README.md) - 完整文档导航
|
|
398
398
|
|
package/README.md
CHANGED
|
@@ -1,102 +1,81 @@
|
|
|
1
1
|
# drizzle-solid
|
|
2
2
|
|
|
3
|
-
`
|
|
3
|
+
Chinese version: [`README.zh-CN.md`](README.zh-CN.md)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
`drizzle-solid` stores application-owned data in Solid Pods with typed schemas and a Drizzle-aligned developer experience.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
- `link` 字段更接近 **RDF link / IRI link**
|
|
9
|
-
- 实体最终身份更接近 **IRI / @id**,而不只是本地主键
|
|
10
|
-
- 读取和写入不再默认共享 SQL 式语义
|
|
7
|
+
Use it when you want:
|
|
11
8
|
|
|
12
|
-
|
|
9
|
+
- TypeScript schemas for Pod data
|
|
10
|
+
- explicit Pod layout through `base` and `subjectTemplate`
|
|
11
|
+
- a choice between a Solid-first API (`pod()`) and a Drizzle-shaped API (`drizzle()`)
|
|
12
|
+
- exact entity reads and writes by IRI or locator
|
|
13
|
+
- SPARQL-backed reads when your backend exposes query capability
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
## What it is good at
|
|
15
16
|
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
- support adjacent Solid flows such as discovery, notifications, and federation where they fit the Pod model
|
|
17
|
+
- application-owned data in your Pod or a user Pod
|
|
18
|
+
- typed CRUD over known resource layouts
|
|
19
|
+
- gradual migration from `drizzle-orm`
|
|
20
|
+
- Pod-native concepts such as IRIs, documents, links, discovery, notifications, and federation
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
## What it is not trying to be
|
|
23
23
|
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
-
|
|
28
|
-
- universal querying over arbitrary open-world RDF as the main goal
|
|
29
|
-
|
|
30
|
-
## Constructor Choice
|
|
31
|
-
|
|
32
|
-
仓库文档与 examples 默认先用 `pod(session, config?)` 讲主线语义:
|
|
33
|
-
|
|
34
|
-
- `pod(session, config?)`
|
|
35
|
-
- `drizzle(session, config?)`
|
|
36
|
-
|
|
37
|
-
建议这样理解:
|
|
38
|
-
|
|
39
|
-
- `pod()` 是语义优先的正式入口,方便把 API 组织成 `collection()` / `entity()` / `bind()`
|
|
40
|
-
- `drizzle()` 是同一运行时上的 Drizzle-aligned 入口,适合迁移或保留 builder/query 代码形状
|
|
41
|
-
- 重要的不是“入口名”,而是下面这些语义:`base`、`subjectTemplate`、`IRI`、exact-target mutation
|
|
42
|
-
|
|
43
|
-
## Public Surface
|
|
44
|
-
|
|
45
|
-
当前公开表面可以分成两层:
|
|
46
|
-
|
|
47
|
-
### Semantic-first surface
|
|
48
|
-
|
|
49
|
-
- `pod(session, config?)`
|
|
50
|
-
- `client.bind(schema, options)`
|
|
51
|
-
- `client.collection(table)`
|
|
52
|
-
- `client.entity(table, iri)`
|
|
53
|
-
- `client.sparql(query)`
|
|
54
|
-
|
|
55
|
-
### Drizzle-aligned surface
|
|
56
|
-
|
|
57
|
-
- `drizzle(session, config?)`
|
|
58
|
-
- `podTable(name, columns, config)`
|
|
59
|
-
- `solidSchema(...)`
|
|
60
|
-
- `select / insert / update / delete`
|
|
61
|
-
- `db.query.*` read facade
|
|
24
|
+
- a general-purpose RDF exploration toolkit
|
|
25
|
+
- full SQL/database parity across every Solid backend
|
|
26
|
+
- a library that hides Pod boundaries, permissions, or network costs
|
|
27
|
+
- a raw SQL-first abstraction
|
|
62
28
|
|
|
63
29
|
## Install
|
|
64
30
|
|
|
65
31
|
```bash
|
|
66
32
|
yarn add @undefineds.co/drizzle-solid drizzle-orm
|
|
67
|
-
# optional
|
|
33
|
+
# optional: install when your app wants the built-in SPARQL engine
|
|
68
34
|
yarn add @comunica/query-sparql-solid
|
|
69
35
|
```
|
|
70
36
|
|
|
71
37
|
```bash
|
|
72
38
|
npm install @undefineds.co/drizzle-solid drizzle-orm
|
|
39
|
+
# optional: install when your app wants the built-in SPARQL engine
|
|
73
40
|
npm install @comunica/query-sparql-solid
|
|
74
41
|
```
|
|
75
42
|
|
|
76
43
|
`@comunica/query-sparql-solid` is an optional peer dependency.
|
|
77
44
|
|
|
78
|
-
|
|
45
|
+
Current support stance:
|
|
79
46
|
|
|
80
|
-
|
|
47
|
+
- supported: `@comunica/query-sparql-solid` `4.x`
|
|
48
|
+
- not in the current supported matrix: `3.x`
|
|
81
49
|
|
|
82
|
-
-
|
|
83
|
-
- not currently in the supported matrix: `3.x`
|
|
50
|
+
If another in-process runtime already ships Comunica, inject that engine instead of installing a second copy.
|
|
84
51
|
|
|
85
|
-
|
|
52
|
+
## Choose your API style
|
|
86
53
|
|
|
87
|
-
|
|
54
|
+
### `pod(session)`
|
|
55
|
+
|
|
56
|
+
Use this for new code when you want Solid concepts to be explicit in the API:
|
|
88
57
|
|
|
89
|
-
|
|
58
|
+
- `collection(table)`
|
|
59
|
+
- `entity(table, iri)`
|
|
60
|
+
- `bind(schema, options)`
|
|
61
|
+
- `sparql(query)`
|
|
90
62
|
|
|
91
|
-
|
|
63
|
+
### `drizzle(session)`
|
|
64
|
+
|
|
65
|
+
Use this when you are migrating from `drizzle-orm` or want to keep builder-shaped code:
|
|
66
|
+
|
|
67
|
+
- `select / insert / update / delete`
|
|
68
|
+
- `db.query.<table>`
|
|
69
|
+
- `findByLocator / findByIri`
|
|
70
|
+
- `updateByLocator / updateByIri`
|
|
71
|
+
- `deleteByLocator / deleteByIri`
|
|
72
|
+
|
|
73
|
+
Both styles use the same runtime. The difference is API shape, not storage behavior.
|
|
74
|
+
|
|
75
|
+
## Quick start
|
|
92
76
|
|
|
93
77
|
```ts
|
|
94
|
-
import {
|
|
95
|
-
pod,
|
|
96
|
-
podTable,
|
|
97
|
-
string,
|
|
98
|
-
datetime,
|
|
99
|
-
} from '@undefineds.co/drizzle-solid';
|
|
78
|
+
import { pod, podTable, string, datetime } from '@undefineds.co/drizzle-solid';
|
|
100
79
|
|
|
101
80
|
const posts = podTable('posts', {
|
|
102
81
|
id: string('id').primaryKey(),
|
|
@@ -112,85 +91,104 @@ const posts = podTable('posts', {
|
|
|
112
91
|
const client = pod(session);
|
|
113
92
|
await client.init(posts);
|
|
114
93
|
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
const created = await postsCollection.create({
|
|
94
|
+
const created = await client.collection(posts).create({
|
|
118
95
|
id: 'post-1',
|
|
119
96
|
title: 'Hello Solid',
|
|
120
97
|
content: 'Stored as RDF in a Pod document.',
|
|
121
98
|
createdAt: new Date(),
|
|
122
99
|
});
|
|
123
100
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
101
|
+
if (!created) {
|
|
102
|
+
throw new Error('Create failed');
|
|
103
|
+
}
|
|
127
104
|
|
|
128
|
-
const
|
|
129
|
-
id: 'post-1',
|
|
130
|
-
title: 'Hello Solid',
|
|
131
|
-
content: 'Stored as RDF in a Pod document.',
|
|
132
|
-
createdAt: new Date(),
|
|
133
|
-
});
|
|
105
|
+
const post = client.entity(posts, created['@id']);
|
|
134
106
|
|
|
135
|
-
|
|
107
|
+
console.log(await post.get());
|
|
136
108
|
await post.update({ title: 'Updated title' });
|
|
137
109
|
await post.delete();
|
|
138
110
|
```
|
|
139
111
|
|
|
140
|
-
|
|
112
|
+
## Drizzle-style exact operations
|
|
141
113
|
|
|
142
|
-
|
|
114
|
+
```ts
|
|
115
|
+
import { drizzle, podTable, string } from '@undefineds.co/drizzle-solid';
|
|
143
116
|
|
|
144
|
-
|
|
117
|
+
const posts = podTable('posts', {
|
|
118
|
+
id: string('id').primaryKey(),
|
|
119
|
+
title: string('title').predicate('http://schema.org/headline'),
|
|
120
|
+
}, {
|
|
121
|
+
base: 'https://alice.example/data/posts/',
|
|
122
|
+
subjectTemplate: '{id}.ttl',
|
|
123
|
+
type: 'http://schema.org/CreativeWork',
|
|
124
|
+
});
|
|
145
125
|
|
|
146
|
-
|
|
126
|
+
const db = drizzle(session);
|
|
127
|
+
await db.init(posts);
|
|
147
128
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
base: 'https://alice.example/profile/card',
|
|
129
|
+
await db.insert(posts).values({
|
|
130
|
+
id: 'post-1',
|
|
131
|
+
title: 'Hello Solid',
|
|
152
132
|
});
|
|
153
|
-
```
|
|
154
133
|
|
|
155
|
-
|
|
134
|
+
const row = await db.findByLocator(posts, { id: 'post-1' });
|
|
156
135
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
const profileTable = db.createTable(profileSchema, {
|
|
160
|
-
base: 'https://alice.example/profile/card',
|
|
136
|
+
await db.updateByLocator(posts, { id: 'post-1' }, {
|
|
137
|
+
title: 'Updated title',
|
|
161
138
|
});
|
|
139
|
+
|
|
140
|
+
await db.deleteByLocator(posts, { id: 'post-1' });
|
|
162
141
|
```
|
|
163
142
|
|
|
164
|
-
##
|
|
143
|
+
## How Pod layout works
|
|
144
|
+
|
|
145
|
+
Every model describes both shape and placement.
|
|
146
|
+
|
|
147
|
+
- `base` defines where documents live
|
|
148
|
+
- `subjectTemplate` defines how business fields map to an IRI
|
|
149
|
+
- `type` is the primary persisted `rdf:type`
|
|
150
|
+
|
|
151
|
+
Common `subjectTemplate` patterns:
|
|
165
152
|
|
|
166
|
-
`
|
|
153
|
+
- `#{id}` — many entities in one document
|
|
154
|
+
- `{id}.ttl` — one document per entity
|
|
155
|
+
- `{id}.ttl#it` — one document per entity with a stable fragment
|
|
156
|
+
- `{chatId}/messages.ttl#{id}` — partitioned layout with multiple locator variables
|
|
167
157
|
|
|
168
|
-
|
|
158
|
+
If your template uses multiple variables, exact lookup requires either:
|
|
169
159
|
|
|
170
|
-
-
|
|
171
|
-
-
|
|
172
|
-
- `{id}.ttl#it`: one document per entity with stable in-document fragment
|
|
173
|
-
- `{chatId}/messages.ttl#{id}`: multi-variable layouts and partitioned resources
|
|
160
|
+
- the full IRI, or
|
|
161
|
+
- a complete locator with every required variable
|
|
174
162
|
|
|
175
|
-
|
|
163
|
+
## Collection reads vs exact entity operations
|
|
176
164
|
|
|
177
|
-
|
|
165
|
+
This is the most important runtime rule.
|
|
178
166
|
|
|
179
|
-
|
|
167
|
+
### Collection reads
|
|
180
168
|
|
|
181
|
-
|
|
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
|
|
169
|
+
Use collection reads when you want a list or filtered subset:
|
|
185
170
|
|
|
186
|
-
|
|
171
|
+
- `client.collection(table).list(...)`
|
|
172
|
+
- `db.select().from(table)...`
|
|
173
|
+
- `db.query.<table>.findMany(...)`
|
|
174
|
+
|
|
175
|
+
### Exact entity operations
|
|
176
|
+
|
|
177
|
+
Use exact-target helpers when you mean one concrete entity:
|
|
178
|
+
|
|
179
|
+
- `client.entity(table, iri)`
|
|
180
|
+
- `db.findByIri(table, iri)`
|
|
181
|
+
- `db.findByLocator(table, locator)`
|
|
182
|
+
- `db.updateByIri(...)`
|
|
183
|
+
- `db.updateByLocator(...)`
|
|
184
|
+
- `db.deleteByIri(...)`
|
|
185
|
+
- `db.deleteByLocator(...)`
|
|
187
186
|
|
|
188
|
-
-
|
|
189
|
-
- 什么时候你已经需要一个确定的实体目标
|
|
187
|
+
Do not rely on `where({ id: ... })` or `where(eq(table.id, ...))` as an exact-target shortcut.
|
|
190
188
|
|
|
191
|
-
##
|
|
189
|
+
## SPARQL support and backend behavior
|
|
192
190
|
|
|
193
|
-
|
|
191
|
+
`drizzle-solid` works across different Solid runtimes, but capabilities differ.
|
|
194
192
|
|
|
195
193
|
| Capability | Community Solid Server | xpod |
|
|
196
194
|
| --- | --- | --- |
|
|
@@ -198,15 +196,19 @@ Reads and writes do not intentionally behave the same way.
|
|
|
198
196
|
| Document notifications | ✅ | ✅ |
|
|
199
197
|
| Drizzle-style read facade | ✅ | ✅ |
|
|
200
198
|
| SPARQL pushdown | ⚠️ Limited / often client-assisted | ✅ Better in-process support |
|
|
201
|
-
| Filter / aggregation pushdown | ❌ Mostly
|
|
199
|
+
| Filter / aggregation pushdown | ❌ Mostly client-side execution | ✅ Better server-side support |
|
|
202
200
|
| Federated queries | ⚠️ Client-side federation | ⚠️ Client-side federation |
|
|
203
201
|
| In-process local runtime | ⚠️ External setup | ✅ via `@undefineds.co/xpod` |
|
|
204
202
|
|
|
205
|
-
|
|
203
|
+
Practical rule:
|
|
206
204
|
|
|
207
|
-
|
|
205
|
+
- if you have a SPARQL endpoint or sidecar, use it for collection queries
|
|
206
|
+
- if you only have plain LDP document access, exact-target reads and writes still work
|
|
207
|
+
- plain-LDP document mode does not silently widen exact operations into scans
|
|
208
208
|
|
|
209
|
-
|
|
209
|
+
## Examples
|
|
210
|
+
|
|
211
|
+
The canonical examples in `examples/` are part of the integration verification flow:
|
|
210
212
|
|
|
211
213
|
- `examples/01-quick-start.ts`
|
|
212
214
|
- `examples/02-relational-query.ts`
|
|
@@ -219,42 +221,24 @@ The canonical examples in `examples/` are part of the real integration verificat
|
|
|
219
221
|
- `examples/08-schema-inheritance.ts`
|
|
220
222
|
- `examples/09-multi-variable-templates.ts`
|
|
221
223
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
## Migration
|
|
224
|
+
## Documentation
|
|
225
225
|
|
|
226
|
-
|
|
226
|
+
Start here:
|
|
227
227
|
|
|
228
|
+
- `docs/guides/installation.md`
|
|
229
|
+
- `docs/api/README.md`
|
|
228
230
|
- `docs/guides/migrating-from-drizzle-orm.md`
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
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 主心智
|
|
236
|
-
|
|
237
|
-
而不是要求你第一步先改掉构造函数名。
|
|
238
|
-
|
|
239
|
-
## Documentation map
|
|
240
|
-
|
|
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
|
|
245
|
-
- `docs/guides/data-discovery.md` — discovery workflows
|
|
246
|
-
- `docs/guides/notifications.md` — notification flows
|
|
247
|
-
- `docs/xpod-features.md` — xpod runtime notes
|
|
248
|
-
- `ACTION-PLAN.md` — parity and implementation plan
|
|
231
|
+
- `docs/guides/multi-variable-templates.md`
|
|
232
|
+
- `docs/guides/notifications.md`
|
|
233
|
+
- `docs/guides/data-discovery.md`
|
|
249
234
|
|
|
250
235
|
## Contributing
|
|
251
236
|
|
|
252
237
|
Before pushing:
|
|
253
238
|
|
|
254
239
|
```bash
|
|
255
|
-
yarn
|
|
256
|
-
yarn
|
|
257
|
-
SOLID_ENABLE_REAL_TESTS=true SOLID_SERIAL_TESTS=true yarn vitest --run --silent
|
|
240
|
+
yarn quality
|
|
241
|
+
SOLID_ENABLE_REAL_TESTS=true SOLID_SERIAL_TESTS=true yarn vitest --run tests/integration/css
|
|
258
242
|
```
|
|
259
243
|
|
|
260
244
|
Examples must remain runnable and verified.
|
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
# drizzle-solid
|
|
2
|
+
|
|
3
|
+
English version: [`README.md`](README.md)
|
|
4
|
+
|
|
5
|
+
`drizzle-solid` 用类型化 schema 和 Drizzle 对齐的开发体验,把应用自有数据存进 Solid Pod。
|
|
6
|
+
|
|
7
|
+
适合在你需要下面这些能力时使用:
|
|
8
|
+
|
|
9
|
+
- 用 TypeScript schema 描述 Pod 数据
|
|
10
|
+
- 用 `base` 和 `subjectTemplate` 明确声明数据布局
|
|
11
|
+
- 在 `pod()` 和 `drizzle()` 两种 API 风格之间选择
|
|
12
|
+
- 通过 IRI 或 locator 精确读写单个实体
|
|
13
|
+
- 在后端提供查询能力时走 SPARQL 读取
|
|
14
|
+
|
|
15
|
+
## 它擅长什么
|
|
16
|
+
|
|
17
|
+
- 应用自有数据存储在你的 Pod 或用户 Pod 中
|
|
18
|
+
- 对已知资源布局做类型化 CRUD
|
|
19
|
+
- 从 `drizzle-orm` 逐步迁移,而不假装 Pod 就是 SQL 表
|
|
20
|
+
- 使用 IRI、文档、链接、发现、通知、联邦等 Pod 原生概念
|
|
21
|
+
|
|
22
|
+
## 它不打算做什么
|
|
23
|
+
|
|
24
|
+
- 通用 RDF 图探索工具
|
|
25
|
+
- 所有 Solid 后端上的完整 SQL / 数据库能力对齐
|
|
26
|
+
- 隐藏 Pod 边界、权限或网络成本
|
|
27
|
+
- raw SQL 优先的抽象层
|
|
28
|
+
|
|
29
|
+
## 安装
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
yarn add @undefineds.co/drizzle-solid drizzle-orm
|
|
33
|
+
# 可选:当你的应用需要内置 SPARQL 引擎时再安装
|
|
34
|
+
yarn add @comunica/query-sparql-solid
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npm install @undefineds.co/drizzle-solid drizzle-orm
|
|
39
|
+
# 可选:当你的应用需要内置 SPARQL 引擎时再安装
|
|
40
|
+
npm install @comunica/query-sparql-solid
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
`@comunica/query-sparql-solid` 是可选 peer dependency。
|
|
44
|
+
|
|
45
|
+
当前支持范围:
|
|
46
|
+
|
|
47
|
+
- 支持:`@comunica/query-sparql-solid` `4.x`
|
|
48
|
+
- 当前不在支持矩阵中:`3.x`
|
|
49
|
+
|
|
50
|
+
如果宿主运行时已经自带 Comunica,就注入那一份,不要再装第二份。
|
|
51
|
+
|
|
52
|
+
## 选择 API 风格
|
|
53
|
+
|
|
54
|
+
### `pod(session)`
|
|
55
|
+
|
|
56
|
+
适合新代码,或者你希望 API 直接体现 Solid 语义:
|
|
57
|
+
|
|
58
|
+
- `collection(table)`
|
|
59
|
+
- `entity(table, iri)`
|
|
60
|
+
- `bind(schema, options)`
|
|
61
|
+
- `sparql(query)`
|
|
62
|
+
|
|
63
|
+
### `drizzle(session)`
|
|
64
|
+
|
|
65
|
+
适合从 `drizzle-orm` 迁移,或者你想保留 builder 形状:
|
|
66
|
+
|
|
67
|
+
- `select / insert / update / delete`
|
|
68
|
+
- `db.query.<table>`
|
|
69
|
+
- `findByLocator / findByIri`
|
|
70
|
+
- `updateByLocator / updateByIri`
|
|
71
|
+
- `deleteByLocator / deleteByIri`
|
|
72
|
+
|
|
73
|
+
两者底层是同一个运行时。区别是 API 组织方式,不是存储行为。
|
|
74
|
+
|
|
75
|
+
## 快速开始
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
import { pod, podTable, string, datetime } from '@undefineds.co/drizzle-solid';
|
|
79
|
+
|
|
80
|
+
const posts = podTable('posts', {
|
|
81
|
+
id: string('id').primaryKey(),
|
|
82
|
+
title: string('title').predicate('http://schema.org/headline'),
|
|
83
|
+
content: string('content').predicate('http://schema.org/text'),
|
|
84
|
+
createdAt: datetime('createdAt').predicate('http://schema.org/dateCreated'),
|
|
85
|
+
}, {
|
|
86
|
+
base: 'https://alice.example/data/posts/',
|
|
87
|
+
subjectTemplate: '{id}.ttl',
|
|
88
|
+
type: 'http://schema.org/CreativeWork',
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
const client = pod(session);
|
|
92
|
+
await client.init(posts);
|
|
93
|
+
|
|
94
|
+
const created = await client.collection(posts).create({
|
|
95
|
+
id: 'post-1',
|
|
96
|
+
title: 'Hello Solid',
|
|
97
|
+
content: 'Stored as RDF in a Pod document.',
|
|
98
|
+
createdAt: new Date(),
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
if (!created) {
|
|
102
|
+
throw new Error('Create failed');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const post = client.entity(posts, created['@id']);
|
|
106
|
+
|
|
107
|
+
console.log(await post.get());
|
|
108
|
+
await post.update({ title: 'Updated title' });
|
|
109
|
+
await post.delete();
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Drizzle 风格的精确操作
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
import { drizzle, podTable, string } from '@undefineds.co/drizzle-solid';
|
|
116
|
+
|
|
117
|
+
const posts = podTable('posts', {
|
|
118
|
+
id: string('id').primaryKey(),
|
|
119
|
+
title: string('title').predicate('http://schema.org/headline'),
|
|
120
|
+
}, {
|
|
121
|
+
base: 'https://alice.example/data/posts/',
|
|
122
|
+
subjectTemplate: '{id}.ttl',
|
|
123
|
+
type: 'http://schema.org/CreativeWork',
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
const db = drizzle(session);
|
|
127
|
+
await db.init(posts);
|
|
128
|
+
|
|
129
|
+
await db.insert(posts).values({
|
|
130
|
+
id: 'post-1',
|
|
131
|
+
title: 'Hello Solid',
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
const row = await db.findByLocator(posts, { id: 'post-1' });
|
|
135
|
+
|
|
136
|
+
await db.updateByLocator(posts, { id: 'post-1' }, {
|
|
137
|
+
title: 'Updated title',
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
await db.deleteByLocator(posts, { id: 'post-1' });
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Pod 布局怎么理解
|
|
144
|
+
|
|
145
|
+
每个模型不仅描述字段,也描述落点。
|
|
146
|
+
|
|
147
|
+
- `base` 决定文档位置
|
|
148
|
+
- `subjectTemplate` 决定业务字段如何映射成 IRI
|
|
149
|
+
- `type` 是主持久化 `rdf:type`
|
|
150
|
+
|
|
151
|
+
常见 `subjectTemplate`:
|
|
152
|
+
|
|
153
|
+
- `#{id}`:一个文档里多个实体
|
|
154
|
+
- `{id}.ttl`:一个实体一个文档
|
|
155
|
+
- `{id}.ttl#it`:一个实体一个文档,并带稳定 fragment
|
|
156
|
+
- `{chatId}/messages.ttl#{id}`:多变量分区布局
|
|
157
|
+
|
|
158
|
+
如果模板用了多个变量,精确定位就必须提供:
|
|
159
|
+
|
|
160
|
+
- 完整 IRI,或
|
|
161
|
+
- 完整 locator
|
|
162
|
+
|
|
163
|
+
## 集合读取 vs 精确实体操作
|
|
164
|
+
|
|
165
|
+
这是最重要的一条运行时规则。
|
|
166
|
+
|
|
167
|
+
### 集合读取
|
|
168
|
+
|
|
169
|
+
当你要拿一批数据时,用:
|
|
170
|
+
|
|
171
|
+
- `client.collection(table).list(...)`
|
|
172
|
+
- `db.select().from(table)...`
|
|
173
|
+
- `db.query.<table>.findMany(...)`
|
|
174
|
+
|
|
175
|
+
### 精确实体操作
|
|
176
|
+
|
|
177
|
+
当你要操作一个明确实体时,用:
|
|
178
|
+
|
|
179
|
+
- `client.entity(table, iri)`
|
|
180
|
+
- `db.findByIri(table, iri)`
|
|
181
|
+
- `db.findByLocator(table, locator)`
|
|
182
|
+
- `db.updateByIri(...)`
|
|
183
|
+
- `db.updateByLocator(...)`
|
|
184
|
+
- `db.deleteByIri(...)`
|
|
185
|
+
- `db.deleteByLocator(...)`
|
|
186
|
+
|
|
187
|
+
不要再把 `where({ id: ... })` 或 `where(eq(table.id, ...))` 当成精确单体捷径。
|
|
188
|
+
|
|
189
|
+
## SPARQL 支持与后端差异
|
|
190
|
+
|
|
191
|
+
`drizzle-solid` 可以跑在不同 Solid 运行时上,但能力有差异。
|
|
192
|
+
|
|
193
|
+
| 能力 | Community Solid Server | xpod |
|
|
194
|
+
| --- | --- | --- |
|
|
195
|
+
| 基础 CRUD | ✅ | ✅ |
|
|
196
|
+
| 文档通知 | ✅ | ✅ |
|
|
197
|
+
| Drizzle 风格读 facade | ✅ | ✅ |
|
|
198
|
+
| SPARQL 下推 | ⚠️ 有限 / 经常依赖客户端辅助 | ✅ 更好的进程内支持 |
|
|
199
|
+
| 过滤 / 聚合下推 | ❌ 多数走客户端执行 | ✅ 更好的服务端支持 |
|
|
200
|
+
| 联邦查询 | ⚠️ 客户端联邦 | ⚠️ 客户端联邦 |
|
|
201
|
+
| 进程内本地运行时 | ⚠️ 需要额外启动 | ✅ 通过 `@undefineds.co/xpod` |
|
|
202
|
+
|
|
203
|
+
实用规则:
|
|
204
|
+
|
|
205
|
+
- 有 SPARQL endpoint 或 sidecar 时,集合查询优先走它
|
|
206
|
+
- 只有 plain LDP document access 时,精确读写仍然可用
|
|
207
|
+
- plain-LDP document mode 不会把精确操作静默扩成扫描
|
|
208
|
+
|
|
209
|
+
## 示例
|
|
210
|
+
|
|
211
|
+
仓库里的标准示例会进入集成验证:
|
|
212
|
+
|
|
213
|
+
- `examples/01-quick-start.ts`
|
|
214
|
+
- `examples/02-relational-query.ts`
|
|
215
|
+
- `examples/03-zero-config-discovery.ts`
|
|
216
|
+
- `examples/04-notifications.ts`
|
|
217
|
+
- `examples/05-data-discovery.ts`
|
|
218
|
+
- `examples/06-federated-query.ts`
|
|
219
|
+
- `examples/07-hooks-and-profile.ts`
|
|
220
|
+
- `examples/08-iri-based-operations.ts`
|
|
221
|
+
- `examples/08-schema-inheritance.ts`
|
|
222
|
+
- `examples/09-multi-variable-templates.ts`
|
|
223
|
+
|
|
224
|
+
## 文档入口
|
|
225
|
+
|
|
226
|
+
建议从这里开始:
|
|
227
|
+
|
|
228
|
+
- `docs/guides/installation.zh-CN.md`
|
|
229
|
+
- `docs/api/README.zh-CN.md`
|
|
230
|
+
- `docs/guides/migrating-from-drizzle-orm.zh-CN.md`
|
|
231
|
+
- `docs/guides/multi-variable-templates.zh-CN.md`
|
|
232
|
+
- `docs/guides/notifications.md`
|
|
233
|
+
- `docs/guides/data-discovery.md`
|
|
234
|
+
|
|
235
|
+
## 贡献
|
|
236
|
+
|
|
237
|
+
推送前运行:
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
yarn quality
|
|
241
|
+
SOLID_ENABLE_REAL_TESTS=true SOLID_SERIAL_TESTS=true yarn vitest --run tests/integration/css
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
示例必须保持可运行且已验证。
|
|
245
|
+
|
|
246
|
+
## License
|
|
247
|
+
|
|
248
|
+
MIT
|
|
249
|
+
|
|
250
|
+
## 相关链接
|
|
251
|
+
|
|
252
|
+
- GitHub: https://github.com/undefinedsco/drizzle-solid
|
|
253
|
+
- npm: https://www.npmjs.com/package/@undefineds.co/drizzle-solid
|
|
254
|
+
- xpod: https://github.com/undefinedsco/xpod
|