@undefineds.co/drizzle-solid 0.2.11 → 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.
Files changed (272) hide show
  1. package/README.md +178 -471
  2. package/dist/core/ast-to-sparql.d.ts +1 -1
  3. package/dist/core/ast-to-sparql.js +1 -1
  4. package/dist/core/comunica-patch.d.ts +1 -6
  5. package/dist/core/comunica-patch.d.ts.map +1 -1
  6. package/dist/core/comunica-patch.js +16 -19
  7. package/dist/core/comunica-patch.js.map +1 -1
  8. package/dist/core/execution/sparql-strategy.d.ts +9 -37
  9. package/dist/core/execution/sparql-strategy.d.ts.map +1 -1
  10. package/dist/core/execution/sparql-strategy.js +44 -148
  11. package/dist/core/execution/sparql-strategy.js.map +1 -1
  12. package/dist/core/execution/strategy-factory.d.ts +7 -20
  13. package/dist/core/execution/strategy-factory.d.ts.map +1 -1
  14. package/dist/core/execution/strategy-factory.js +2 -17
  15. package/dist/core/execution/strategy-factory.js.map +1 -1
  16. package/dist/core/expressions.d.ts +4 -0
  17. package/dist/core/expressions.d.ts.map +1 -1
  18. package/dist/core/expressions.js +8 -1
  19. package/dist/core/expressions.js.map +1 -1
  20. package/dist/core/order-by.d.ts +9 -0
  21. package/dist/core/order-by.d.ts.map +1 -0
  22. package/dist/core/order-by.js +20 -0
  23. package/dist/core/order-by.js.map +1 -0
  24. package/dist/core/pod-database.d.ts +8 -2
  25. package/dist/core/pod-database.d.ts.map +1 -1
  26. package/dist/core/pod-database.js +142 -52
  27. package/dist/core/pod-database.js.map +1 -1
  28. package/dist/core/pod-dialect.d.ts +12 -1
  29. package/dist/core/pod-dialect.d.ts.map +1 -1
  30. package/dist/core/pod-dialect.js +85 -4
  31. package/dist/core/pod-dialect.js.map +1 -1
  32. package/dist/core/query-builders/delete-query-builder.d.ts +7 -12
  33. package/dist/core/query-builders/delete-query-builder.d.ts.map +1 -1
  34. package/dist/core/query-builders/delete-query-builder.js +47 -20
  35. package/dist/core/query-builders/delete-query-builder.js.map +1 -1
  36. package/dist/core/query-builders/helpers.d.ts +7 -0
  37. package/dist/core/query-builders/helpers.d.ts.map +1 -1
  38. package/dist/core/query-builders/helpers.js +169 -0
  39. package/dist/core/query-builders/helpers.js.map +1 -1
  40. package/dist/core/query-builders/insert-query-builder.d.ts +10 -3
  41. package/dist/core/query-builders/insert-query-builder.d.ts.map +1 -1
  42. package/dist/core/query-builders/insert-query-builder.js +50 -6
  43. package/dist/core/query-builders/insert-query-builder.js.map +1 -1
  44. package/dist/core/query-builders/select-query-builder.d.ts +31 -3
  45. package/dist/core/query-builders/select-query-builder.d.ts.map +1 -1
  46. package/dist/core/query-builders/select-query-builder.js +415 -95
  47. package/dist/core/query-builders/select-query-builder.js.map +1 -1
  48. package/dist/core/query-builders/types.d.ts +5 -3
  49. package/dist/core/query-builders/types.d.ts.map +1 -1
  50. package/dist/core/query-builders/update-query-builder.d.ts +8 -12
  51. package/dist/core/query-builders/update-query-builder.d.ts.map +1 -1
  52. package/dist/core/query-builders/update-query-builder.js +63 -21
  53. package/dist/core/query-builders/update-query-builder.js.map +1 -1
  54. package/dist/core/query-conditions.d.ts +17 -16
  55. package/dist/core/query-conditions.d.ts.map +1 -1
  56. package/dist/core/query-conditions.js.map +1 -1
  57. package/dist/core/resource-resolver/base-resolver.d.ts +8 -4
  58. package/dist/core/resource-resolver/base-resolver.d.ts.map +1 -1
  59. package/dist/core/resource-resolver/base-resolver.js +39 -36
  60. package/dist/core/resource-resolver/base-resolver.js.map +1 -1
  61. package/dist/core/resource-resolver/document-resolver.d.ts.map +1 -1
  62. package/dist/core/resource-resolver/document-resolver.js +75 -78
  63. package/dist/core/resource-resolver/document-resolver.js.map +1 -1
  64. package/dist/core/schema/columns.d.ts +6 -6
  65. package/dist/core/schema/columns.d.ts.map +1 -1
  66. package/dist/core/schema/columns.js +18 -18
  67. package/dist/core/schema/columns.js.map +1 -1
  68. package/dist/core/schema/defs.d.ts +4 -4
  69. package/dist/core/schema/defs.d.ts.map +1 -1
  70. package/dist/core/schema/pod-table.d.ts +1 -0
  71. package/dist/core/schema/pod-table.d.ts.map +1 -1
  72. package/dist/core/schema/pod-table.js +69 -2
  73. package/dist/core/schema/pod-table.js.map +1 -1
  74. package/dist/core/select-plan.d.ts +6 -3
  75. package/dist/core/select-plan.d.ts.map +1 -1
  76. package/dist/core/services/pod-services.d.ts +7 -5
  77. package/dist/core/services/pod-services.d.ts.map +1 -1
  78. package/dist/core/services/pod-services.js +7 -5
  79. package/dist/core/services/pod-services.js.map +1 -1
  80. package/dist/core/shape/generator.js +3 -3
  81. package/dist/core/shape/generator.js.map +1 -1
  82. package/dist/core/shape/manager.js +5 -5
  83. package/dist/core/shape/manager.js.map +1 -1
  84. package/dist/core/sparql/builder/expression-builder.d.ts +15 -5
  85. package/dist/core/sparql/builder/expression-builder.d.ts.map +1 -1
  86. package/dist/core/sparql/builder/expression-builder.js +42 -19
  87. package/dist/core/sparql/builder/expression-builder.js.map +1 -1
  88. package/dist/core/sparql/builder/select-builder.d.ts +10 -1
  89. package/dist/core/sparql/builder/select-builder.d.ts.map +1 -1
  90. package/dist/core/sparql/builder/select-builder.js +148 -3
  91. package/dist/core/sparql/builder/select-builder.js.map +1 -1
  92. package/dist/core/sparql/builder/update-builder.d.ts +1 -1
  93. package/dist/core/sparql/builder/update-builder.d.ts.map +1 -1
  94. package/dist/core/sparql/builder/update-builder.js +4 -4
  95. package/dist/core/sparql/builder/update-builder.js.map +1 -1
  96. package/dist/core/sparql/helpers.js +3 -3
  97. package/dist/core/sparql/helpers.js.map +1 -1
  98. package/dist/core/sparql-engine.d.ts +16 -0
  99. package/dist/core/sparql-engine.d.ts.map +1 -0
  100. package/dist/core/sparql-engine.js +128 -0
  101. package/dist/core/sparql-engine.js.map +1 -0
  102. package/dist/core/sparql-executor.d.ts +3 -1
  103. package/dist/core/sparql-executor.d.ts.map +1 -1
  104. package/dist/core/sparql-executor.js +3 -4
  105. package/dist/core/sparql-executor.js.map +1 -1
  106. package/dist/core/triple/handlers/inverse.js +4 -4
  107. package/dist/core/triple/handlers/inverse.js.map +1 -1
  108. package/dist/core/triple/handlers/uri.d.ts +1 -1
  109. package/dist/core/triple/handlers/uri.js +3 -3
  110. package/dist/core/triple/handlers/uri.js.map +1 -1
  111. package/dist/core/uri/resolver.d.ts +9 -9
  112. package/dist/core/uri/resolver.d.ts.map +1 -1
  113. package/dist/core/uri/resolver.js +51 -51
  114. package/dist/core/uri/resolver.js.map +1 -1
  115. package/dist/core/uri/types.d.ts +10 -10
  116. package/dist/core/uri/types.d.ts.map +1 -1
  117. package/dist/core/utils/debug-logger.d.ts +19 -0
  118. package/dist/core/utils/debug-logger.d.ts.map +1 -0
  119. package/dist/core/utils/debug-logger.js +59 -0
  120. package/dist/core/utils/debug-logger.js.map +1 -0
  121. package/dist/driver.d.ts +14 -1
  122. package/dist/driver.d.ts.map +1 -1
  123. package/dist/driver.js +2 -5
  124. package/dist/driver.js.map +1 -1
  125. package/dist/esm/core/ast-to-sparql.d.ts +1 -1
  126. package/dist/esm/core/ast-to-sparql.js +1 -1
  127. package/dist/esm/core/comunica-patch.d.ts +1 -6
  128. package/dist/esm/core/comunica-patch.d.ts.map +1 -1
  129. package/dist/esm/core/comunica-patch.js +13 -20
  130. package/dist/esm/core/comunica-patch.js.map +1 -1
  131. package/dist/esm/core/execution/sparql-strategy.d.ts +9 -37
  132. package/dist/esm/core/execution/sparql-strategy.d.ts.map +1 -1
  133. package/dist/esm/core/execution/sparql-strategy.js +44 -148
  134. package/dist/esm/core/execution/sparql-strategy.js.map +1 -1
  135. package/dist/esm/core/execution/strategy-factory.d.ts +7 -20
  136. package/dist/esm/core/execution/strategy-factory.d.ts.map +1 -1
  137. package/dist/esm/core/execution/strategy-factory.js +2 -17
  138. package/dist/esm/core/execution/strategy-factory.js.map +1 -1
  139. package/dist/esm/core/expressions.d.ts +4 -0
  140. package/dist/esm/core/expressions.d.ts.map +1 -1
  141. package/dist/esm/core/expressions.js +6 -0
  142. package/dist/esm/core/expressions.js.map +1 -1
  143. package/dist/esm/core/order-by.d.ts +9 -0
  144. package/dist/esm/core/order-by.d.ts.map +1 -0
  145. package/dist/esm/core/order-by.js +15 -0
  146. package/dist/esm/core/order-by.js.map +1 -0
  147. package/dist/esm/core/pod-database.d.ts +8 -2
  148. package/dist/esm/core/pod-database.d.ts.map +1 -1
  149. package/dist/esm/core/pod-database.js +143 -53
  150. package/dist/esm/core/pod-database.js.map +1 -1
  151. package/dist/esm/core/pod-dialect.d.ts +12 -1
  152. package/dist/esm/core/pod-dialect.d.ts.map +1 -1
  153. package/dist/esm/core/pod-dialect.js +85 -4
  154. package/dist/esm/core/pod-dialect.js.map +1 -1
  155. package/dist/esm/core/query-builders/delete-query-builder.d.ts +7 -12
  156. package/dist/esm/core/query-builders/delete-query-builder.d.ts.map +1 -1
  157. package/dist/esm/core/query-builders/delete-query-builder.js +48 -21
  158. package/dist/esm/core/query-builders/delete-query-builder.js.map +1 -1
  159. package/dist/esm/core/query-builders/helpers.d.ts +7 -0
  160. package/dist/esm/core/query-builders/helpers.d.ts.map +1 -1
  161. package/dist/esm/core/query-builders/helpers.js +164 -0
  162. package/dist/esm/core/query-builders/helpers.js.map +1 -1
  163. package/dist/esm/core/query-builders/insert-query-builder.d.ts +10 -3
  164. package/dist/esm/core/query-builders/insert-query-builder.d.ts.map +1 -1
  165. package/dist/esm/core/query-builders/insert-query-builder.js +50 -6
  166. package/dist/esm/core/query-builders/insert-query-builder.js.map +1 -1
  167. package/dist/esm/core/query-builders/select-query-builder.d.ts +31 -3
  168. package/dist/esm/core/query-builders/select-query-builder.d.ts.map +1 -1
  169. package/dist/esm/core/query-builders/select-query-builder.js +417 -97
  170. package/dist/esm/core/query-builders/select-query-builder.js.map +1 -1
  171. package/dist/esm/core/query-builders/types.d.ts +5 -3
  172. package/dist/esm/core/query-builders/types.d.ts.map +1 -1
  173. package/dist/esm/core/query-builders/update-query-builder.d.ts +8 -12
  174. package/dist/esm/core/query-builders/update-query-builder.d.ts.map +1 -1
  175. package/dist/esm/core/query-builders/update-query-builder.js +64 -22
  176. package/dist/esm/core/query-builders/update-query-builder.js.map +1 -1
  177. package/dist/esm/core/query-conditions.d.ts +17 -16
  178. package/dist/esm/core/query-conditions.d.ts.map +1 -1
  179. package/dist/esm/core/query-conditions.js.map +1 -1
  180. package/dist/esm/core/resource-resolver/base-resolver.d.ts +8 -4
  181. package/dist/esm/core/resource-resolver/base-resolver.d.ts.map +1 -1
  182. package/dist/esm/core/resource-resolver/base-resolver.js +39 -36
  183. package/dist/esm/core/resource-resolver/base-resolver.js.map +1 -1
  184. package/dist/esm/core/resource-resolver/document-resolver.d.ts.map +1 -1
  185. package/dist/esm/core/resource-resolver/document-resolver.js +75 -78
  186. package/dist/esm/core/resource-resolver/document-resolver.js.map +1 -1
  187. package/dist/esm/core/schema/columns.d.ts +6 -6
  188. package/dist/esm/core/schema/columns.d.ts.map +1 -1
  189. package/dist/esm/core/schema/columns.js +18 -18
  190. package/dist/esm/core/schema/columns.js.map +1 -1
  191. package/dist/esm/core/schema/defs.d.ts +4 -4
  192. package/dist/esm/core/schema/defs.d.ts.map +1 -1
  193. package/dist/esm/core/schema/pod-table.d.ts +1 -0
  194. package/dist/esm/core/schema/pod-table.d.ts.map +1 -1
  195. package/dist/esm/core/schema/pod-table.js +68 -2
  196. package/dist/esm/core/schema/pod-table.js.map +1 -1
  197. package/dist/esm/core/select-plan.d.ts +6 -3
  198. package/dist/esm/core/select-plan.d.ts.map +1 -1
  199. package/dist/esm/core/services/pod-services.d.ts +7 -5
  200. package/dist/esm/core/services/pod-services.d.ts.map +1 -1
  201. package/dist/esm/core/services/pod-services.js +7 -5
  202. package/dist/esm/core/services/pod-services.js.map +1 -1
  203. package/dist/esm/core/shape/generator.js +3 -3
  204. package/dist/esm/core/shape/generator.js.map +1 -1
  205. package/dist/esm/core/shape/manager.js +5 -5
  206. package/dist/esm/core/shape/manager.js.map +1 -1
  207. package/dist/esm/core/sparql/builder/expression-builder.d.ts +15 -5
  208. package/dist/esm/core/sparql/builder/expression-builder.d.ts.map +1 -1
  209. package/dist/esm/core/sparql/builder/expression-builder.js +42 -19
  210. package/dist/esm/core/sparql/builder/expression-builder.js.map +1 -1
  211. package/dist/esm/core/sparql/builder/select-builder.d.ts +10 -1
  212. package/dist/esm/core/sparql/builder/select-builder.d.ts.map +1 -1
  213. package/dist/esm/core/sparql/builder/select-builder.js +149 -4
  214. package/dist/esm/core/sparql/builder/select-builder.js.map +1 -1
  215. package/dist/esm/core/sparql/builder/update-builder.d.ts +1 -1
  216. package/dist/esm/core/sparql/builder/update-builder.d.ts.map +1 -1
  217. package/dist/esm/core/sparql/builder/update-builder.js +4 -4
  218. package/dist/esm/core/sparql/builder/update-builder.js.map +1 -1
  219. package/dist/esm/core/sparql/helpers.js +3 -3
  220. package/dist/esm/core/sparql/helpers.js.map +1 -1
  221. package/dist/esm/core/sparql-engine.d.ts +16 -0
  222. package/dist/esm/core/sparql-engine.d.ts.map +1 -0
  223. package/dist/esm/core/sparql-engine.js +89 -0
  224. package/dist/esm/core/sparql-engine.js.map +1 -0
  225. package/dist/esm/core/sparql-executor.d.ts +3 -1
  226. package/dist/esm/core/sparql-executor.d.ts.map +1 -1
  227. package/dist/esm/core/sparql-executor.js +3 -4
  228. package/dist/esm/core/sparql-executor.js.map +1 -1
  229. package/dist/esm/core/triple/handlers/inverse.js +4 -4
  230. package/dist/esm/core/triple/handlers/inverse.js.map +1 -1
  231. package/dist/esm/core/triple/handlers/uri.d.ts +1 -1
  232. package/dist/esm/core/triple/handlers/uri.js +3 -3
  233. package/dist/esm/core/triple/handlers/uri.js.map +1 -1
  234. package/dist/esm/core/uri/resolver.d.ts +9 -9
  235. package/dist/esm/core/uri/resolver.d.ts.map +1 -1
  236. package/dist/esm/core/uri/resolver.js +51 -51
  237. package/dist/esm/core/uri/resolver.js.map +1 -1
  238. package/dist/esm/core/uri/types.d.ts +10 -10
  239. package/dist/esm/core/uri/types.d.ts.map +1 -1
  240. package/dist/esm/core/utils/debug-logger.d.ts +19 -0
  241. package/dist/esm/core/utils/debug-logger.d.ts.map +1 -0
  242. package/dist/esm/core/utils/debug-logger.js +53 -0
  243. package/dist/esm/core/utils/debug-logger.js.map +1 -0
  244. package/dist/esm/driver.d.ts +14 -1
  245. package/dist/esm/driver.d.ts.map +1 -1
  246. package/dist/esm/driver.js +2 -5
  247. package/dist/esm/driver.js.map +1 -1
  248. package/dist/esm/index.d.ts +4 -1
  249. package/dist/esm/index.d.ts.map +1 -1
  250. package/dist/esm/index.js +6 -2
  251. package/dist/esm/index.js.map +1 -1
  252. package/dist/esm/pod.d.ts +68 -0
  253. package/dist/esm/pod.d.ts.map +1 -0
  254. package/dist/esm/pod.js +146 -0
  255. package/dist/esm/pod.js.map +1 -0
  256. package/dist/esm/utils/webid-resolver.d.ts +1 -2
  257. package/dist/esm/utils/webid-resolver.d.ts.map +1 -1
  258. package/dist/esm/utils/webid-resolver.js +36 -30
  259. package/dist/esm/utils/webid-resolver.js.map +1 -1
  260. package/dist/index.d.ts +4 -1
  261. package/dist/index.d.ts.map +1 -1
  262. package/dist/index.js +17 -3
  263. package/dist/index.js.map +1 -1
  264. package/dist/pod.d.ts +68 -0
  265. package/dist/pod.d.ts.map +1 -0
  266. package/dist/pod.js +153 -0
  267. package/dist/pod.js.map +1 -0
  268. package/dist/utils/webid-resolver.d.ts +1 -2
  269. package/dist/utils/webid-resolver.d.ts.map +1 -1
  270. package/dist/utils/webid-resolver.js +36 -30
  271. package/dist/utils/webid-resolver.js.map +1 -1
  272. package/package.json +27 -8
