@factoidal/core 0.3.0 → 0.5.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 (126) hide show
  1. package/CHANGELOG.md +167 -0
  2. package/NOTICE +31 -0
  3. package/README.md +70 -8
  4. package/bin/factoidal.mjs +250 -14
  5. package/bin/pack-host.mjs +341 -0
  6. package/bin/pack-worker.mjs +31 -0
  7. package/bin/pack.mjs +174 -0
  8. package/bin/store.mjs +32 -0
  9. package/l4-assets/l4factoidal.js +61 -1
  10. package/l4-assets/l4factoidal.mjs +1 -1
  11. package/l4-assets/l4factoidal.wasm +0 -0
  12. package/l4-assets/version.json +4 -4
  13. package/package.json +19 -2
  14. package/sample-store/CURRENT +1 -0
  15. package/sample-store/gen-1/manifest.sbm2 +0 -0
  16. package/sample-store/gen-1/manifest.tsv +14 -0
  17. package/sample-store/gen-1/predicate-0.ibk3 +0 -0
  18. package/sample-store/gen-1/predicate-0.ibk3.merkle +1 -0
  19. package/sample-store/gen-1/predicate-0.ibk3.oli2 +0 -0
  20. package/sample-store/gen-1/predicate-0.ibk3.oli2.merkle +1 -0
  21. package/sample-store/gen-1/predicate-0.ibk3.sri2 +0 -0
  22. package/sample-store/gen-1/predicate-0.ibk3.sri2.merkle +1 -0
  23. package/sample-store/gen-1/predicate-0.ibk3.tli1 +0 -0
  24. package/sample-store/gen-1/predicate-0.ibk3.tli1.merkle +0 -0
  25. package/sample-store/gen-1/predicate-1.ibk3 +0 -0
  26. package/sample-store/gen-1/predicate-1.ibk3.merkle +0 -0
  27. package/sample-store/gen-1/predicate-1.ibk3.oli2 +0 -0
  28. package/sample-store/gen-1/predicate-1.ibk3.oli2.merkle +0 -0
  29. package/sample-store/gen-1/predicate-1.ibk3.sri2 +0 -0
  30. package/sample-store/gen-1/predicate-1.ibk3.sri2.merkle +1 -0
  31. package/sample-store/gen-1/predicate-1.ibk3.tli1 +0 -0
  32. package/sample-store/gen-1/predicate-1.ibk3.tli1.merkle +1 -0
  33. package/sample-store/gen-1/predicate-10.ibk3 +0 -0
  34. package/sample-store/gen-1/predicate-10.ibk3.merkle +1 -0
  35. package/sample-store/gen-1/predicate-10.ibk3.oli2 +0 -0
  36. package/sample-store/gen-1/predicate-10.ibk3.oli2.merkle +0 -0
  37. package/sample-store/gen-1/predicate-10.ibk3.sri2 +0 -0
  38. package/sample-store/gen-1/predicate-10.ibk3.sri2.merkle +1 -0
  39. package/sample-store/gen-1/predicate-10.ibk3.tli1 +0 -0
  40. package/sample-store/gen-1/predicate-10.ibk3.tli1.merkle +1 -0
  41. package/sample-store/gen-1/predicate-11.ibk3 +0 -0
  42. package/sample-store/gen-1/predicate-11.ibk3.merkle +0 -0
  43. package/sample-store/gen-1/predicate-11.ibk3.oli2 +0 -0
  44. package/sample-store/gen-1/predicate-11.ibk3.oli2.merkle +1 -0
  45. package/sample-store/gen-1/predicate-11.ibk3.sri2 +0 -0
  46. package/sample-store/gen-1/predicate-11.ibk3.sri2.merkle +1 -0
  47. package/sample-store/gen-1/predicate-11.ibk3.tli1 +0 -0
  48. package/sample-store/gen-1/predicate-11.ibk3.tli1.merkle +1 -0
  49. package/sample-store/gen-1/predicate-12.ibk3 +0 -0
  50. package/sample-store/gen-1/predicate-12.ibk3.merkle +1 -0
  51. package/sample-store/gen-1/predicate-12.ibk3.oli2 +0 -0
  52. package/sample-store/gen-1/predicate-12.ibk3.oli2.merkle +1 -0
  53. package/sample-store/gen-1/predicate-12.ibk3.sri2 +0 -0
  54. package/sample-store/gen-1/predicate-12.ibk3.sri2.merkle +1 -0
  55. package/sample-store/gen-1/predicate-12.ibk3.tli1 +0 -0
  56. package/sample-store/gen-1/predicate-12.ibk3.tli1.merkle +1 -0
  57. package/sample-store/gen-1/predicate-2.ibk3 +0 -0
  58. package/sample-store/gen-1/predicate-2.ibk3.merkle +1 -0
  59. package/sample-store/gen-1/predicate-2.ibk3.oli2 +0 -0
  60. package/sample-store/gen-1/predicate-2.ibk3.oli2.merkle +1 -0
  61. package/sample-store/gen-1/predicate-2.ibk3.sri2 +0 -0
  62. package/sample-store/gen-1/predicate-2.ibk3.sri2.merkle +0 -0
  63. package/sample-store/gen-1/predicate-2.ibk3.tli1 +0 -0
  64. package/sample-store/gen-1/predicate-2.ibk3.tli1.merkle +0 -0
  65. package/sample-store/gen-1/predicate-3.ibk3 +0 -0
  66. package/sample-store/gen-1/predicate-3.ibk3.merkle +1 -0
  67. package/sample-store/gen-1/predicate-3.ibk3.oli2 +0 -0
  68. package/sample-store/gen-1/predicate-3.ibk3.oli2.merkle +1 -0
  69. package/sample-store/gen-1/predicate-3.ibk3.sri2 +0 -0
  70. package/sample-store/gen-1/predicate-3.ibk3.sri2.merkle +1 -0
  71. package/sample-store/gen-1/predicate-3.ibk3.tli1 +0 -0
  72. package/sample-store/gen-1/predicate-3.ibk3.tli1.merkle +1 -0
  73. package/sample-store/gen-1/predicate-4.ibk3 +0 -0
  74. package/sample-store/gen-1/predicate-4.ibk3.merkle +0 -0
  75. package/sample-store/gen-1/predicate-4.ibk3.oli2 +0 -0
  76. package/sample-store/gen-1/predicate-4.ibk3.oli2.merkle +1 -0
  77. package/sample-store/gen-1/predicate-4.ibk3.sri2 +0 -0
  78. package/sample-store/gen-1/predicate-4.ibk3.sri2.merkle +1 -0
  79. package/sample-store/gen-1/predicate-4.ibk3.tli1 +0 -0
  80. package/sample-store/gen-1/predicate-4.ibk3.tli1.merkle +2 -0
  81. package/sample-store/gen-1/predicate-5.ibk3 +0 -0
  82. package/sample-store/gen-1/predicate-5.ibk3.merkle +1 -0
  83. package/sample-store/gen-1/predicate-5.ibk3.oli2 +0 -0
  84. package/sample-store/gen-1/predicate-5.ibk3.oli2.merkle +1 -0
  85. package/sample-store/gen-1/predicate-5.ibk3.sri2 +0 -0
  86. package/sample-store/gen-1/predicate-5.ibk3.sri2.merkle +1 -0
  87. package/sample-store/gen-1/predicate-5.ibk3.tli1 +0 -0
  88. package/sample-store/gen-1/predicate-5.ibk3.tli1.merkle +1 -0
  89. package/sample-store/gen-1/predicate-6.ibk3 +0 -0
  90. package/sample-store/gen-1/predicate-6.ibk3.merkle +1 -0
  91. package/sample-store/gen-1/predicate-6.ibk3.oli2 +0 -0
  92. package/sample-store/gen-1/predicate-6.ibk3.oli2.merkle +1 -0
  93. package/sample-store/gen-1/predicate-6.ibk3.sri2 +0 -0
  94. package/sample-store/gen-1/predicate-6.ibk3.sri2.merkle +1 -0
  95. package/sample-store/gen-1/predicate-6.ibk3.tli1 +0 -0
  96. package/sample-store/gen-1/predicate-6.ibk3.tli1.merkle +0 -0
  97. package/sample-store/gen-1/predicate-7.ibk3 +0 -0
  98. package/sample-store/gen-1/predicate-7.ibk3.merkle +0 -0
  99. package/sample-store/gen-1/predicate-7.ibk3.oli2 +0 -0
  100. package/sample-store/gen-1/predicate-7.ibk3.oli2.merkle +2 -0
  101. package/sample-store/gen-1/predicate-7.ibk3.sri2 +0 -0
  102. package/sample-store/gen-1/predicate-7.ibk3.sri2.merkle +1 -0
  103. package/sample-store/gen-1/predicate-7.ibk3.tli1 +0 -0
  104. package/sample-store/gen-1/predicate-7.ibk3.tli1.merkle +1 -0
  105. package/sample-store/gen-1/predicate-8.ibk3 +0 -0
  106. package/sample-store/gen-1/predicate-8.ibk3.merkle +1 -0
  107. package/sample-store/gen-1/predicate-8.ibk3.oli2 +0 -0
  108. package/sample-store/gen-1/predicate-8.ibk3.oli2.merkle +1 -0
  109. package/sample-store/gen-1/predicate-8.ibk3.sri2 +0 -0
  110. package/sample-store/gen-1/predicate-8.ibk3.sri2.merkle +1 -0
  111. package/sample-store/gen-1/predicate-8.ibk3.tli1 +0 -0
  112. package/sample-store/gen-1/predicate-8.ibk3.tli1.merkle +1 -0
  113. package/sample-store/gen-1/predicate-9.ibk3 +0 -0
  114. package/sample-store/gen-1/predicate-9.ibk3.merkle +1 -0
  115. package/sample-store/gen-1/predicate-9.ibk3.oli2 +0 -0
  116. package/sample-store/gen-1/predicate-9.ibk3.oli2.merkle +1 -0
  117. package/sample-store/gen-1/predicate-9.ibk3.sri2 +0 -0
  118. package/sample-store/gen-1/predicate-9.ibk3.sri2.merkle +3 -0
  119. package/sample-store/gen-1/predicate-9.ibk3.tli1 +0 -0
  120. package/sample-store/gen-1/predicate-9.ibk3.tli1.merkle +2 -0
  121. package/sample-store.d.ts +16 -0
  122. package/sample-store.mjs +37 -0
  123. package/store-host/deno.mjs +63 -0
  124. package/store-host/index.mjs +39 -0
  125. package/store-host/node.mjs +62 -1
  126. package/version.json +1 -1
