@stream44.studio/t44-blockchaincommons.com 0.1.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/.dco-signatures +9 -0
  2. package/.github/workflows/dco.yml +12 -0
  3. package/.github/workflows/gordian-open-integrity.yml +13 -0
  4. package/.o/GordianOpenIntegrity-CurrentLifehash.svg +1026 -0
  5. package/.o/GordianOpenIntegrity-InceptionLifehash.svg +1026 -0
  6. package/.o/GordianOpenIntegrity.yaml +25 -0
  7. package/DCO.md +34 -0
  8. package/README.md +210 -0
  9. package/action.yml +47 -0
  10. package/bin/oi +152 -0
  11. package/caps/GordianOpenIntegrity.test.ts +879 -0
  12. package/caps/GordianOpenIntegrity.ts +821 -0
  13. package/caps/XidDocumentLedger.test.ts +687 -0
  14. package/caps/XidDocumentLedger.ts +545 -0
  15. package/caps/__snapshots__/XidDocumentLedger.test.ts.snap +11 -0
  16. package/caps/__snapshots__/XidLedger.test.ts.snap +11 -0
  17. package/caps/lifehash.test.ts +302 -0
  18. package/caps/lifehash.ts +142 -0
  19. package/caps/open-integrity-js.test.ts +252 -0
  20. package/caps/open-integrity-js.ts +485 -0
  21. package/caps/open-integrity-sh.test.ts +188 -0
  22. package/caps/open-integrity-sh.ts +187 -0
  23. package/caps/open-integrity.test.ts +259 -0
  24. package/caps/provenance-mark-cli.test.ts +387 -0
  25. package/caps/provenance-mark-cli.ts +174 -0
  26. package/caps/provenance-mark.test.ts +233 -0
  27. package/caps/provenance-mark.ts +223 -0
  28. package/caps/xid.test.ts +828 -0
  29. package/caps/xid.ts +565 -0
  30. package/examples/01-XID-DocumentLedger/__snapshots__/main.test.ts.snap +10 -0
  31. package/examples/01-XID-DocumentLedger/main.test.ts +182 -0
  32. package/examples/02-XID-Rotate-InceptionKey/__snapshots__/main.test.ts.snap +53 -0
  33. package/examples/02-XID-Rotate-InceptionKey/main.test.ts +232 -0
  34. package/examples/03-GordianOpenIntegrity/main.test.ts +176 -0
  35. package/examples/04-GordianOpenIntegrityCli/main.test.ts +119 -0
  36. package/package.json +37 -0
  37. package/tsconfig.json +28 -0