package/README.md CHANGED
@@ -1,563 +1,270 @@
1
- # Drizzle Solid
1
+ # drizzle-solid
2
2
 
3
- 一个为Solid Pod设计的类型安全ORM,基于Drizzle ORM构建,让您能够像操作传统数据库一样操作Solid Pod中的RDF数据。
3
+ `drizzle-solid` is a Drizzle-aligned data layer for application-owned data in Solid Pods.
4
4
 
5
- ## 特性
5
+ 这次变化的重点不是强迫你把构造函数名字从 `drizzle()` 换成别的东西,而是把语义讲清楚:
6
6
 
7
- - 🔒 **类型安全**: 完整的 TypeScript 支持与严格模式提示
8
- - 🧭 **Drizzle 对齐**: 沿用 Drizzle ORM 的查询构建器与错误形态,降低迁移成本
9
- - 🌐 **Solid 实测**: CSS 集成测试覆盖 CRUD、条件组合、聚合与联结场景
10
- - 🔁 **智能回退**: SQL 查询自动转换为 SPARQL;当 CSS/Comunica 无法处理过滤器或聚合时由方言拉取数据并在内存中回放
11
- - 🔧 **灵活映射**: 自定义命名空间、谓词和列类型(字符串、数字、布尔、时间、JSON/Object)
7
+ - `table` 更接近 **resource layout / collection definition**
8
+ - `link` 字段更接近 **RDF link / IRI link**
9
+ - 实体最终身份更接近 **IRI / @id**,而不只是本地主键
10
+ - 读取和写入不再默认共享 SQL 式语义
12
11
 
