@lix-js/sdk 0.0.1 → 0.1.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 (278) hide show
  1. package/LICENSE +21 -0
  2. package/dist/change/apply-changes.js +9 -9
  3. package/dist/change/apply-changes.js.map +1 -1
  4. package/dist/change/apply-changes.test.js +5 -5
  5. package/dist/change/apply-changes.test.js.map +1 -1
  6. package/dist/database/apply-schema.d.ts.map +1 -1
  7. package/dist/database/apply-schema.js +20 -72
  8. package/dist/database/apply-schema.js.map +1 -1
  9. package/dist/database/execute-sync.test.js +3 -3
  10. package/dist/database/execute-sync.test.js.map +1 -1
  11. package/dist/database/init-db.d.ts.map +1 -1
  12. package/dist/database/init-db.js +18 -3
  13. package/dist/database/init-db.js.map +1 -1
  14. package/dist/database/init-db.test.js +128 -17
  15. package/dist/database/init-db.test.js.map +1 -1
  16. package/dist/database/kysely-plugin/parse-jsonb-plugin-v1.d.ts.map +1 -1
  17. package/dist/database/kysely-plugin/parse-jsonb-plugin-v1.js +5 -2
  18. package/dist/database/kysely-plugin/parse-jsonb-plugin-v1.js.map +1 -1
  19. package/dist/database/mutation-log/database-schema.d.ts.map +1 -1
  20. package/dist/database/mutation-log/database-schema.js +1 -3
  21. package/dist/database/mutation-log/database-schema.js.map +1 -1
  22. package/dist/database/nano-id.d.ts +21 -0
  23. package/dist/database/nano-id.d.ts.map +1 -0
  24. package/dist/database/nano-id.js +58 -0
  25. package/dist/database/nano-id.js.map +1 -0
  26. package/dist/database/nano-id.test.d.ts +2 -0
  27. package/dist/database/nano-id.test.d.ts.map +1 -0
  28. package/dist/database/nano-id.test.js +13 -0
  29. package/dist/database/nano-id.test.js.map +1 -0
  30. package/dist/database/schema.d.ts +15 -23
  31. package/dist/database/schema.d.ts.map +1 -1
  32. package/dist/discussion/create-comment.d.ts +0 -2
  33. package/dist/discussion/create-comment.d.ts.map +1 -1
  34. package/dist/discussion/create-comment.js +0 -1
  35. package/dist/discussion/create-comment.js.map +1 -1
  36. package/dist/discussion/create-discussion.d.ts +3 -5
  37. package/dist/discussion/create-discussion.d.ts.map +1 -1
  38. package/dist/discussion/create-discussion.js +4 -5
  39. package/dist/discussion/create-discussion.js.map +1 -1
  40. package/dist/discussion/create-discussion.test.js +8 -34
  41. package/dist/discussion/create-discussion.test.js.map +1 -1
  42. package/dist/file-queue/file-handlers.d.ts +24 -0
  43. package/dist/file-queue/file-handlers.d.ts.map +1 -0
  44. package/dist/file-queue/file-handlers.js +209 -0
  45. package/dist/file-queue/file-handlers.js.map +1 -0
  46. package/dist/file-queue/file-queue-process.d.ts +5 -0
  47. package/dist/file-queue/file-queue-process.d.ts.map +1 -0
  48. package/dist/file-queue/file-queue-process.js +85 -0
  49. package/dist/file-queue/file-queue-process.js.map +1 -0
  50. package/dist/file-queue/file-queue-process.test.d.ts +2 -0
  51. package/dist/file-queue/file-queue-process.test.d.ts.map +1 -0
  52. package/dist/file-queue/file-queue-process.test.js +373 -0
  53. package/dist/file-queue/file-queue-process.test.js.map +1 -0
  54. package/dist/file-queue/file-queue-settled.d.ts +13 -0
  55. package/dist/file-queue/file-queue-settled.d.ts.map +1 -0
  56. package/dist/file-queue/file-queue-settled.js +25 -0
  57. package/dist/file-queue/file-queue-settled.js.map +1 -0
  58. package/dist/file-queue/file-queue-settled.test.d.ts +2 -0
  59. package/dist/file-queue/file-queue-settled.test.d.ts.map +1 -0
  60. package/dist/file-queue/file-queue-settled.test.js +47 -0
  61. package/dist/file-queue/file-queue-settled.test.js.map +1 -0
  62. package/dist/file-queue/index.d.ts +2 -0
  63. package/dist/file-queue/index.d.ts.map +1 -0
  64. package/dist/file-queue/index.js +2 -0
  65. package/dist/file-queue/index.js.map +1 -0
  66. package/dist/file-queue/with-skip-file-queue.d.ts +3 -0
  67. package/dist/file-queue/with-skip-file-queue.d.ts.map +1 -0
  68. package/dist/file-queue/with-skip-file-queue.js +26 -0
  69. package/dist/file-queue/with-skip-file-queue.js.map +1 -0
  70. package/dist/file-queue/with-skip-file-queue.test.d.ts +2 -0
  71. package/dist/file-queue/with-skip-file-queue.test.d.ts.map +1 -0
  72. package/dist/file-queue/with-skip-file-queue.test.js +138 -0
  73. package/dist/file-queue/with-skip-file-queue.test.js.map +1 -0
  74. package/dist/index.d.ts +2 -2
  75. package/dist/index.d.ts.map +1 -1
  76. package/dist/index.js +2 -2
  77. package/dist/index.js.map +1 -1
  78. package/dist/key-value/database-schema.d.ts +11 -2
  79. package/dist/key-value/database-schema.d.ts.map +1 -1
  80. package/dist/key-value/database-schema.js +7 -4
  81. package/dist/key-value/database-schema.js.map +1 -1
  82. package/dist/key-value/database-schema.test.js +21 -5
  83. package/dist/key-value/database-schema.test.js.map +1 -1
  84. package/dist/lix/close-lix.d.ts +8 -0
  85. package/dist/lix/close-lix.d.ts.map +1 -0
  86. package/dist/lix/close-lix.js +7 -0
  87. package/dist/lix/close-lix.js.map +1 -0
  88. package/dist/lix/index.d.ts +1 -0
  89. package/dist/lix/index.d.ts.map +1 -1
  90. package/dist/lix/index.js +1 -0
  91. package/dist/lix/index.js.map +1 -1
  92. package/dist/lix/merge.js +3 -3
  93. package/dist/lix/merge.js.map +1 -1
  94. package/dist/lix/merge.test.js +7 -18
  95. package/dist/lix/merge.test.js.map +1 -1
  96. package/dist/lix/new-lix.d.ts.map +1 -1
  97. package/dist/lix/new-lix.js +2 -3
  98. package/dist/lix/new-lix.js.map +1 -1
  99. package/dist/lix/open-lix-in-memory.test.js +2 -1
  100. package/dist/lix/open-lix-in-memory.test.js.map +1 -1
  101. package/dist/lix/open-lix.d.ts +3 -5
  102. package/dist/lix/open-lix.d.ts.map +1 -1
  103. package/dist/lix/open-lix.js +4 -18
  104. package/dist/lix/open-lix.js.map +1 -1
  105. package/dist/lix/open-lix.test.js +16 -7
  106. package/dist/lix/open-lix.test.js.map +1 -1
  107. package/dist/lix/to-blob.d.ts +11 -0
  108. package/dist/lix/to-blob.d.ts.map +1 -0
  109. package/dist/lix/to-blob.js +11 -0
  110. package/dist/lix/to-blob.js.map +1 -0
  111. package/dist/own-change-control/apply-own-change.d.ts +10 -0
  112. package/dist/own-change-control/apply-own-change.d.ts.map +1 -0
  113. package/dist/own-change-control/apply-own-change.js +69 -0
  114. package/dist/own-change-control/apply-own-change.js.map +1 -0
  115. package/dist/own-change-control/apply-own-change.test.d.ts +2 -0
  116. package/dist/own-change-control/apply-own-change.test.d.ts.map +1 -0
  117. package/dist/own-change-control/apply-own-change.test.js +297 -0
  118. package/dist/own-change-control/apply-own-change.test.js.map +1 -0
  119. package/dist/own-change-control/change-controlled-tables.d.ts +60 -0
  120. package/dist/own-change-control/change-controlled-tables.d.ts.map +1 -0
  121. package/dist/own-change-control/change-controlled-tables.js +70 -0
  122. package/dist/own-change-control/change-controlled-tables.js.map +1 -0
  123. package/dist/own-change-control/change-controlled-tables.test.d.ts +2 -0
  124. package/dist/own-change-control/change-controlled-tables.test.d.ts.map +1 -0
  125. package/dist/own-change-control/change-controlled-tables.test.js +48 -0
  126. package/dist/own-change-control/change-controlled-tables.test.js.map +1 -0
  127. package/dist/own-change-control/database-triggers.d.ts +5 -0
  128. package/dist/own-change-control/database-triggers.d.ts.map +1 -0
  129. package/dist/own-change-control/database-triggers.js +135 -0
  130. package/dist/own-change-control/database-triggers.js.map +1 -0
  131. package/dist/own-change-control/database-triggers.test.d.ts +2 -0
  132. package/dist/own-change-control/database-triggers.test.d.ts.map +1 -0
  133. package/dist/own-change-control/database-triggers.test.js +214 -0
  134. package/dist/own-change-control/database-triggers.test.js.map +1 -0
  135. package/dist/own-change-control/index.d.ts +2 -0
  136. package/dist/own-change-control/index.d.ts.map +1 -0
  137. package/dist/own-change-control/index.js +2 -0
  138. package/dist/own-change-control/index.js.map +1 -0
  139. package/dist/own-change-control/with-skip-own-change-control.d.ts +3 -0
  140. package/dist/own-change-control/with-skip-own-change-control.d.ts.map +1 -0
  141. package/dist/own-change-control/with-skip-own-change-control.js +28 -0
  142. package/dist/own-change-control/with-skip-own-change-control.js.map +1 -0
  143. package/dist/own-change-control/with-skip-own-change-control.test.d.ts +2 -0
  144. package/dist/own-change-control/with-skip-own-change-control.test.d.ts.map +1 -0
  145. package/dist/own-change-control/with-skip-own-change-control.test.js +49 -0
  146. package/dist/own-change-control/with-skip-own-change-control.test.js.map +1 -0
  147. package/dist/own-entity-change-control/apply-own-entity-change.js +2 -2
  148. package/dist/own-entity-change-control/apply-own-entity-change.js.map +1 -1
  149. package/dist/own-entity-change-control/apply-own-entity-change.test.js +9 -9
  150. package/dist/own-entity-change-control/apply-own-entity-change.test.js.map +1 -1
  151. package/dist/own-entity-change-control/database-triggers.js +6 -6
  152. package/dist/own-entity-change-control/database-triggers.js.map +1 -1
  153. package/dist/own-entity-change-control/database-triggers.test.js +1 -1
  154. package/dist/own-entity-change-control/database-triggers.test.js.map +1 -1
  155. package/dist/query-filter/version-change-in-difference.test.js +37 -29
  156. package/dist/query-filter/version-change-in-difference.test.js.map +1 -1
  157. package/dist/query-filter/version-change-in-symmetric-difference.test.js +37 -39
  158. package/dist/query-filter/version-change-in-symmetric-difference.test.js.map +1 -1
  159. package/dist/server-api-handler/environment/create-in-memory-environment.d.ts.map +1 -1
  160. package/dist/server-api-handler/environment/create-in-memory-environment.js +7 -3
  161. package/dist/server-api-handler/environment/create-in-memory-environment.js.map +1 -1
  162. package/dist/server-api-handler/environment/create-in-memory-environment.test.js +8 -7
  163. package/dist/server-api-handler/environment/create-in-memory-environment.test.js.map +1 -1
  164. package/dist/server-api-handler/routes/get-v1.d.ts.map +1 -1
  165. package/dist/server-api-handler/routes/get-v1.js +1 -2
  166. package/dist/server-api-handler/routes/get-v1.js.map +1 -1
  167. package/dist/server-api-handler/routes/get-v1.test.js +6 -5
  168. package/dist/server-api-handler/routes/get-v1.test.js.map +1 -1
  169. package/dist/server-api-handler/routes/new-v1.d.ts.map +1 -1
  170. package/dist/server-api-handler/routes/new-v1.js +3 -1
  171. package/dist/server-api-handler/routes/new-v1.js.map +1 -1
  172. package/dist/server-api-handler/routes/new-v1.test.js +2 -1
  173. package/dist/server-api-handler/routes/new-v1.test.js.map +1 -1
  174. package/dist/server-api-handler/routes/pull-v1.test.js +4 -3
  175. package/dist/server-api-handler/routes/pull-v1.test.js.map +1 -1
  176. package/dist/server-api-handler/routes/push-v1.test.js +5 -4
  177. package/dist/server-api-handler/routes/push-v1.test.js.map +1 -1
  178. package/dist/sync/pull-from-server.d.ts.map +1 -1
  179. package/dist/sync/pull-from-server.js +0 -2
  180. package/dist/sync/pull-from-server.js.map +1 -1
  181. package/dist/sync/pull-from-server.test.js +29 -14
  182. package/dist/sync/pull-from-server.test.js.map +1 -1
  183. package/dist/sync/push-to-server.test.js +17 -13
  184. package/dist/sync/push-to-server.test.js.map +1 -1
  185. package/dist/sync/sync-process.d.ts +2 -4
  186. package/dist/sync/sync-process.d.ts.map +1 -1
  187. package/dist/sync/sync-process.js +4 -9
  188. package/dist/sync/sync-process.js.map +1 -1
  189. package/dist/sync/sync-process.test.js +26 -38
  190. package/dist/sync/sync-process.test.js.map +1 -1
  191. package/dist/version/create-version.d.ts +5 -5
  192. package/dist/version/create-version.d.ts.map +1 -1
  193. package/dist/version/create-version.js +23 -11
  194. package/dist/version/create-version.js.map +1 -1
  195. package/dist/version/create-version.test.js +2 -2
  196. package/dist/version/create-version.test.js.map +1 -1
  197. package/dist/version/merge-version.d.ts.map +1 -1
  198. package/dist/version/merge-version.js +16 -26
  199. package/dist/version/merge-version.js.map +1 -1
  200. package/dist/version/switch-version.js +3 -3
  201. package/dist/version/switch-version.js.map +1 -1
  202. package/dist/version/switch-version.test.js +12 -15
  203. package/dist/version/switch-version.test.js.map +1 -1
  204. package/dist/version/update-changes-in-version.d.ts.map +1 -1
  205. package/dist/version/update-changes-in-version.js +11 -31
  206. package/dist/version/update-changes-in-version.js.map +1 -1
  207. package/node_modules/@lix-js/server-api-schema/CHANGELOG.md +9 -0
  208. package/node_modules/@lix-js/server-api-schema/LICENSE +21 -0
  209. package/node_modules/@lix-js/server-api-schema/dist/schema.js +0 -0
  210. package/node_modules/@lix-js/server-api-schema/package.json +2 -2
  211. package/node_modules/sqlite-wasm-kysely/LICENSE +21 -0
  212. package/package.json +4 -4
  213. package/src/change/apply-changes.test.ts +7 -7
  214. package/src/change/apply-changes.ts +9 -9
  215. package/src/database/apply-schema.ts +20 -73
  216. package/src/database/execute-sync.test.ts +3 -3
  217. package/src/database/init-db.test.ts +163 -19
  218. package/src/database/init-db.ts +20 -3
  219. package/src/database/kysely-plugin/parse-jsonb-plugin-v1.ts +9 -2
  220. package/src/database/mutation-log/database-schema.ts +1 -3
  221. package/src/database/nano-id.test.ts +15 -0
  222. package/src/database/nano-id.ts +72 -0
  223. package/src/database/schema.ts +15 -24
  224. package/src/discussion/create-comment.ts +0 -3
  225. package/src/discussion/create-discussion.test.ts +8 -39
  226. package/src/discussion/create-discussion.ts +6 -9
  227. package/src/{change-queue → file-queue}/file-handlers.ts +27 -27
  228. package/src/{change-queue/init-change-queue.test.ts → file-queue/file-queue-process.test.ts} +24 -24
  229. package/src/{change-queue/init-change-queue.ts → file-queue/file-queue-process.ts} +18 -16
  230. package/src/{change-queue/change-queue-settled.test.ts → file-queue/file-queue-settled.test.ts} +12 -12
  231. package/src/{change-queue/change-queue-settled.ts → file-queue/file-queue-settled.ts} +4 -4
  232. package/src/file-queue/index.ts +1 -0
  233. package/src/{change-queue/with-skip-change-queue.test.ts → file-queue/with-skip-file-queue.test.ts} +9 -9
  234. package/src/{change-queue/with-skip-change-queue.ts → file-queue/with-skip-file-queue.ts} +3 -3
  235. package/src/index.ts +2 -2
  236. package/src/key-value/database-schema.test.ts +26 -5
  237. package/src/key-value/database-schema.ts +18 -6
  238. package/src/lix/close-lix.ts +8 -0
  239. package/src/lix/index.ts +1 -0
  240. package/src/lix/merge.test.ts +7 -19
  241. package/src/lix/merge.ts +4 -4
  242. package/src/lix/new-lix.ts +2 -3
  243. package/src/lix/open-lix-in-memory.test.ts +5 -1
  244. package/src/lix/open-lix.test.ts +19 -7
  245. package/src/lix/open-lix.ts +7 -24
  246. package/src/lix/to-blob.ts +14 -0
  247. package/src/{own-entity-change-control/apply-own-entity-change.test.ts → own-change-control/apply-own-change.test.ts} +27 -31
  248. package/src/{own-entity-change-control/apply-own-entity-change.ts → own-change-control/apply-own-change.ts} +3 -3
  249. package/src/{own-entity-change-control → own-change-control}/change-controlled-tables.ts +0 -1
  250. package/src/{own-entity-change-control → own-change-control}/database-triggers.test.ts +7 -7
  251. package/src/{own-entity-change-control → own-change-control}/database-triggers.ts +11 -11
  252. package/src/{own-entity-change-control → own-change-control}/with-skip-own-change-control.ts +6 -2
  253. package/src/query-filter/version-change-in-difference.test.ts +41 -32
  254. package/src/query-filter/version-change-in-symmetric-difference.test.ts +41 -42
  255. package/src/server-api-handler/environment/create-in-memory-environment.test.ts +8 -7
  256. package/src/server-api-handler/environment/create-in-memory-environment.ts +7 -3
  257. package/src/server-api-handler/routes/get-v1.test.ts +6 -5
  258. package/src/server-api-handler/routes/get-v1.ts +1 -3
  259. package/src/server-api-handler/routes/new-v1.test.ts +2 -1
  260. package/src/server-api-handler/routes/new-v1.ts +3 -1
  261. package/src/server-api-handler/routes/pull-v1.test.ts +4 -3
  262. package/src/server-api-handler/routes/push-v1.test.ts +5 -4
  263. package/src/sync/pull-from-server.test.ts +29 -14
  264. package/src/sync/pull-from-server.ts +0 -2
  265. package/src/sync/push-to-server.test.ts +19 -15
  266. package/src/sync/sync-process.test.ts +37 -43
  267. package/src/sync/sync-process.ts +6 -18
  268. package/src/version/create-version.test.ts +2 -2
  269. package/src/version/create-version.ts +24 -12
  270. package/src/version/merge-version.ts +18 -26
  271. package/src/version/switch-version.test.ts +12 -15
  272. package/src/version/switch-version.ts +3 -3
  273. package/src/version/update-changes-in-version.ts +11 -30
  274. package/node_modules/@lix-js/server-api-schema/dist/schema.d.ts +0 -384
  275. package/src/change-queue/index.ts +0 -1
  276. /package/src/{own-entity-change-control → own-change-control}/change-controlled-tables.test.ts +0 -0
  277. /package/src/{own-entity-change-control → own-change-control}/index.ts +0 -0
  278. /package/src/{own-entity-change-control → own-change-control}/with-skip-own-change-control.test.ts +0 -0