@@ -0,0 +1,387 @@
1
+ #!/usr/bin/env bun test
2
+
3
+ import * as bunTest from 'bun:test'
4
+ import { run } from 't44/workspace-rt'
5
+ import { join } from 'path'
6
+ import { rm, mkdir } from 'fs/promises'
7
+
8
+ const WORK_DIR = join(import.meta.dir, '.~provenance-mark-cli')
9
+
10
+ const {
11
+ test: { describe, it, expect },
12
+ cli,
13
+ } = await run(async ({ encapsulate, CapsulePropertyTypes, makeImportStack }: any) => {
14
+ const spine = await encapsulate({
15
+ '#@stream44.studio/encapsulate/spine-contracts/CapsuleSpineContract.v0': {
16
+ '#@stream44.studio/encapsulate/structs/Capsule': {},
17
+ '#': {
18
+ test: {
19
+ type: CapsulePropertyTypes.Mapping,
20
+ value: 't44/caps/WorkspaceTest',
21
+ options: {
22
+ '#': {
23
+ bunTest,
24
+ env: {}
25
+ }
26
+ }
27
+ },
28
+ cli: {
29
+ type: CapsulePropertyTypes.Mapping,
30
+ value: './provenance-mark-cli'
31
+ },
32
+ }
33
+ }
34
+ }, {
35
+ importMeta: import.meta,
36
+ importStack: makeImportStack(),
37
+ capsuleName: 't44/caps/providers/blockchaincommons.com/provenance-mark-cli.test'
38
+ })
39
+ return { spine }
40
+ }, async ({ spine, apis }: any) => {
41
+ return apis[spine.capsuleSourceLineRef]
42
+ }, {
43
+ importMeta: import.meta
44
+ })
45
+
46
+ const fs = await import('fs')
47
+ const path = await import('path')
48
+
49
+ // Clean up before tests
50
+ await rm(WORK_DIR, { recursive: true, force: true })
51
+ await mkdir(WORK_DIR, { recursive: true })
52
+
53
+ describe('Provenance Mark CLI Capsule', function () {
54
+
55
+ let chainDir: string
56
+
57
+ // ──────────────────────────────────────────────────────────────────
58
+ // 1. Create a new chain (provenance new)
59
+ // ──────────────────────────────────────────────────────────────────
60
+
61
+ describe('1. New chain', function () {
62
+
63
+ it('should create a new provenance mark chain with default resolution', async function () {
64
+ chainDir = path.join(WORK_DIR, 'mychain')
65
+
66
+ const output = await cli.newChain({
67
+ path: chainDir,
68
+ comment: 'Genesis mark for tests',
69
+ date: new Date(Date.UTC(2025, 0, 1)),
70
+ quiet: true,
71
+ })
72
+
73
+ expect(typeof output).toBe('string')
74
+ expect(output.length).toBeGreaterThan(0)
75
+
76
+ // Verify directory structure
77
+ expect(fs.existsSync(chainDir)).toBe(true)
78
+ expect(fs.existsSync(path.join(chainDir, 'generator.json'))).toBe(true)
79
+ expect(fs.existsSync(path.join(chainDir, 'marks'))).toBe(true)
80
+ expect(fs.existsSync(path.join(chainDir, 'marks', 'mark-0.json'))).toBe(true)
81
+ })
82
+
83
+ it('should create a chain with a specific resolution', async function () {
84
+ const highResDir = path.join(WORK_DIR, 'highres-chain')
85
+
86
+ const output = await cli.newChain({
87
+ path: highResDir,
88
+ resolution: 'high',
89
+ comment: 'High resolution chain',
90
+ date: new Date(Date.UTC(2025, 0, 1)),
91
+ quiet: true,
92
+ })
93
+
94
+ expect(output.length).toBeGreaterThan(0)
95
+ expect(fs.existsSync(path.join(highResDir, 'marks', 'mark-0.json'))).toBe(true)
96
+ })
97
+
98
+ it('should reject creating a chain in an existing directory', async function () {
99
+ await expect(
100
+ cli.newChain({
101
+ path: chainDir,
102
+ comment: 'Duplicate',
103
+ date: new Date(Date.UTC(2025, 0, 1)),
104
+ })
105
+ ).rejects.toThrow('already exists')
106
+ })
107
+ })
108
+
109
+ // ──────────────────────────────────────────────────────────────────
110
+ // 2. Add marks (provenance next)
111
+ // ──────────────────────────────────────────────────────────────────
112
+
113
+ describe('2. Next mark', function () {
114
+
115
+ it('should add a second mark to the chain', async function () {
116
+ const output = await cli.nextMark({
117
+ path: chainDir,
118
+ comment: 'Release v1.0',
119
+ date: new Date(Date.UTC(2025, 0, 2)),
120
+ quiet: true,
121
+ })
122
+
123
+ expect(typeof output).toBe('string')
124
+ expect(output.length).toBeGreaterThan(0)
125
+ expect(fs.existsSync(path.join(chainDir, 'marks', 'mark-1.json'))).toBe(true)
126
+ })
127
+
128
+ it('should add a third mark to the chain', async function () {
129
+ const output = await cli.nextMark({
130
+ path: chainDir,
131
+ comment: 'Release v2.0',
132
+ date: new Date(Date.UTC(2025, 0, 3)),
133
+ quiet: true,
134
+ })
135
+
136
+ expect(output.length).toBeGreaterThan(0)
137
+ expect(fs.existsSync(path.join(chainDir, 'marks', 'mark-2.json'))).toBe(true)
138
+ })
139
+
140
+ it('should add a fourth mark to the chain', async function () {
141
+ await cli.nextMark({
142
+ path: chainDir,
143
+ comment: 'Release v3.0',
144
+ date: new Date(Date.UTC(2025, 0, 4)),
145
+ quiet: true,
146
+ })
147
+
148
+ expect(fs.existsSync(path.join(chainDir, 'marks', 'mark-3.json'))).toBe(true)
149
+ })
150
+ })
151
+
152
+ // ──────────────────────────────────────────────────────────────────
153
+ // 3. Output formats (provenance next --format)
154
+ // ──────────────────────────────────────────────────────────────────
155
+
156
+ describe('3. Output formats', function () {
157
+
158
+ it('should output in markdown format', async function () {
159
+ const formatDir = path.join(WORK_DIR, 'format-chain')
160
+ await cli.newChain({
161
+ path: formatDir,
162
+ comment: 'Format test',
163
+ date: new Date(Date.UTC(2025, 0, 1)),
164
+ format: 'markdown',
165
+ quiet: true,
166
+ })
167
+
168
+ // Markdown output contains the bytewords line with 🅟
169
+ expect(true).toBe(true) // chain created successfully
170
+ })
171
+
172
+ it('should output in UR format', async function () {
173
+ const urDir = path.join(WORK_DIR, 'ur-chain')
174
+ const output = await cli.newChain({
175
+ path: urDir,
176
+ comment: 'UR test',
177
+ date: new Date(Date.UTC(2025, 0, 1)),
178
+ format: 'ur',
179
+ quiet: true,
180
+ })
181
+
182
+ expect(output.startsWith('ur:provenance/')).toBe(true)
183
+ })
184
+
185
+ it('should output in JSON format', async function () {
186
+ const jsonDir = path.join(WORK_DIR, 'json-chain')
187
+ const output = await cli.newChain({
188
+ path: jsonDir,
189
+ comment: 'JSON test',
190
+ date: new Date(Date.UTC(2025, 0, 1)),
191
+ format: 'json',
192
+ quiet: true,
193
+ })
194
+
195
+ const parsed = JSON.parse(output)
196
+ expect(parsed).toBeDefined()
197
+ expect(parsed.comment).toBe('JSON test')
198
+ })
199
+
200
+ it('should output next mark in UR format', async function () {
201
+ const urDir = path.join(WORK_DIR, 'ur-chain')
202
+ const output = await cli.nextMark({
203
+ path: urDir,
204
+ comment: 'UR next',
205
+ date: new Date(Date.UTC(2025, 0, 2)),
206
+ format: 'ur',
207
+ quiet: true,
208
+ })
209
+
210
+ expect(output.startsWith('ur:provenance/')).toBe(true)
211
+ })
212
+
213
+ it('should output next mark in JSON format', async function () {
214
+ const jsonDir = path.join(WORK_DIR, 'json-chain')
215
+ const output = await cli.nextMark({
216
+ path: jsonDir,
217
+ comment: 'JSON next',
218
+ date: new Date(Date.UTC(2025, 0, 2)),
219
+ format: 'json',
220
+ quiet: true,
221
+ })
222
+
223
+ const parsed = JSON.parse(output)
224
+ expect(parsed.comment).toBe('JSON next')
225
+ })
226
+ })
227
+
228
+ // ──────────────────────────────────────────────────────────────────
229
+ // 4. Print marks (provenance print)
230
+ // ──────────────────────────────────────────────────────────────────
231
+
232
+ describe('4. Print marks', function () {
233
+
234
+ it('should print all marks in the chain', async function () {
235
+ const output = await cli.print({
236
+ path: chainDir,
237
+ })
238
+
239
+ expect(typeof output).toBe('string')
240
+ expect(output.length).toBeGreaterThan(0)
241
+ })
242
+
243
+ it('should print a specific range of marks', async function () {
244
+ const output = await cli.print({
245
+ path: chainDir,
246
+ start: 0,
247
+ end: 1,
248
+ })
249
+
250
+ expect(output.length).toBeGreaterThan(0)
251
+ })
252
+
253
+ it('should print only the genesis mark', async function () {
254
+ const output = await cli.print({
255
+ path: chainDir,
256
+ start: 0,
257
+ end: 0,
258
+ })
259
+
260
+ expect(output.length).toBeGreaterThan(0)
261
+ })
262
+
263
+ it('should print marks in JSON format', async function () {
264
+ const output = await cli.print({
265
+ path: chainDir,
266
+ format: 'json',
267
+ })
268
+
269
+ const parsed = JSON.parse(output)
270
+ expect(Array.isArray(parsed)).toBe(true)
271
+ expect(parsed.length).toBe(4)
272
+ })
273
+
274
+ it('should print marks in UR format', async function () {
275
+ const output = await cli.print({
276
+ path: chainDir,
277
+ format: 'ur',
278
+ })
279
+
280
+ const lines = output.split('\n').filter((l: string) => l.trim() !== '')
281
+ expect(lines.length).toBe(4)
282
+ for (const line of lines) {
283
+ expect(line.startsWith('ur:provenance/')).toBe(true)
284
+ }
285
+ })
286
+ })
287
+
288
+ // ──────────────────────────────────────────────────────────────────
289
+ // 5. Validate chain (provenance validate)
290
+ // ──────────────────────────────────────────────────────────────────
291
+
292
+ describe('5. Validate chain', function () {
293
+
294
+ it('should validate a valid chain directory', async function () {
295
+ const output = await cli.validate({
296
+ dir: chainDir,
297
+ format: 'json-pretty',
298
+ })
299
+
300
+ const parsed = JSON.parse(output)
301
+ expect(parsed).toBeDefined()
302
+ expect(parsed.chains).toBeDefined()
303
+ })
304
+
305
+ it('should validate with text format (empty for valid chain)', async function () {
306
+ const output = await cli.validate({
307
+ dir: chainDir,
308
+ format: 'text',
309
+ })
310
+
311
+ // A valid chain produces empty text output (no issues)
312
+ expect(typeof output).toBe('string')
313
+ })
314
+
315
+ it('should validate with json-compact format', async function () {
316
+ const output = await cli.validate({
317
+ dir: chainDir,
318
+ format: 'json-compact',
319
+ })
320
+
321
+ const parsed = JSON.parse(output)
322
+ expect(parsed).toBeDefined()
323
+ })
324
+ })
325
+
326
+ // ──────────────────────────────────────────────────────────────────
327
+ // 6. Resolution levels
328
+ // ──────────────────────────────────────────────────────────────────
329
+
330
+ describe('6. Resolution levels', function () {
331
+
332
+ for (const resolution of ['low', 'medium', 'quartile', 'high'] as const) {
333
+ it(`should create a chain with ${resolution} resolution`, async function () {
334
+ const resDir = path.join(WORK_DIR, `res-${resolution}`)
335
+ const output = await cli.newChain({
336
+ path: resDir,
337
+ resolution,
338
+ comment: `${resolution} resolution`,
339
+ date: new Date(Date.UTC(2025, 0, 1)),
340
+ format: 'json',
341
+ quiet: true,
342
+ })
343
+
344
+ const parsed = JSON.parse(output)
345
+ expect(parsed).toBeDefined()
346
+ expect(parsed.comment).toBe(`${resolution} resolution`)
347
+ })
348
+ }
349
+ })
350
+
351
+ // ──────────────────────────────────────────────────────────────────
352
+ // 7. Mark JSON structure
353
+ // ──────────────────────────────────────────────────────────────────
354
+
355
+ describe('7. Mark JSON structure', function () {
356
+
357
+ it('should have expected fields in mark JSON', async function () {
358
+ const output = await cli.print({
359
+ path: chainDir,
360
+ start: 0,
361
+ end: 0,
362
+ format: 'json',
363
+ })
364
+
365
+ const parsed = JSON.parse(output)
366
+ expect(Array.isArray(parsed)).toBe(true)
367
+ const mark = parsed[0]
368
+
369
+ expect(mark.comment).toBe('Genesis mark for tests')
370
+ expect(mark.mark).toBeDefined()
371
+ })
372
+
373
+ it('should preserve comments across marks', async function () {
374
+ const output = await cli.print({
375
+ path: chainDir,
376
+ format: 'json',
377
+ })
378
+
379
+ const parsed = JSON.parse(output)
380
+ expect(parsed[0].comment).toBe('Genesis mark for tests')
381
+ expect(parsed[1].comment).toBe('Release v1.0')
382
+ expect(parsed[2].comment).toBe('Release v2.0')
383
+ expect(parsed[3].comment).toBe('Release v3.0')
384
+ })
385
+ })
386
+
387
+ })
@@ -0,0 +1,174 @@
1
+
2
+ import {
3
+ NewCommand,
4
+ NextCommand,
5
+ PrintCommand,
6
+ ValidateCommand,
7
+ OutputFormat,
8
+ Resolution,
9
+ ValidateFormat,
10
+ type NewCommandArgs,
11
+ type NextCommandArgs,
12
+ type PrintCommandArgs,
13
+ type ValidateCommandArgs,
14
+ defaultNewCommandArgs,
15
+ defaultNextCommandArgs,
16
+ defaultPrintCommandArgs,
17
+ defaultValidateCommandArgs,
18
+ } from '@bcts/provenance-mark-cli'
19
+
20
+
21
+ export async function capsule({
22
+ encapsulate,
23
+ CapsulePropertyTypes,
24
+ makeImportStack
25
+ }: {
26
+ encapsulate: any
27
+ CapsulePropertyTypes: any
28
+ makeImportStack: any
29
+ }) {
30
+
31
+ return encapsulate({
32
+ '#@stream44.studio/encapsulate/spine-contracts/CapsuleSpineContract.v0': {
33
+ '#@stream44.studio/encapsulate/structs/Capsule': {},
34
+ '#': {
35
+
36
+ newChain: {
37
+ type: CapsulePropertyTypes.Function,
38
+ value: async function (this: any, context: {
39
+ path: string
40
+ resolution?: 'low' | 'medium' | 'quartile' | 'high'
41
+ comment?: string
42
+ date?: Date
43
+ seed?: string
44
+ format?: 'markdown' | 'ur' | 'json'
45
+ quiet?: boolean
46
+ }) {
47
+ const args: NewCommandArgs = {
48
+ ...defaultNewCommandArgs(),
49
+ path: context.path,
50
+ }
51
+ if (context.resolution) {
52
+ args.resolution = context.resolution as Resolution
53
+ }
54
+ if (context.comment !== undefined) {
55
+ args.comment = context.comment
56
+ }
57
+ if (context.date) {
58
+ args.date = context.date
59
+ }
60
+ if (context.format) {
61
+ args.format = context.format as OutputFormat
62
+ }
63
+ if (context.quiet !== undefined) {
64
+ args.quiet = context.quiet
65
+ }
66
+ const cmd = new NewCommand(args)
67
+ return cmd.exec()
68
+ }
69
+ },
70
+
71
+ nextMark: {
72
+ type: CapsulePropertyTypes.Function,
73
+ value: async function (this: any, context: {
74
+ path: string
75
+ comment?: string
76
+ date?: Date
77
+ format?: 'markdown' | 'ur' | 'json'
78
+ quiet?: boolean
79
+ }) {
80
+ const args: NextCommandArgs = {
81
+ ...defaultNextCommandArgs(),
82
+ path: context.path,
83
+ }
84
+ if (context.comment !== undefined) {
85
+ args.comment = context.comment
86
+ }
87
+ if (context.date) {
88
+ args.date = context.date
89
+ }
90
+ if (context.format) {
91
+ args.format = context.format as OutputFormat
92
+ }
93
+ if (context.quiet !== undefined) {
94
+ args.quiet = context.quiet
95
+ }
96
+ const cmd = new NextCommand(args)
97
+ return cmd.exec()
98
+ }
99
+ },
100
+
101
+ print: {
102
+ type: CapsulePropertyTypes.Function,
103
+ value: async function (this: any, context: {
104
+ path: string
105
+ start?: number
106
+ end?: number
107
+ format?: 'markdown' | 'ur' | 'json'
108
+ }) {
109
+ const args: PrintCommandArgs = {
110
+ ...defaultPrintCommandArgs(),
111
+ path: context.path,
112
+ }
113
+ if (context.start !== undefined) {
114
+ args.start = context.start
115
+ }
116
+ if (context.end !== undefined) {
117
+ args.end = context.end
118
+ }
119
+ if (context.format) {
120
+ args.format = context.format as OutputFormat
121
+ }
122
+ const cmd = new PrintCommand(args)
123
+ return cmd.exec()
124
+ }
125
+ },
126
+
127
+ validate: {
128
+ type: CapsulePropertyTypes.Function,
129
+ value: async function (this: any, context: {
130
+ dir?: string
131
+ marks?: string[]
132
+ warn?: boolean
133
+ format?: 'text' | 'json-compact' | 'json-pretty'
134
+ }) {
135
+ const args: ValidateCommandArgs = {
136
+ ...defaultValidateCommandArgs(),
137
+ }
138
+ if (context.dir) {
139
+ args.dir = context.dir
140
+ }
141
+ if (context.marks) {
142
+ args.marks = context.marks
143
+ }
144
+ if (context.warn !== undefined) {
145
+ args.warn = context.warn
146
+ }
147
+ if (context.format) {
148
+ args.format = context.format as ValidateFormat
149
+ }
150
+ const cmd = new ValidateCommand(args)
151
+ return cmd.exec()
152
+ }
153
+ },
154
+
155
+ types: {
156
+ type: CapsulePropertyTypes.Function,
157
+ value: async function (this: any) {
158
+ return {
159
+ OutputFormat,
160
+ Resolution,
161
+ ValidateFormat,
162
+ }
163
+ }
164
+ },
165
+
166
+ }
167
+ }
168
+ }, {
169
+ importMeta: import.meta,
170
+ importStack: makeImportStack(),
171
+ capsuleName: capsule['#'],
172
+ })
173
+ }
174
+ capsule['#'] = 't44/caps/providers/blockchaincommons.com/provenance-mark-cli'