13
- ## 🔋 服务器支持
12
+ ## Scope
14
13
 
15
- drizzle-solid 支持两种 Solid 服务器:
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
- | 能力 | 原生 CSS | xpod |
20
- |------|----------|------|
21
- | **基础 CRUD** | ✅ LDP 模式 | ✅ LDP 模式 |
22
- | **SPARQL SELECT** | ❌ 不支持(Comunica 客户端执行) | ✅ 服务端索引下推 |
23
- | **SPARQL UPDATE** | ⚠️ 仅 BGP 写入 | ✅ 完整支持 |
24
- | **条件查询** (where) | Comunica 读文件到内存 | 索引下推(单 Pod) |
25
- | **聚合函数** (count/sum/avg) | Comunica 读文件到内存 | 索引下推(单 Pod) |
26
- | **GROUP BY / JOIN** | Comunica 读文件到内存 | 索引下推(单 Pod) |
27
- | **联邦查询** (跨 Pod) | Comunica 内存处理 | Comunica 内存处理 |
28
- | **SPARQL 端点** | ❌ 不支持 | ✅ `/-/sparql` Sidecar |
29
- | **SPARQL 1.1 覆盖率** | - | ~90% |
22
+ Not in v1:
30
23
 
31
- ### 原生 Community Solid Server (CSS)
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
32
29
 