@@ -19,7 +19,7 @@ export function applySchema(args: { sqlite: SqliteDatabase }): SqliteDatabase {
19
19
  -- file
20
20
 
21
21
  CREATE TABLE IF NOT EXISTS file (
22
- id TEXT PRIMARY KEY DEFAULT (uuid_v7()),
22
+ id TEXT PRIMARY KEY DEFAULT (nano_id(10)),
23
23
  path TEXT NOT NULL UNIQUE,
24
24
  data BLOB NOT NULL,
25
25
  metadata BLOB,
@@ -28,7 +28,7 @@ export function applySchema(args: { sqlite: SqliteDatabase }): SqliteDatabase {
28
28
  CHECK (is_valid_file_path(path))
29
29
  ) STRICT;
30
30
 
31
- CREATE TABLE IF NOT EXISTS change_queue (
31
+ CREATE TABLE IF NOT EXISTS file_queue (
32
32
  id INTEGER PRIMARY KEY AUTOINCREMENT,
33
33
  file_id TEXT NOT NULL,
34
34
  data_before BLOB,
@@ -41,18 +41,18 @@ export function applySchema(args: { sqlite: SqliteDatabase }): SqliteDatabase {
41
41
 
42
42
  CREATE TRIGGER IF NOT EXISTS file_insert BEFORE INSERT ON file
43
43
  BEGIN
44
- INSERT INTO change_queue(
44
+ INSERT INTO file_queue(
45
45
  file_id, path_after, data_after, metadata_after
46
46
  )
47
47
  VALUES (
48
48
  NEW.id, NEW.path, NEW.data, NEW.metadata
49
49
  );
50
- SELECT triggerChangeQueue();
50
+ SELECT triggerFileQueue();
51
51
  END;
52
52
 
53
53
  CREATE TRIGGER IF NOT EXISTS file_update BEFORE UPDATE ON file
54
54
  BEGIN
55
- INSERT INTO change_queue(
55
+ INSERT INTO file_queue(
56
56
  file_id,
57
57
  path_before, data_before, metadata_before,
58
58
  path_after, data_after, metadata_after
@@ -64,14 +64,14 @@ export function applySchema(args: { sqlite: SqliteDatabase }): SqliteDatabase {
64
64
  NEW.path, NEW.data, NEW.metadata
65
65
  );
66
66
 
67
- SELECT triggerChangeQueue();
67
+ SELECT triggerFileQueue();
68
68
  END;
69
69
 
70
70
  CREATE TRIGGER IF NOT EXISTS file_delete BEFORE DELETE ON file
71
71
  BEGIN
72
- INSERT INTO change_queue(file_id)
72
+ INSERT INTO file_queue(file_id)
73
73
  VALUES (OLD.id);
74
- SELECT triggerChangeQueue();
74
+ SELECT triggerFileQueue();
75
75
  END;
76
76
 
77
77
  CREATE TABLE IF NOT EXISTS change (
@@ -144,7 +144,7 @@ export function applySchema(args: { sqlite: SqliteDatabase }): SqliteDatabase {
144
144
  -- change sets
145
145
 
146
146
  CREATE TABLE IF NOT EXISTS change_set (
147
- id TEXT PRIMARY KEY DEFAULT (uuid_v7())
147
+ id TEXT PRIMARY KEY DEFAULT (nano_id(16))
148
148
  ) STRICT;
149
149
 
150
150
  CREATE TABLE IF NOT EXISTS change_set_element (
@@ -165,34 +165,21 @@ export function applySchema(args: { sqlite: SqliteDatabase }): SqliteDatabase {
165
165
  FOREIGN KEY(change_set_id) REFERENCES change_set(id)
166
166
  ) STRICT;
167
167
 
168
- CREATE TABLE IF NOT EXISTS change_set_label_author (
169
- label_id TEXT NOT NULL,
170
- change_set_id TEXT NOT NULL,
171
- account_id TEXT NOT NULL,
172
-
173
- PRIMARY KEY(label_id, change_set_id, account_id),
174
- FOREIGN KEY(label_id, change_set_id) REFERENCES change_set_label(label_id, change_set_id),
175
- FOREIGN KEY(account_id) REFERENCES account(id)
176
- ) STRICT;
177
-
178
168
  -- discussions
179
169
 
180
170
  CREATE TABLE IF NOT EXISTS discussion (
181
- id TEXT PRIMARY KEY DEFAULT (uuid_v7()),
171
+ id TEXT PRIMARY KEY DEFAULT (nano_id(12)),
182
172
  change_set_id TEXT NOT NULL,
183
173
 
184
174
  FOREIGN KEY(change_set_id) REFERENCES change_set(id)
185
175
  ) STRICT;
186
176
 
187
177
  CREATE TABLE IF NOT EXISTS comment (
188
- id TEXT PRIMARY KEY DEFAULT (uuid_v7()),
178
+ id TEXT PRIMARY KEY DEFAULT (nano_id(14)),
189
179
  parent_id TEXT,
190
180
  discussion_id TEXT NULL,
191
- created_at TEXT DEFAULT CURRENT_TIMESTAMP NOT NULL,
192
181
  content TEXT NOT NULL,
193
- created_by TEXT NOT NULL,
194
182
 
195
- FOREIGN KEY(created_by) REFERENCES account(id),
196
183
  FOREIGN KEY(discussion_id) REFERENCES discussion(id),
197
184
  FOREIGN KEY(parent_id) REFERENCES comment(id)
198
185
  ) STRICT;
@@ -200,7 +187,7 @@ export function applySchema(args: { sqlite: SqliteDatabase }): SqliteDatabase {
200
187
  -- labels
201
188
 
202
189
  CREATE TABLE IF NOT EXISTS label (
203
- id TEXT PRIMARY KEY DEFAULT (uuid_v7()),
190
+ id TEXT PRIMARY KEY DEFAULT (nano_id(8)),
204
191
 
205
192
  name TEXT NOT NULL UNIQUE -- e.g., 'confirmed', 'reviewed'
206
193
 
@@ -214,23 +201,22 @@ export function applySchema(args: { sqlite: SqliteDatabase }): SqliteDatabase {
214
201
  CREATE TABLE IF NOT EXISTS version (
215
202
  id TEXT PRIMARY KEY DEFAULT (uuid_v7()),
216
203
 
217
- -- name is optional.
218
- --
219
- -- "anonymous" versiones can ease workflows.
220
- -- For example, a user can create a version
221
- -- without a name to experiment with
222
- -- changes with no mental overhead of
223
- -- naming the version.
224
- name TEXT
204
+ name TEXT NOT NULL UNIQUE DEFAULT (human_id())
225
205
  ) STRICT;
226
206
 
227
207
  CREATE TABLE IF NOT EXISTS version_change (
228
208
  version_id TEXT NOT NULL,
229
209
  change_id TEXT NOT NULL,
230
210
 
211
+ entity_id TEXT NOT NULL,
212
+ schema_key TEXT NOT NULL,
213
+ file_id TEXT NOT NULL,
214
+
231
215
  PRIMARY KEY (version_id, change_id),
232
216
  FOREIGN KEY (version_id) REFERENCES version(id) ON DELETE CASCADE,
233
- FOREIGN KEY (change_id) REFERENCES change(id) ON DELETE CASCADE
217
+ FOREIGN KEY (change_id, entity_id, schema_key, file_id) REFERENCES change(id, entity_id, schema_key, file_id) ON DELETE CASCADE,
218
+
219
+ UNIQUE (version_id, entity_id, schema_key, file_id)
234
220
  ) STRICT;
235
221
 
236
222
  CREATE TABLE IF NOT EXISTS version_change_conflict (
@@ -276,47 +262,8 @@ export function applySchema(args: { sqlite: SqliteDatabase }): SqliteDatabase {
276
262
  WHERE id = NEW.account_id;
277
263
  END;
278
264
 
279
- CREATE TEMP TRIGGER IF NOT EXISTS insert_account_if_not_exists_on_change_set_label_author
280
- BEFORE INSERT ON change_set_label_author
281
- FOR EACH ROW
282
- WHEN NEW.account_id NOT IN (SELECT id FROM account) AND NEW.account_id IN (SELECT id FROM temp.active_account)
283
- BEGIN
284
- INSERT OR IGNORE INTO account
285
- SELECT
286
- *
287
- FROM active_account
288
- WHERE id = NEW.account_id;
289
- END;
290
-
291
- CREATE TEMP TRIGGER IF NOT EXISTS insert_account_if_not_exists_on_comment
292
- BEFORE INSERT ON comment
293
- FOR EACH ROW
294
- WHEN NEW.created_by NOT IN (SELECT id FROM account) AND NEW.created_by IN (SELECT id FROM temp.active_account)
295
- BEGIN
296
- INSERT OR IGNORE INTO account
297
- SELECT
298
- *
299
- FROM active_account
300
- WHERE id = NEW.created_by;
301
- END;
302
-
303
-
304
265
  `;
305
266
 
306
- // CREATE TRIGGER IF NOT EXISTS insert_account_if_not_exists_on_change_set_label_author
307
- // BEFORE INSERT ON change_set_label_author
308
- // FOR EACH ROW
309
- // BEGIN
310
- // INSERT OR IGNORE INTO account (id, name)
311
- // VALUES (
312
- // NEW.account_id,
313
- // CASE
314
- // WHEN NEW.account_id LIKE 'anonymous_%' THEN 'anonymous'
315
- // ELSE NEW.account_id
316
- // END
317
- // );
318
- // END;
319
-
320
267
  applyMutationLogDatabaseSchema(args.sqlite);
321
268
 
322
269
  return args.sqlite;
@@ -28,7 +28,7 @@ test("handles joins", async () => {
28
28
  const mockFile0: LixFile = {
29
29
  id: "file-0",
30
30
  path: "/file-0",
31
- data: new ArrayBuffer(0),
31
+ data: new Uint8Array(),
32
32
  metadata: {},
33
33
  };
34
34
  const mockChange0 = mockChange({ id: "change-0", file_id: "file-0" });
@@ -56,7 +56,7 @@ test("does not transform the query or results (json parsing)", async () => {
56
56
  const mockFile0: LixFile = {
57
57
  id: "file-0",
58
58
  path: "/file-0",
59
- data: new ArrayBuffer(0),
59
+ data: new Uint8Array(),
60
60
  metadata: {
61
61
  foo: "bar",
62
62
  },
@@ -100,7 +100,7 @@ test("using executeSync with a 'fake async' function should work", async () => {
100
100
 
101
101
  const result = await fakeAyncQuery(lix);
102
102
 
103
- expect(result).toEqual([{ key: "foo", value: "bar" }]);
103
+ expect(result).toMatchObject([{ key: "foo", value: "bar" }]);
104
104
  });
105
105
 
106
106
  test("it works with kysely transactions", async () => {
@@ -7,31 +7,28 @@ import { jsonSha256 } from "../snapshot/json-sha-256.js";
7
7
  import { sql } from "kysely";
8
8
  import { openLixInMemory } from "../lix/open-lix-in-memory.js";
9
9
  import { updateChangesInVersion } from "../version/update-changes-in-version.js";
10
+ import { createVersion } from "../version/create-version.js";
11
+
12
+ // file ids are always in the URL of lix apps
13
+ // to increase sharing, the ids should be as short as possible
14
+ //
15
+ // 129 million file creations will lead to a 1% chance of a collision
16
+ //
17
+ // if someone uses lix to handle 129 million files, we can
18
+ // increase the length of the id :D
19
+ test("file ids should default to nano_id(10)", async () => {
20
+ const lix = await openLixInMemory({});
10
21
 
11
- test("file ids should default to uuid", async () => {
12
- const sqlite = await createInMemoryDatabase({
13
- readOnly: false,
14
- });
15
- const db = initDb({ sqlite });
16
-
17
- // init the trigger function (usually defined by lix only)
18
- sqlite.createFunction({
19
- name: "triggerChangeQueue",
20
- arity: 0,
21
- // @ts-expect-error - dynamic function
22
- xFunc: () => {},
23
- });
24
-
25
- const file = await db
22
+ const file = await lix.db
26
23
  .insertInto("file")
27
24
  .values({
28
- path: "/mock",
25
+ path: "/mock.txt",
29
26
  data: new Uint8Array(),
30
27
  })
31
28
  .returningAll()
32
29
  .executeTakeFirstOrThrow();
33
30
 
34
- expect(validate(file.id)).toBe(true);
31
+ expect(file.id.length).toBe(10);
35
32
  });
36
33
 
37
34
  test("change ids should default to uuid", async () => {
@@ -142,7 +139,7 @@ test("files should be able to have metadata", async () => {
142
139
  const db = initDb({ sqlite });
143
140
 
144
141
  sqlite.createFunction({
145
- name: "triggerChangeQueue",
142
+ name: "triggerFileQueue",
146
143
  arity: 0,
147
144
  // @ts-expect-error - dynamic function
148
145
  xFunc: () => {
@@ -243,6 +240,98 @@ test("change set items must be unique", async () => {
243
240
  );
244
241
  });
245
242
 
243
+ // 8B IDs needed, in order to have a 1% probability of at least one collision.
244
+ test("discussion.id are nano_id(12)", async () => {
245
+ const sqlite = await createInMemoryDatabase({
246
+ readOnly: false,
247
+ });
248
+ const db = initDb({ sqlite });
249
+
250
+ const changeSet = await db
251
+ .insertInto("change_set")
252
+ .defaultValues()
253
+ .returningAll()
254
+ .executeTakeFirstOrThrow();
255
+
256
+ const discussion = await db
257
+ .insertInto("discussion")
258
+ .values({
259
+ change_set_id: changeSet.id,
260
+ })
261
+ .returningAll()
262
+ .executeTakeFirstOrThrow();
263
+
264
+ expect(discussion.id.length).toBe(12);
265
+ });
266
+
267
+ // 499B IDs needed, in order to have a 1% probability of at least one collision.
268
+ test("comment.id are nano_id(14)", async () => {
269
+ const sqlite = await createInMemoryDatabase({
270
+ readOnly: false,
271
+ });
272
+ const db = initDb({ sqlite });
273
+
274
+ const changeSet = await db
275
+ .insertInto("change_set")
276
+ .defaultValues()
277
+ .returningAll()
278
+ .executeTakeFirstOrThrow();
279
+
280
+ const discussion = await db
281
+ .insertInto("discussion")
282
+ .values({
283
+ change_set_id: changeSet.id,
284
+ })
285
+ .returningAll()
286
+ .executeTakeFirstOrThrow();
287
+
288
+ const comment = await db
289
+ .insertInto("comment")
290
+ .values({
291
+ discussion_id: discussion.id,
292
+ content: "mock",
293
+ })
294
+ .returningAll()
295
+ .executeTakeFirstOrThrow();
296
+
297
+ expect(comment.id.length).toBe(14);
298
+ });
299
+
300
+ // 30T IDs needed, in order to have a 1% probability of at least one collision
301
+ test("change_set.id are nano_id(16)", async () => {
302
+ const sqlite = await createInMemoryDatabase({
303
+ readOnly: false,
304
+ });
305
+ const db = initDb({ sqlite });
306
+
307
+ const changeSet = await db
308
+ .insertInto("change_set")
309
+ .defaultValues()
310
+ .returningAll()
311
+ .executeTakeFirstOrThrow();
312
+
313
+ expect(changeSet.id.length).toBe(16);
314
+ });
315
+
316
+ // 2M IDs needed, in order to have a 1% probability of at least one collision.
317
+ // it is assumed that creating 2 million labels is ... unlikely
318
+ test("label.id is nano_id(8)", async () => {
319
+ const sqlite = await createInMemoryDatabase({
320
+ readOnly: false,
321
+ });
322
+ const db = initDb({ sqlite });
323
+
324
+ const label = await db
325
+ .insertInto("label")
326
+ .values({
327
+ name: "mock",
328
+ })
329
+ .returningAll()
330
+ .executeTakeFirstOrThrow();
331
+
332
+ expect(label.id.length).toBe(8);
333
+ });
334
+
246
335
  test("creating multiple discussions for one change set should be possible", async () => {
247
336
  const sqlite = await createInMemoryDatabase({
248
337
  readOnly: false,
@@ -337,7 +426,7 @@ test("invalid file paths should be rejected", async () => {
337
426
 
338
427
  // init the trigger function (usually defined by lix only)
339
428
  sqlite.createFunction({
340
- name: "triggerChangeQueue",
429
+ name: "triggerFileQueue",
341
430
  arity: 0,
342
431
  // @ts-expect-error - dynamic function
343
432
  xFunc: () => {},
@@ -482,3 +571,58 @@ test("deleting a version cascades to version changes", async () => {
482
571
 
483
572
  expect(versionChangesAfterDelete).toHaveLength(0);
484
573
  });
574
+
575
+ test("version_change must have a unique entity_id, file_id, version_id, and schema_id", async () => {
576
+ const lix = await openLixInMemory({});
577
+
578
+ const version0 = await createVersion({ lix, name: "version0" });
579
+ const version1 = await createVersion({ lix, name: "version1" });
580
+
581
+ const mockChanges = [
582
+ mockChange({ entity_id: "mock0", file_id: "file0", schema_key: "file" }),
583
+ ] as const;
584
+
585
+ await lix.db.insertInto("change").values(mockChanges).execute();
586
+
587
+ const versionChange = {
588
+ change_id: mockChanges[0].id,
589
+ file_id: mockChanges[0].file_id,
590
+ entity_id: mockChanges[0].entity_id,
591
+ schema_key: mockChanges[0].schema_key,
592
+ };
593
+
594
+ await lix.db
595
+ .insertInto("version_change")
596
+ .values({ ...versionChange, version_id: version0.id })
597
+ .execute();
598
+
599
+ // inserting the same change again should throw
600
+ await expect(
601
+ lix.db
602
+ .insertInto("version_change")
603
+ .values({ ...versionChange, version_id: version0.id })
604
+ .execute()
605
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
606
+ `[SQLite3Error: SQLITE_CONSTRAINT_UNIQUE: sqlite3 result code 2067: UNIQUE constraint failed: version_change.version_id, version_change.entity_id, version_change.schema_key, version_change.file_id]`
607
+ );
608
+
609
+ // insert into version 1 should work
610
+ await lix.db
611
+ .insertInto("version_change")
612
+ .values({ ...versionChange, version_id: version1.id })
613
+ .execute();
614
+ });
615
+
616
+ test("versions have a unique and default human readable name", async () => {
617
+ const lix = await openLixInMemory({});
618
+
619
+ const version0 = await createVersion({ lix });
620
+ const version1 = await createVersion({ lix });
621
+
622
+ expect(version0.name).not.toBe(version1.name);
623
+
624
+ await expect(
625
+ createVersion({ lix, name: version0.name }),
626
+ "version.name is unique"
627
+ ).rejects.toThrow();
628
+ });
@@ -8,7 +8,9 @@ import { jsonSha256 } from "../snapshot/json-sha-256.js";
8
8
  import { ParseJsonBPluginV1 } from "./kysely-plugin/parse-jsonb-plugin-v1.js";
9
9
  import { SerializeJsonBPlugin } from "./kysely-plugin/serialize-jsonb-plugin.js";
10
10
  import { createSession } from "./mutation-log/lix-session.js";
11
- import { applyOwnEntityChangeControlTriggers } from "../own-entity-change-control/database-triggers.js";
11
+ import { applyOwnChangeControlTriggers } from "../own-change-control/database-triggers.js";
12
+ import { humanId } from "human-id";
13
+ import { nanoid } from "./nano-id.js";
12
14
 
13
15
  export function initDb(args: {
14
16
  sqlite: SqliteDatabase;
@@ -23,7 +25,7 @@ export function initDb(args: {
23
25
  ParseJsonBPluginV1({
24
26
  // jsonb columns
25
27
  file: ["metadata"],
26
- change_queue: ["metadata_before", "metadata_after"],
28
+ file_queue: ["metadata_before", "metadata_after"],
27
29
  snapshot: ["content"],
28
30
  mutation_log: ["row_id"],
29
31
  }),
@@ -32,7 +34,7 @@ export function initDb(args: {
32
34
  });
33
35
 
34
36
  // need to apply it here because db object needs to be available
35
- applyOwnEntityChangeControlTriggers(args.sqlite, db);
37
+ applyOwnChangeControlTriggers(args.sqlite, db);
36
38
  return db;
37
39
  }
38
40
 
@@ -91,4 +93,19 @@ function initFunctions(args: { sqlite: SqliteDatabase }) {
91
93
  arity: 0,
92
94
  xFunc: () => lixSession.sessionClockTick(),
93
95
  });
96
+
97
+ args.sqlite.createFunction({
98
+ name: "human_id",
99
+ arity: 0,
100
+ xFunc: () => humanId({ separator: "-", capitalize: false }),
101
+ });
102
+
103
+ args.sqlite.createFunction({
104
+ name: "nano_id",
105
+ arity: 1,
106
+ // @ts-expect-error - not sure why this is not working
107
+ xFunc: (_ctx: number, length: number) => {
108
+ return nanoid(length);
109
+ },
110
+ });
94
111
  }
@@ -1,9 +1,12 @@
1
1
  import { type KyselyPlugin } from "kysely";
2
- import { createInMemoryDatabase } from "sqlite-wasm-kysely";
2
+ import {
3
+ createInMemoryDatabase,
4
+ type SqliteDatabase,
5
+ } from "sqlite-wasm-kysely";
3
6
 
4
7
  // workaround for v1. v2 doesn't need to transform
5
8
  // jsonb columns during runtime
6
- const sqlite = await createInMemoryDatabase({});
9
+ let sqlite: SqliteDatabase;
7
10
 
8
11
  export function ParseJsonBPluginV1(
9
12
  jsonbColumns: Record<string, string[]>
@@ -14,6 +17,10 @@ export function ParseJsonBPluginV1(
14
17
 
15
18
  return {
16
19
  transformResult: async (args) => {
20
+ if (!sqlite) {
21
+ sqlite = await createInMemoryDatabase({});
22
+ }
23
+
17
24
  for (const row of args.result.rows) {
18
25
  for (const col of jsonColumnNames) {
19
26
  if (!row[col]) {
@@ -14,7 +14,7 @@ export const tablesByDepencies: string[] = [
14
14
  "change",
15
15
 
16
16
  // Depends on: file
17
- "change_queue",
17
+ "file_queue",
18
18
  // Depends on: change
19
19
  "change_author",
20
20
  // Depends on: change
@@ -27,8 +27,6 @@ export const tablesByDepencies: string[] = [
27
27
  "change_set_element",
28
28
  // Depends on: label, change_set
29
29
  "change_set_label",
30
- // Depends on: change_set_label
31
- "change_set_label_author",
32
30
  //Depends on: change_set
33
31
  "discussion",
34
32
  // Depends on: account, discussion, comment
@@ -0,0 +1,15 @@
1
+ import { expect, test } from "vitest";
2
+ import { _nanoIdAlphabet, nanoid } from "./nano-id.js";
3
+
4
+ test("length is obeyed", () => {
5
+ const id = nanoid(10);
6
+ expect(id.length).toBe(10);
7
+ });
8
+
9
+ test("the alphabet does not contain underscores `_` because they are not URL safe", () => {
10
+ expect(_nanoIdAlphabet).not.toContain("_");
11
+ });
12
+
13
+ test("the alphabet does not contain dashes `-` because they break selecting the ID from the URL in the browser", () => {
14
+ expect(_nanoIdAlphabet).not.toContain("-");
15
+ });
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Code taken from the [nanoid](https://github.com/ai/nanoid/blob/main/index.browser.js)
3
+ * browser implementation. The code is licensed under the MIT license.
4
+ *
5
+ */
6
+
7
+ const random = (bytes: any) => crypto.getRandomValues(new Uint8Array(bytes));
8
+
9
+ const customRandom = (
10
+ alphabet: string,
11
+ defaultSize: number,
12
+ getRandom: any
13
+ ) => {
14
+ // First, a bitmask is necessary to generate the ID. The bitmask makes bytes
15
+ // values closer to the alphabet size. The bitmask calculates the closest
16
+ // `2^31 - 1` number, which exceeds the alphabet size.
17
+ // For example, the bitmask for the alphabet size 30 is 31 (00011111).
18
+ // `Math.clz32` is not used, because it is not available in browsers.
19
+ const mask = (2 << Math.log2(alphabet.length - 1)) - 1;
20
+ // Though, the bitmask solution is not perfect since the bytes exceeding
21
+ // the alphabet size are refused. Therefore, to reliably generate the ID,
22
+ // the random bytes redundancy has to be satisfied.
23
+
24
+ // Note: every hardware random generator call is performance expensive,
25
+ // because the system call for entropy collection takes a lot of time.
26
+ // So, to avoid additional system calls, extra bytes are requested in advance.
27
+
28
+ // Next, a step determines how many random bytes to generate.
29
+ // The number of random bytes gets decided upon the ID size, mask,
30
+ // alphabet size, and magic number 1.6 (using 1.6 peaks at performance
31
+ // according to benchmarks).
32
+
33
+ // `-~f => Math.ceil(f)` if f is a float
34
+ // `-~i => i + 1` if i is an integer
35
+ const step = -~((1.6 * mask * defaultSize) / alphabet.length);
36
+
37
+ return (size = defaultSize) => {
38
+ let id = "";
39
+ while (true) {
40
+ const bytes = getRandom(step);
41
+ // A compact alternative for `for (var i = 0; i < step; i++)`.
42
+ let j = step | 0;
43
+ while (j--) {
44
+ // Adding `|| ''` refuses a random byte that exceeds the alphabet size.
45
+ id += alphabet[bytes[j] & mask] || "";
46
+ if (id.length >= size) return id;
47
+ }
48
+ }
49
+ };
50
+ };
51
+
52
+ const customAlphabet = (alphabet: string, size = 21) =>
53
+ customRandom(alphabet, size | 0, random);
54
+
55
+ /**
56
+ * Uses every character from nano id except for the `-` and `_` character.
57
+ *
58
+ * - Underscore `_` is not URL safe https://github.com/ai/nanoid/issues/347.
59
+ * - Dash `-` breaks selecting the ID from the URL in the browser.
60
+ */
61
+ export const _nanoIdAlphabet =
62
+ "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
63
+
64
+ /**
65
+ * Generate secure URL-friendly unique ID.
66
+ *
67
+ * Use https://zelark.github.io/nano-id-cc/ to calculate the length
68
+ * of the ID for the use case with the alphabet provided in the
69
+ * implementation.
70
+ */
71
+ export const nanoid: (size?: number) => string =
72
+ customAlphabet(_nanoIdAlphabet);