@@ -0,0 +1 @@
1
+ i���<�%4�0���z."~�v4n�* $ǝ
@@ -0,0 +1 @@
1
+ ��(WUVh�ؠ$w���6�zh��L�Qo���
@@ -0,0 +1 @@
1
+ �Đ��5 �k��EQ2��zE��;��oɗ�
@@ -0,0 +1 @@
1
+ ���2N����Y�M~8������3�(z>l�r��
@@ -0,0 +1 @@
1
+ �6�� %v� .+u���}0�v�0��������
@@ -0,0 +1 @@
1
+ q�/F����AL&��z�1�z�YP�x#
@@ -0,0 +1,2 @@
1
+ �w�#���H��Ѐ:4����<V�
2
+ Q�n��
@@ -0,0 +1 @@
1
+ �~��CV�L��3&#�|��D�6�� ��
@@ -0,0 +1 @@
1
+ �8}� �P0�oK���5t ����8�fOR�
@@ -0,0 +1 @@
1
+ _��k��2Ǟ��bk[�r�Փ���>+�8�"
@@ -0,0 +1 @@
1
+ ���]",�b��\�B\�$o n�ΰ�gcEb
@@ -0,0 +1 @@
1
+ `��y�
@@ -0,0 +1 @@
1
+ Zvt&��Sv�V�u¹t��ڼ{7�MP��j*
@@ -0,0 +1 @@
1
+ !'M�Uw����,GD��������A�|���a�
@@ -0,0 +1,3 @@
1
+ ��9\X��
2
+
3
+ �cGC�%Mο�c���*��G?
@@ -0,0 +1,2 @@
1
+
2
+ >��fa���?��i�+-N���>R��
@@ -0,0 +1,16 @@
1
+ /** The collection root of the sample Shardborough store bundled with this
2
+ * package: the directory that holds CURRENT. */
3
+ export declare function sampleStorePath (): string
4
+
5
+ export declare const sampleStoreFacts: Readonly<{
6
+ triples: number
7
+ blocks: number
8
+ layout: string
9
+ wireVersion: number
10
+ source: string
11
+ licence: string
12
+ licenceUrl: string
13
+ publisher: string
14
+ }>
15
+
16
+ export default sampleStorePath
@@ -0,0 +1,37 @@
1
+ // The sample Shardborough store this package carries, so a fresh install
2
+ // answers a SPARQL query with no other download and no native binary.
3
+ //
4
+ // The store was packed by `l4block-shard-pack … ibk3` and activated by
5
+ // `l4block-shard-activate`. Nothing here decodes it; this module only
6
+ // resolves its path inside the installed package.
7
+ //
8
+ // CONTENT AND LICENCE. Five IPTC NewsCodes vocabularies, published by the
9
+ // IPTC under CC BY 4.0 and taken from danbri/skosdex third_party/skos:
10
+ // spamfstat, videoqualifier, subjectqualifier, videocodec and spct.
11
+ // 4,434 triples in 13 predicate blocks. See NOTICE.
12
+
13
+ import { fileUrlToPath, joinPath } from './store-host/paths.mjs'
14
+
15
+ /** The collection root of the bundled sample store: the directory holding
16
+ * CURRENT. Pass it to `factoidal inspect` / `factoidal query`, or to
17
+ * `openStore` from `@factoidal/core/store-host`. */
18
+ export function sampleStorePath () {
19
+ const packageRoot = fileUrlToPath(new URL('.', import.meta.url).href)
20
+ return joinPath(packageRoot, 'sample-store')
21
+ }
22
+
23
+ /** What the sample store holds, as recorded when it was packed. The engine
24
+ * is the authority on every one of these numbers at run time; they are here
25
+ * so a caller can print something before it opens the store. */
26
+ export const sampleStoreFacts = Object.freeze({
27
+ triples: 4434,
28
+ blocks: 13,
29
+ layout: 'predicate-ibk3-ptd1-sri2-tli1-oli2-merkle-v0',
30
+ wireVersion: 6,
31
+ source: 'IPTC NewsCodes (spamfstat, videoqualifier, subjectqualifier, videocodec, spct)',
32
+ licence: 'CC BY 4.0',
33
+ licenceUrl: 'https://creativecommons.org/licenses/by/4.0/',
34
+ publisher: 'https://iptc.org/'
35
+ })
36
+
37
+ export default sampleStorePath
@@ -79,6 +79,69 @@ export function readRange (path, offset, length) {
79
79
  }
80
80
  }
81
81
 
82
+ /**
83
+ * Read at most `length` bytes at `offset`. Unlike `readRange` a short read
84
+ * is NOT an error: it is how the reader learns it reached the end of the
85
+ * file. The packer streams a file it did not stat first, so it needs this
86
+ * shape rather than the exact-length one.
87
+ */
88
+ export function readChunk (path, offset, length) {
89
+ if (length === 0) return new Uint8Array(0)
90
+ const file = openRead(path)
91
+ try {
92
+ const out = new Uint8Array(length)
93
+ const done = readInto(file, out, length, offset, path)
94
+ return done === length ? out : out.subarray(0, done)
95
+ } finally {
96
+ file.close()
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Create `path` and write `bytes`. Refuses to replace an existing file:
102
+ * a generation directory is immutable once written, and a packer that
103
+ * silently overwrote an artifact would hide a name collision.
104
+ */
105
+ export function writeNew (path, bytes) {
106
+ let file
107
+ try {
108
+ file = Deno.openSync(path, { write: true, createNew: true })
109
+ } catch (cause) {
110
+ if (cause instanceof Deno.errors.AlreadyExists) {
111
+ throw new StoreHostError('FILE_EXISTS', `${path} already exists`, { path })
112
+ }
113
+ throw wrap('OPEN_FAILED', `cannot create ${path}`, path, cause)
114
+ }
115
+ try {
116
+ let done = 0
117
+ while (done < bytes.length) {
118
+ let written
119
+ try {
120
+ written = file.writeSync(bytes.subarray(done))
121
+ } catch (cause) {
122
+ throw wrap('WRITE_FAILED', `write failed on ${path}`, path, cause)
123
+ }
124
+ done += written
125
+ }
126
+ try {
127
+ file.syncSync()
128
+ } catch (cause) {
129
+ throw wrap('FSYNC_FAILED', `fsync failed on ${path}`, path, cause)
130
+ }
131
+ } finally {
132
+ file.close()
133
+ }
134
+ }
135
+
136
+ /** Create a directory and every missing parent. */
137
+ export function makeDirectory (path) {
138
+ try {
139
+ Deno.mkdirSync(path, { recursive: true })
140
+ } catch (cause) {
141
+ throw wrap('MKDIR_FAILED', `cannot create ${path}`, path, cause)
142
+ }
143
+ }
144
+
82
145
  export function appendSyncAtSize (path, bytes, expectedSize) {
83
146
  let file
84
147
  try {
@@ -105,6 +105,45 @@ export function readRange (path, offset, length) {
105
105
  return impl.readRange(path, offset, length)
106
106
  }
107
107
 
108
+ /**
109
+ * Read at most `length` bytes starting at `offset`. A short read is not an
110
+ * error: fewer bytes than asked for means the file ended there, and zero
111
+ * bytes means the offset is at or past the end. This is the shape a
112
+ * streaming reader needs; `readRange` is the exact-length shape the store
113
+ * uses when the manifest already committed a length.
114
+ * @param {string} path
115
+ * @param {number} offset
116
+ * @param {number} length
117
+ * @returns {Uint8Array} between 0 and `length` bytes
118
+ */
119
+ export function readChunk (path, offset, length) {
120
+ requirePath(path, 'path')
121
+ requireCount(offset, 'offset')
122
+ requireCount(length, 'length')
123
+ return impl.readChunk(path, offset, length)
124
+ }
125
+
126
+ /**
127
+ * Create `path`, write `bytes` and fsync. Refuses an existing file with
128
+ * code FILE_EXISTS: a generation directory is immutable once written, so
129
+ * a silent overwrite would hide a name collision rather than report it.
130
+ * @param {string} path
131
+ * @param {Uint8Array} bytes
132
+ */
133
+ export function writeNew (path, bytes) {
134
+ requirePath(path, 'path')
135
+ impl.writeNew(path, bytes)
136
+ }
137
+
138
+ /**
139
+ * Create a directory and every missing parent.
140
+ * @param {string} path
141
+ */
142
+ export function makeDirectory (path) {
143
+ requirePath(path, 'path')
144
+ impl.makeDirectory(path)
145
+ }
146
+
108
147
  /**
109
148
  * Append `bytes` only if the file currently has exactly `expectedSize`
110
149
  * bytes, then fsync it. The counterpart of
@@ -6,7 +6,7 @@
6
6
  // moves bytes, and syncs.
7
7
 
8
8
  import {
9
- closeSync, fstatSync, fsyncSync, openSync, readSync, readdirSync,
9
+ closeSync, fstatSync, fsyncSync, mkdirSync, openSync, readSync, readdirSync,
10
10
  renameSync, statSync, unlinkSync, writeSync
11
11
  } from 'node:fs'
12
12
 
@@ -85,6 +85,67 @@ export function readRange (path, offset, length) {
85
85
  }
86
86
  }
87
87
 
88
+ /**
89
+ * Read at most `length` bytes at `offset`. Unlike `readRange` a short read
90
+ * is NOT an error: it is how the reader learns it reached the end of the
91
+ * file. The packer streams a file it did not stat first, so it needs this
92
+ * shape rather than the exact-length one.
93
+ */
94
+ export function readChunk (path, offset, length) {
95
+ if (length === 0) return new Uint8Array(0)
96
+ const fd = openRead(path)
97
+ try {
98
+ const out = new Uint8Array(length)
99
+ const done = preadInto(fd, out, 0, length, offset, path)
100
+ return done === length ? out : out.subarray(0, done)
101
+ } finally {
102
+ closeSync(fd)
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Create `path` and write `bytes`. Refuses to replace an existing file:
108
+ * a generation directory is immutable once written, and a packer that
109
+ * silently overwrote an artifact would hide a name collision.
110
+ */
111
+ export function writeNew (path, bytes) {
112
+ let fd
113
+ try {
114
+ // 'wx' is O_WRONLY | O_CREAT | O_EXCL.
115
+ fd = openSync(path, 'wx')
116
+ } catch (cause) {
117
+ if (cause && cause.code === 'EEXIST') {
118
+ throw new StoreHostError('FILE_EXISTS', `${path} already exists`, { path })
119
+ }
120
+ throw wrap('OPEN_FAILED', `cannot create ${path}`, path, cause)
121
+ }
122
+ try {
123
+ let done = 0
124
+ while (done < bytes.length) {
125
+ let written
126
+ try {
127
+ written = writeSync(fd, bytes, done, bytes.length - done, null)
128
+ } catch (cause) {
129
+ if (isInterrupt(cause)) continue
130
+ throw wrap('WRITE_FAILED', `write failed on ${path}`, path, cause)
131
+ }
132
+ done += written
133
+ }
134
+ fsyncSync(fd)
135
+ } finally {
136
+ closeSync(fd)
137
+ }
138
+ }
139
+
140
+ /** Create a directory and every missing parent. */
141
+ export function makeDirectory (path) {
142
+ try {
143
+ mkdirSync(path, { recursive: true })
144
+ } catch (cause) {
145
+ throw wrap('MKDIR_FAILED', `cannot create ${path}`, path, cause)
146
+ }
147
+ }
148
+
88
149
  export function appendSyncAtSize (path, bytes, expectedSize) {
89
150
  let fd
90
151
  try {
package/version.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.3.0",
2
+ "version": "0.5.0",
3
3
  "gitSha": "49f8ca4d70bf57c12fea445611e2b01b067bf6ba",
4
4
  "builtAt": "2026-08-26T21:45:40Z",
5
5
  "claims": {