33
- 原生 CSS **不支持 SPARQL SELECT**,drizzle-solid 通过 **Comunica 客户端**读取文件到内存后执行查询:
34
- - ✅ 基本的 CRUD 操作(LDP)
35
- - ⚠️ 写入仅支持 BGP(Basic Graph Pattern)
36
- - ⚠️ 所有查询由 Comunica 客户端在内存中执行
30
+ ## Constructor Choice
37
31
 
38
- ### xpod - 扩展的 CSS (推荐)
32
+ 仓库文档与 examples 默认先用 `pod(session, config?)` 讲主线语义:
39
33
 
40
- **xpod** 使用 **QuintStore**(五元组存储,GSPOV)替代文件存储,通过 6 组索引实现查询下推:
34
+ - `pod(session, config?)`
35
+ - `drizzle(session, config?)`
41
36
 
42
- - ✅ **SPARQL SELECT 服务端执行**:查询下推到 QuintStore 索引
43
- - ✅ **Sidecar API**:`/-/sparql` 端点,权限继承自资源路径
44
- - ✅ **混合存储**:RDF → QuintStore 索引,非 RDF → 文件系统
45
- - ⚠️ **联邦查询**:跨 Pod 查询仍需 Comunica 内存处理
46
- - ⚠️ **SPARQL 覆盖**:约 90% W3C SPARQL 1.1 测试通过
37
+ 建议这样理解:
47
38
 
48
- **仓库**:https://github.com/undefinedsco/xpod | **详细对比**:[xpod 特性文档](./docs/xpod-features.md)
39
+ - `pod()` 是语义优先的正式入口,方便把 API 组织成 `collection()` / `entity()` / `bind()`
40
+ - `drizzle()` 是同一运行时上的 Drizzle-aligned 入口,适合迁移或保留 builder/query 代码形状
41
+ - 重要的不是“入口名”,而是下面这些语义:`base`、`subjectTemplate`、`IRI`、exact-target mutation
49
42
 
50
- ## 🚀 快速开始
43
+ ## Public Surface
51
44
 
52
- ### 安装
45
+ 当前公开表面可以分成两层:
53
46
 
54
- ```bash
55
- yarn add drizzle-solid
56
- ```
57
-
58
- ### 基本用法
59
-
60
- ```typescript
61
- import { drizzle } from 'drizzle-solid';
62
- import { podTable, string, int } from 'drizzle-solid';
63
- import { Session } from '@inrupt/solid-client-authn-node';
64
-
65
- // 定义表结构
66
- const profileTable = podTable('profile', {
67
- name: string('name'),
68
- email: string('email'),
69
- age: int('age')
70
- }, {
71
- // 相对 Pod 根的容器/资源路径,不需要 idp:// 前缀
72
- base: '/profiles/',
73
- type: 'https://schema.org/Person'
74
- });
75
-
76
- // 自定义 subject 模板示例:按年月目录、文件名是 id,仍指向单独文件
77
- const logsTable = podTable('logs', {
78
- id: string('id').primaryKey(),
79
- message: string('message')
80
- }, {
81
- type: 'https://schema.org/DigitalDocument',
82
- base: '/logs/',
83
- subjectTemplate: '{yyyy}/{MM}/{dd}/{id}.ttl'
84
- });
85
-
86
- // subjectTemplate(选填):
87
- // - 作用:控制每条记录主体 URI 的形态,支持占位符 {yyyy}/{MM}/{dd}/{HH}/{mm}/{ss}/{id}。
88
- // - 推断:未指定时按 base 判断:base 是容器(以 / 结尾且包含表名)→ document 模式;base 是具体文件 → fragment 模式。模板含 #(如 '#{id}')也强制 fragment;其他模板按 document 处理,可用 {yyyy}/{MM}/{dd}/{id}.ttl 等。
89
- // - document 模式:每条记录独立文件,URI 形如 .../users/{id}.ttl,SELECT/UPDATE/DELETE 由 Comunica dereference 目标文件后本地求值;适合记录级隔离/按目录分片。
90
- // - fragment 模式:多条记录共享同一文件,主体是片段(.../file.ttl#{id}),SELECT 同样由 Comunica 拉取该文件求值;适合小表或聚合存放的数据。
91
- // - 示例:subjectTemplate: '{yyyy}/{MM}/{dd}/{id}.ttl'(document 分日期分片);subjectTemplate: '#{id}'(fragment,共享文件)。
92
-
93
- // 创建数据库连接
94
- const session = new Session(); // 已认证的session
95
- const db = drizzle(session);
96
-
97
- // 初始化需要使用的表(创建容器、资源并注册 TypeIndex)
98
- await db.init([profileTable]);
99
-
100
- // 查询数据
101
- const profiles = await db.select().from(profileTable);
102
-
103
- // 插入数据
104
- await db.insert(profileTable).values({
105
- name: 'Alice',
106
- email: 'alice@example.com',
107
- age: 30
108
- });
109
-
110
- // 使用 SPARQL 端点(适用于 xpod)
111
- const sparqlTable = podTable('posts', {
112
- id: string('id').primaryKey(),
113
- title: string('title'),
114
- }, {
115
- type: 'https://schema.org/CreativeWork',
116
- base: '/data/posts.ttl', // 资源路径
117
- sparqlEndpoint: '/data/posts.ttl/-/sparql' // xpod Sidecar 模式端点
118
- // 💡 xpod 使用 Sidecar 模式:在资源路径后添加 /-/sparql 后缀
119
- // - 文件资源: /data/posts.ttl/-/sparql
120
- // - 容器: /data/users/-/sparql
121
- });
122
- await db.init([sparqlTable]); // 不会创建容器,直接使用端点执行 SPARQL UPDATE/SELECT
123
- await db.insert(sparqlTable).values({ id: 'post-1', title: 'Hello SPARQL' });
124
-
125
- ### SPARQL Endpoint 模式兼容性说明 (重要!)
47
+ ### Semantic-first surface
126
48
 
127
- `podTable` 配置 `sparqlEndpoint` 时,Drizzle Solid 会直接使用该端点执行所有 CRUD 操作。理解其行为模式对于数据一致性至关重要:
49
+ - `pod(session, config?)`
50
+ - `client.bind(schema, options)`
51
+ - `client.collection(table)`
52
+ - `client.entity(table, iri)`
53
+ - `client.sparql(query)`
128
54
 
129
- 1. **Fragment Mode (聚合 Named Graph)**:
130
- * **定义**: 当表的 `base` 指向一个具体资源 URL (例如 `/data/posts.ttl`),或者 `subjectTemplate` 使用片段 (例如 `'#{id}'`) 时,Drizzle Solid 会将所有数据操作集中在 `base` 对应的单一 Named Graph (`GRAPH <base_url>`) 中。
131
- * **行为**: 在此模式下,LDP 读写与 SPARQL 读写**完全兼容且互操作**。LDP 对 `base` 资源的 PATCH/GET 操作,与 SPARQL 对 `GRAPH <base_url>` 的 DML/SELECT 操作,都作用于相同的数据集,并能互相可见。**这是 SPARQL Endpoint 模式下的推荐用法**。
132
- * **示例**: `base: '/data/posts.ttl'`, `subjectTemplate: '#{id}'`。所有 posts 都存储在 `posts.ttl` 文件对应的 Named Graph 中。
55
+ ### Drizzle-aligned surface
133
56
 
134
- 2. **Document Mode (聚合视图,写操作不兼容)**:
135
- * **定义**: 当表的 `base` 指向一个容器 URL (例如 `/data/users/`),且 `subjectTemplate` 生成文件路径 (例如 `'{id}.ttl'`) 时。
136
- * **读操作 (SELECT)**: SPARQL SELECT 查询指向容器的 `/sparql` 端点时,Community Solid Server (CSS) 会**聚合该容器内所有 LDP 子文件**的数据,形成一个统一的视图。因此,通过 LDP PUT 写入的子文件 (例如 `alice.ttl`),**可以被 SPARQL SELECT 查询到**。
137
- * **写操作 (DML)**: SPARQL INSERT/UPDATE/DELETE 操作是针对**容器的聚合 Named Graph** (`GRAPH <container_url>`)。这些操作**不会**直接创建、修改或删除 LDP 子文件。
138
- * 例如,通过 SPARQL INSERT 写入 `bob`,数据会存在容器 Graph 中,但**不会**创建 `bob.ttl` 文件 (LDP GET `/data/users/bob.ttl` 将返回 404)。
139
- * 通过 SPARQL UPDATE `alice`,只会修改容器 Graph 中的 `alice` 数据(SPARQL 视图),而**不会**更新 `alice.ttl` 文件本身 (LDP GET `/data/users/alice.ttl` 将显示旧数据)。
140
- * **结论**: 在 Document Mode 下,SPARQL 读操作可以兼容 LDP 资源(因为聚合),但 **写操作与 LDP 资源的物理文件视图不兼容**。这意味着 LDP 客户端可能无法看到 SPARQL DML 操作的影响,反之亦然。**不建议混合使用 LDP Document Mode 和 SPARQL DML 操作**,除非您明确理解其数据存储的视图差异,并仅利用 SPARQL 作为聚合查询工具。
57
+ - `drizzle(session, config?)`
58
+ - `podTable(name, columns, config)`
59
+ - `solidSchema(...)`
60
+ - `select / insert / update / delete`
61
+ - `db.query.*` read facade
141
62
 
142
-
143
- ```
144
-
145
- ## 📚 示例教程
146
-
147
- 我们提供了完整的示例来帮助您快速上手:
148
-
149
- ### 🏗️ 示例1: 快速开始
63
+ ## Install
150
64
 
151
65
  ```bash
152
- yarn example:quick
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
153
69
  ```
154
70
 
155
- 这个示例展示:
156
- - 如何连接到 Solid Pod 并定义表结构
157
- - 使用 Drizzle 风格 API 执行插入、查询、更新、删除
158
-
159
- ### 📖 示例2: 关系查询
160
-
161
- ```bash
162
- yarn example:query
163
- ```
164
-
165
- 这个示例展示:
166
- - 如何定义表之间的关系
167
- - 使用 `with` 进行关联查询
168
- - 嵌套数据的加载方式
169
-
170
- ### 🛠️ 示例3: 零配置数据发现
171
-
172
71
  ```bash
173
- yarn example:discovery
72
+ npm install @undefineds.co/drizzle-solid drizzle-orm
73
+ npm install @comunica/query-sparql-solid
174
74
  ```
175
75
 
176
- 这个示例展示:
177
- - 如何使用 TypeIndex 自动发现数据位置
178
- - SAI (Solid Application Interoperability) 数据授权
179
- - 无需硬编码路径的数据访问
180
-
181
- ## 📖 详细文档
76
+ `@comunica/query-sparql-solid` is an optional peer dependency.
182
77
 
183
- ### 表定义
78
+ Install it in the consuming app when you need built-in SPARQL query execution, LDP-backed query fallback, or direct SPARQL workflows.
184
79
 
185
- ```typescript
186
- import { podTable, string, int, boolean, date, uri, eq, gte, and } from 'drizzle-solid';
80
+ Current public compatibility stance:
187
81
 
188
- const userTable = podTable('users', {
189
- name: string('name'), // foaf:name
190
- email: string('email'), // foaf:mbox
191
- age: int('age'), // foaf:age
192
- verified: boolean('verified'), // 自定义谓词
193
- createdAt: date('createdAt'), // dcterms:created
194
- organization: uri('organization')
195
- .predicate('https://schema.org/member') // <org> schema:member <person>
196
- .inverse()
197
- }, {
198
- // 目标 Turtle 资源,必填,可以是相对 Pod 路径或绝对 URL
199
- base: 'data/users.ttl',
200
- // 主体类型
201
- type: 'https://schema.org/Person',
202
- // 可选:注册 TypeIndex(仅在提供 typeIndex 时才会尝试)
203
- typeIndex: 'private' // 'public' | 'private' | undefined
204
- });
82
+ - officially supported: `@comunica/query-sparql-solid` `4.x`
83
+ - not currently in the supported matrix: `3.x`
205
84
 
206
- // 初始化:在 CRUD 前确保容器/资源存在(会按 base 自动创建)
207
- await db.init([userTable]);
208
- ```
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`.
209
86
 
210
- - 使用 `.inverse()` 可以把列映射为 `<object> predicate <subject>` 方向,适合例如 `<org> schema:member <person>` 这样的反向边;查询/写入都会自动交换 RDF 三元组的主体和宾语。
87
+ ## Quick start
211
88
 
212
- ### Drizzle 风格查询:`db.query` + `findByIRI`
89
+ 仓库里的主线 quick start 默认使用 `pod(session)`,因为它会把 `collection()` / `entity()` / `bind()` 语义直接写在代码里。
213
90
 
214
- schema 传入 `drizzle(session, { schema })` 后,可通过 Drizzle 对齐的查询助手调用:
91
+ 如果你已有大量 Drizzle 风格代码,`drizzle(session)` 仍然是正式可用入口。
215
92
 
216
93
  ```ts
217
- import * as schema from './schema';
218
- const db = drizzle(session, { schema });
219
-
220
- const users = await db.query.users.findMany({
221
- where: { verified: true },
222
- orderBy: [{ column: schema.users.name, direction: 'asc' }],
223
- with: {
224
- posts: true // 根据子表 referenceTarget + @id 预加载关联行
225
- }
94
+ import {
95
+ pod,
96
+ podTable,
97
+ string,
98
+ datetime,
99
+ } from '@undefineds.co/drizzle-solid';
100
+
101
+ const posts = podTable('posts', {
102
+ id: string('id').primaryKey(),
103
+ title: string('title').predicate('http://schema.org/headline'),
104
+ content: string('content').predicate('http://schema.org/text'),
105
+ createdAt: datetime('createdAt').predicate('http://schema.org/dateCreated'),
106
+ }, {
107
+ base: 'https://alice.example/data/posts/',
108
+ subjectTemplate: '{id}.ttl',
109
+ type: 'http://schema.org/CreativeWork',
226
110
  });
227
111
 
228
- // 通过 IRI 查找单条记录(推荐使用 db.findByIri)
229
- const alice = await db.findByIri(schema.users, 'https://pod.example/data/users.ttl#alice');
230
- ```
231
-
232
- - `findMany/findFirst/findById/count` 与 Drizzle ORM 行为一致,复用现有 `select` 管道。
233
- - `with` 支持基于 `reference(target)` 的引用外键(通过 `@id` 关联),结果会嵌套数组挂在相应键上。
234
- - `db.findByIri(table, iri)` 可直接接受绝对 IRI 或 fragment(无协议时按 `id` 匹配),推荐使用此方法。
235
- - TypeIndex 注册策略:仅当表配置了 `typeIndex: 'private' | 'public'` 时才会尝试写入 TypeIndex;未配置则跳过。
236
-
237
- ### 支持的列类型
238
-
239
- Drizzle Solid 完全兼容所有 Drizzle ORM 数据库方言的列类型:
240
-
241
- #### 基础类型
242
- ```typescript
243
- // 字符串类型
244
- string('name') // 通用字符串
245
- text('content') // 文本内容
246
- varchar('title') // 可变长度字符串
247
- char('code') // 固定长度字符串
248
-
249
- // 数字类型
250
- int('count') // MySQL 风格整数
251
- integer('id') // PostgreSQL 风格整数
252
- bigint('large') // 大整数
253
- smallint('small') // 小整数
254
- tinyint('tiny') // 微整数 (MySQL)
255
- mediumint('medium') // 中等整数 (MySQL)
256
- serial('auto') // 自增序列
257
-
258
- // 浮点数类型(内部映射为 xsd:decimal)
259
- real('price') // 实数
260
- decimal('amount') // 十进制数
261
- numeric('value') // 数值
262
- float('ratio') // 单精度浮点
263
- double('precise') // 双精度浮点
264
-
265
- // 布尔类型
266
- boolean('active') // 布尔值
267
-
268
- // 日期时间类型
269
- date('birthday') // 日期
270
- datetime('event') // 日期时间
271
- timestamp('created') // 时间戳
272
-
273
- // JSON 类型
274
- json('data') // JSON 数据
275
- jsonb('config') // 二进制 JSON (PostgreSQL)
276
- object('metadata') // 对象类型 (扩展)
277
- ```
278
-
279
- ### 查询操作
280
-
281
- ```typescript
282
- // 查询所有记录
283
- const users = await db.select().from(userTable);
284
-
285
- // 条件查询
286
- const adults = await db.select()
287
- .from(userTable)
288
- .where(gte(userTable.age, 18));
289
-
290
- // 选择特定字段
291
- const names = await db.select({ name: userTable.name })
292
- .from(userTable);
293
-
294
- // 使用条件构建器
295
- const verifiedAdults = await db.select()
296
- .from(userTable)
297
- .where(and(gte(userTable.age, 18), eq(userTable.verified, true)));
298
-
299
- // 排序、分页查询
300
- const recentUsers = await db.select()
301
- .from(userTable)
302
- .orderBy(userTable.createdAt, 'desc') // 默认升序,可显式指定 'desc'
303
- .limit(10) // 取前 10 条
304
- .offset(10); // 跳过前 10 条,实现分页
305
-
306
- // DISTINCT 查询,去重后返回唯一记录
307
- const uniqueEmails = await db.select({ email: userTable.email })
308
- .from(userTable)
309
- .distinct();
310
- ```
311
-
312
- ### 聚合查询
313
-
314
- ```typescript
315
- import { count, max } from 'drizzle-solid';
316
-
317
- const stats = await db
318
- .select({
319
- totalUsers: count(),
320
- oldestAge: max(userTable.age)
321
- })
322
- .from(userTable)
323
- .where(gte(userTable.age, 18));
324
-
325
- console.log(stats[0]);
326
- // { totalUsers: 42, oldestAge: 63 }
327
- ```
328
-
329
- > 当前聚合支持 `count/sum/avg/min/max`,由客户端在内存中计算,选择列表需全部为聚合字段;`JOIN` 与 `GROUP BY` 亦已通过客户端回放实现(在 CSS 升级至最新 Comunica 前仍保留此策略)。
112
+ const client = pod(session);
113
+ await client.init(posts);
330
114
 
331
- ### 插入数据
115
+ const postsCollection = client.collection(posts);
332
116
 
333
- ```typescript
334
- // 插入单条记录
335
- await db.insert(userTable).values({
336
- name: 'Bob',
337
- email: 'bob@example.com',
338
- age: 25
117
+ const created = await postsCollection.create({
118
+ id: 'post-1',
119
+ title: 'Hello Solid',
120
+ content: 'Stored as RDF in a Pod document.',
121
+ createdAt: new Date(),
339
122
  });
340
123
 
341
- // 批量插入
342
- await db.insert(userTable).values([
343
- { name: 'Alice', email: 'alice@example.com', age: 30 },
344
- { name: 'Charlie', email: 'charlie@example.com', age: 35 }
345
- ]);
346
- ```
347
-
348
- ### 更新数据
349
-
350
- ```typescript
351
- await db.update(userTable)
352
- .set({ age: 26 })
353
- .where(eq(userTable.name, 'Bob'));
354
- ```
355
-
356
- ### 删除数据
357
-
358
- ```typescript
359
- await db.delete(userTable)
360
- .where(eq(userTable.name, 'Bob'));
361
- ```
362
-
363
- ## 🔍 数据发现与零配置访问 (Data Discovery & Interoperability)
364
-
365
- Drizzle Solid 实现了 Solid 的 **TypeIndex** 和 **SAI (Application Interoperability)** 规范,这意味着您**不需要**硬编码数据的具体路径(`base`)。只要定义好 RDF 类型,Drizzle 就会自动帮您找到数据,无论它是在您自己的 Pod 里,还是别人通过 SAI 授权给您的。
366
-
367
- ### 核心概念
368
-
369
- - **TypeIndex**: 您的“个人数据索引”,记录了“我的笔记在哪里”、“我的联系人在哪里”。适合单用户场景。
370
- - **SAI Discovery**: 一种更高级的发现机制,用于发现**跨 Pod** 共享的数据(例如 Alice 分享给 Bob 的聊天室)。适合社交、协作场景。
371
-
372
- ### 如何启用自动发现?
373
-
374
- 只需在定义表时,移除 `base` 属性,并设置 `typeIndex: 'private'`(或 `'public'`):
124
+ const first = await postsCollection.first({
125
+ where: { id: 'post-1' },
126
+ });
375
127
 
376
- ```typescript
377
- const messageTable = podTable('message', {
378
- id: id(),
379
- content: string('content').predicate('http://schema.org/text'),
380
- }, {
381
- type: 'http://schema.org/Message',
382
- typeIndex: 'private' // ✨ 开启自动发现魔法
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(),
383
133
  });
384
134
 
385
- // 当您执行查询时,Drizzle 会自动:
386
- // 1. 查您的 Private TypeIndex
387
- // 2. 如果没找到,查 SAI Agent Registry (查看是否有授权给您的数据)
388
- // 3. 找到数据位置后,自动定向查询
389
- const messages = await db.select().from(messageTable);
135
+ const post = client.entity(posts, postIri);
136
+ await post.update({ title: 'Updated title' });
137
+ await post.delete();
390
138
  ```
391
139
 
392
- ### Pod 聊天应用示例 (SAI Chat)
140
+ > 如果你保持 `drizzle(session)` 作为入口,语义并不会变:`base`、`subjectTemplate`、`IRI`、精确写目标这些约束仍然完全成立。
393
141
 
394
- 这是一个展示如何利用 SAI 发现机制构建“零配置”聊天应用的完整示例。Bob 不需要知道 Alice 的聊天室 URL,只要 Alice 授权了,Bob 的应用就能自动发现并加入聊天。
142
+ ## Reusable schema + runtime binding
395
143
 
396
- > 查看完整可运行代码:[`examples/04-sai-chat.ts`](examples/04-sai-chat.ts)
144
+ Use `solidSchema(...)` when you want to separate the reusable data shape from where that data lives in a Pod.
397
145
 
398
- ```typescript
399
- // 1. 定义通用的消息表(不绑定具体路径)
400
- const messageTable = podTable('message', {
401
- id: id(),
402
- content: string('content').predicate('http://schema.org/text'),
403
- author: uri('author').predicate('http://schema.org/author'),
404
- createdAt: datetime('createdAt').predicate('http://schema.org/dateCreated')
405
- }, {
406
- type: 'http://schema.org/Message',
407
- typeIndex: 'private', // 启用发现
408
- autoRegister: false // 对于访客 Bob,不仅不需要注册,而且不能注册到自己的 TypeIndex
409
- });
146
+ 如果你使用 `pod()` façade:
410
147
 
411
- // 2. Alice (房主) 初始化聊天室
412
- // Alice 需要明确指定存储位置 (base)
413
- const aliceChatTable = podTable('message', { ...messageTable.columns }, {
414
- ...messageTable.config,
415
- base: 'https://alice.pod/data/chat/room.ttl',
416
- subjectTemplate: '#{id}' // 强制单文件存储模式
148
+ ```ts
149
+ const client = pod(session);
150
+ const profileTable = client.bind(profileSchema, {
151
+ base: 'https://alice.example/profile/card',
417
152
  });
418
- await db.insert(aliceChatTable).values({ ... });
419
-
420
- // ... (Alice 通过 SAI 授权给 Bob,此处省略 SAI 注册代码) ...
421
-
422
- // 3. Bob (访客) 发现并回复
423
- // Bob 初始化 Drizzle 时,完全不需要知道 room.ttl 的地址
424
- const bobDb = drizzle(bobSession);
153
+ ```
425
154
 
426
- // 读取:自动发现 Alice 分享的数据
427
- const messages = await bobDb.select().from(messageTable);
428
- console.log(messages); // 包含 Alice 的消息
155
+ 如果你保持 `drizzle()` 入口:
429
156
 
430
- // 写入:自动向发现的地址写入回复
431
- await bobDb.insert(messageTable).values({
432
- content: 'Hi Alice! I found your chat room!',
433
- author: bobSession.info.webId
157
+ ```ts
158
+ const db = drizzle(session);
159
+ const profileTable = db.createTable(profileSchema, {
160
+ base: 'https://alice.example/profile/card',
434
161
  });
435
162
  ```
436
163
 
437
- ### Discovery 策略
164
+ ## Identity and placement
438
165
 
439
- Drizzle Solid 使用组合策略 (`CompositeDiscovery`):
440
- 1. **TypeIndex 优先**:检查用户的 `privateTypeIndex.ttl` 和 `publicTypeIndex.ttl`。
441
- 2. **SAI 后备**:如果 TypeIndex 未命中,检查用户的 Profile -> RegistrySet -> Agent Registry,寻找匹配当前 ClientID 的 Data Grant。
166
+ `subjectTemplate` defines how application identity maps onto Pod resources.
442
167
 
443
- 这种设计确保了既能兼容旧的 Solid 应用(基于 TypeIndex),又能支持未来的互操作性规范(SAI)。
168
+ Common patterns:
444
169
 
445
- 架构说明见 `docs/guides/architecture.md`。
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
446
174
 
447
- ## ✅ 当前 SQL 支持范围
175
+ 这不是命名细节,而是持久化语义本身。
448
176
 
449
- - 已实现:`select/insert/update/delete`、Drizzle 风格的 `where` 条件构建器(`eq/ne/lt/gte/like/inArray/not` 等)、`orderBy`、`limit/offset`、`distinct`、嵌套布尔组合,以及 `count/sum/avg/min/max` 聚合、`leftJoin/innerJoin` 和 `GROUP BY`。
450
- - 运行策略:
451
- - **xpod + 单 Pod**:查询下推到 QuintStore 索引执行
452
- - **原生 CSS / 联邦查询**:Comunica 将数据读入内存后执行
453
- - 未实现:`rightJoin`/`fullJoin`(待评估 xpod 支持情况)
454
- - 未覆盖:`HAVING`、窗口函数、`UNION/UNION ALL`、子查询与跨容器联结;如需这些能力,请暂时改用手写 SPARQL 或拆分查询。
177
+ ## Exact-target mutation semantics
455
178
 
456
- ## 🗺️ Roadmap
179
+ Reads and writes do not intentionally behave the same way.
457
180
 
458
- - **`rightJoin`/`fullJoin` 支持**: 待评估 xpod SPARQL 能力后实现,详见[设计方案](docs/guides/right-full-join-sparql-design.md)。
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
459
185
 
460
- ## 🔧 配置
186
+ 所以最重要的变化不是“要不要换成 `pod()`”,而是:
461
187
 
462
- ### 自定义命名空间
188
+ - 什么时候你只是在做集合读取
189
+ - 什么时候你已经需要一个确定的实体目标
463
190
 
464
- Drizzle Solid 不再内置 vocab 常量,请从 RDF vocab 库(例如 `@inrupt/vocab-common-rdf`)导入需要的术语;若需要扩展缺失字段,可使用 `extendNamespace`:
191
+ ## Server support
465
192
 
466
- ```typescript
467
- import { podTable, string, extendNamespace } from 'drizzle-solid';
468
- import { SCHEMA_INRUPT as SCHEMA } from '@inrupt/vocab-common-rdf';
193
+ ### Capability matrix
469
194
 
470
- const LINQ = extendNamespace(
471
- { prefix: 'linq', uri: 'https://linq.dev/ns/' },
472
- { favorite: 'profile#favorite' },
473
- { namespace: 'https://linq.dev/ns/' }
474
- );
195
+ | Capability | Community Solid Server | xpod |
196
+ | --- | --- | --- |
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 |
202
+ | Federated queries | ⚠️ Client-side federation | ⚠️ Client-side federation |
203
+ | In-process local runtime | ⚠️ External setup | ✅ via `@undefineds.co/xpod` |
475
204
 
476
- const customTable = podTable('custom', {
477
- title: string('title').predicate(`${SCHEMA.NAMESPACE}title`),
478
- favorite: string('favorite').predicate(LINQ.favorite)
479
- }, {
480
- base: 'idp:///custom/index.ttl', // 目标资源
481
- type: `${SCHEMA.NAMESPACE}CreativeWork`,
482
- namespace: LINQ
483
- });
484
- ```
205
+ If `xpod` already ships its own Comunica stack, `drizzle-solid` can reuse that copy instead of requiring another app-level install.
485
206
 
486
- ### 认证配置
207
+ ## Verified examples
487
208
 
488
- ```typescript
489
- import { Session } from '@inrupt/solid-client-authn-node';
209
+ The canonical examples in `examples/` are part of the real integration verification flow:
490
210
 
491
- const session = new Session();
492
- await session.login({
493
- oidcIssuer: 'https://solidcommunity.net',
494
- redirectUrl: 'http://localhost:3000/callback',
495
- clientName: 'My Solid App'
496
- });
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`
497
221
 
498
- const db = drizzle(session);
499
- ```
222
+ See `examples/manifest.json` and `tests/integration/css/examples-verification.test.ts`.
500
223
 
501
- ### `base` / `@id` / `id` 与 Pod 根的关系
224
+ ## Migration
502
225
 
503
- - `podUrl`(Pod 根)由 WebID 推导,所有相对路径都基于它解析。
504
- - `base` 是表的目标 Turtle 资源(必填),可为相对路径或绝对 URL。示例:`base: '/data/contacts.ttl'` → `https://pod.example/data/contacts.ttl`。
505
- - 写入:
506
- - 提供 `@id` 则直接作为 subject;
507
- - 提供 `id`(或库自动生成)则 subject 形如 `base#<id>`(或按表的 subject 模板选择 `#`/`/`)。
508
- - 查询:
509
- - `where({ '@id': 'https://…#foo' })` 精确匹配该 subject;
510
- - `where({ id: 'foo' })` 匹配 fragment 为 `foo` 且落在该表 `base` 下的 subject。
511
- - `base` 同时决定存储地址(PUT/PATCH 目标)和 subject 生成;`podUrl` 只负责解析相对的 `base`。
226
+ If you already know `drizzle-orm`, start here:
512
227
 
513
- ## 🏗️ 架构
228
+ - `docs/guides/migrating-from-drizzle-orm.md`
514
229
 
515
- Drizzle Solid基于以下组件构建:
230
+ 那份指南重点解释的是:
516
231
 
517
- - **PodDialect**: Solid Pod的Drizzle方言实现
518
- - **SPARQL转换器**: 将Drizzle查询转换为SPARQL
519
- - **Comunica执行器**: 执行SPARQL查询
520
- - **类型系统**: 完整的TypeScript类型支持
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 主心智
521
236
 
522
- ### Comunica CRUD 流程
237
+ 而不是要求你第一步先改掉构造函数名。
523
238
 
524
- - 查询会经过 AST → SPARQL 转换;若 Comunica 无法执行带过滤器/聚合的 `UPDATE`/`DELETE`,方言会先通过 `SELECT` 拉取命中的 subject,再以 PATCH 方式回写,实现与 SQL 行级操作一致的语义。
525
- - `PodDialect` 会自动推导目标容器与 `.ttl` 资源文件路径,必要时发送 `HEAD`/`PUT` 请求确保容器和资源已经存在,再交由 Comunica 处理数据修改。
526
- - 插入会预先读取现有资源以检测重复 subject,避免重复写入;删除或更新只针对匹配的 subject 生成最小化补丁。
527
- - 对于 `JOIN`、`GROUP BY` 与聚合,选取的数据仍由 SPARQL 拉取,但结果会在内存中组合或聚合,直到 CSS 升级到支持完整 SPARQL 1.1 为止。
239
+ ## Documentation map
528
240
 
529
- ## 🤝 贡献
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
530
249
 
531
- 欢迎贡献代码!请阅读 [CONTRIBUTING.md](CONTRIBUTING.md) 了解测试要求、提交流程与验证内容。
250
+ ## Contributing
532
251
 
533
- 在提交 PR 之前,请同步运行完整的 CSS 集成测试(覆盖 CRUD、TypeIndex 等场景):
252
+ Before pushing:
534
253
 
535
254
  ```bash
536
- SOLID_ENABLE_REAL_TESTS=true npx vitest run tests/integration/css --runInBand
255
+ yarn build
256
+ yarn lint
257
+ SOLID_ENABLE_REAL_TESTS=true SOLID_SERIAL_TESTS=true yarn vitest --run --silent
537
258
  ```
538
259
 
539
- > `SOLID_ENABLE_REAL_TESTS=true` 会启用真实 Pod,`--runInBand` 保证所有 suite 共用一个会话并顺序执行,避免对 OIDC 服务造成并发压力。
540
-
541
- ## 📄 许可证
542
-
543
- MIT License - 查看[LICENSE](LICENSE)文件了解详情。
544
-
545
- ## 🔗 相关链接
546
-
547
- - [Drizzle ORM](https://orm.drizzle.team/)
548
- - [Solid Project](https://solidproject.org/)
549
- - [Community Solid Server](https://github.com/CommunitySolidServer/CommunitySolidServer)
550
- - [Inrupt Solid Client](https://github.com/inrupt/solid-client-js)
551
-
552
-
553
- ## 📞 支持
260
+ Examples must remain runnable and verified.
554
261
 
555
- 如果遇到问题,可先查阅:
262
+ ## License
556
263
 
557
- 1. `docs/quick-start-local.md` 获取本地 CSS 启动与疑难解答
558
- 2. `examples/README.md` 了解脚本入口与运行方式
559
- 3. [Issue 列表](https://github.com/undefinedsco/drizzle-solid/issues) 提交复现步骤与日志
264
+ MIT
560
265
 
561
- ---
266
+ ## Related links
562
267
 
563
- **开始您的 Solid 数据之旅!** 🚀
268
+ - GitHub: https://github.com/undefinedsco/drizzle-solid
269
+ - npm: https://www.npmjs.com/package/@undefineds.co/drizzle-solid
270
+ - xpod: https://github.com/undefinedsco/xpod