@stream44.studio/t44 0.4.0-rc.24

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 (99) hide show
  1. package/.dco-signatures +9 -0
  2. package/.github/workflows/dco.yaml +12 -0
  3. package/.github/workflows/gordian-open-integrity.yaml +13 -0
  4. package/.github/workflows/test.yaml +31 -0
  5. package/.o/GordianOpenIntegrity-CurrentLifehash.svg +1026 -0
  6. package/.o/GordianOpenIntegrity-InceptionLifehash.svg +1026 -0
  7. package/.o/GordianOpenIntegrity.yaml +21 -0
  8. package/.o/assets/Hero-Terminal44-v0.jpeg +0 -0
  9. package/.o/stream44.studio/assets/Icon-v1.svg +1170 -0
  10. package/.repo-identifier +1 -0
  11. package/DCO.md +34 -0
  12. package/LICENSE.txt +186 -0
  13. package/README.md +189 -0
  14. package/bin/activate +36 -0
  15. package/bin/activate.ts +30 -0
  16. package/bin/postinstall.sh +19 -0
  17. package/bin/shell +27 -0
  18. package/bin/t44 +27 -0
  19. package/caps/ConfigSchemaStruct.ts +55 -0
  20. package/caps/Home.ts +57 -0
  21. package/caps/HomeRegistry.ts +319 -0
  22. package/caps/HomeRegistryFile.ts +144 -0
  23. package/caps/JsonSchemas.ts +220 -0
  24. package/caps/OpenApiSchema.ts +67 -0
  25. package/caps/PackageDescriptor.ts +88 -0
  26. package/caps/ProjectCatalogs.ts +153 -0
  27. package/caps/ProjectDeployment.ts +426 -0
  28. package/caps/ProjectDevelopment.ts +257 -0
  29. package/caps/ProjectPublishing.ts +654 -0
  30. package/caps/ProjectPulling.ts +234 -0
  31. package/caps/ProjectRack.ts +155 -0
  32. package/caps/ProjectRepository.ts +332 -0
  33. package/caps/ProjectTest.ts +251 -0
  34. package/caps/ProjectTestLib.ts +257 -0
  35. package/caps/RootKey.ts +219 -0
  36. package/caps/SigningKey.ts +243 -0
  37. package/caps/TaskWorkflow.ts +192 -0
  38. package/caps/WorkspaceCli.ts +448 -0
  39. package/caps/WorkspaceConfig.ts +268 -0
  40. package/caps/WorkspaceConfig.yaml +87 -0
  41. package/caps/WorkspaceConfigFile.ts +902 -0
  42. package/caps/WorkspaceConnection.ts +329 -0
  43. package/caps/WorkspaceEntityConfig.ts +78 -0
  44. package/caps/WorkspaceEntityConfig.v0.ts +77 -0
  45. package/caps/WorkspaceEntityFact.ts +218 -0
  46. package/caps/WorkspaceInfo.ts +619 -0
  47. package/caps/WorkspaceInit.ts +30 -0
  48. package/caps/WorkspaceKey.ts +338 -0
  49. package/caps/WorkspaceModel.ts +373 -0
  50. package/caps/WorkspaceProjects.ts +636 -0
  51. package/caps/WorkspacePrompt.ts +430 -0
  52. package/caps/WorkspaceShell.sh +39 -0
  53. package/caps/WorkspaceShell.ts +104 -0
  54. package/caps/WorkspaceShell.yaml +64 -0
  55. package/caps/WorkspaceShellCli.ts +109 -0
  56. package/caps/patterns/README.md +2 -0
  57. package/caps/patterns/git-scm.com/ProjectPublishing.ts +507 -0
  58. package/caps/patterns/semver.org/ProjectPublishing.ts +458 -0
  59. package/docs/Overview.drawio +248 -0
  60. package/docs/Overview.svg +4 -0
  61. package/examples/01-Lifecycle/main.test.ts +223 -0
  62. package/lib/crypto.ts +53 -0
  63. package/lib/key.ts +381 -0
  64. package/lib/schema-console-renderer.ts +181 -0
  65. package/lib/schema-resolver.ts +349 -0
  66. package/lib/ucan.ts +137 -0
  67. package/package.json +91 -0
  68. package/standalone-rt.test.ts +150 -0
  69. package/standalone-rt.ts +140 -0
  70. package/structs/HomeRegistry.ts +55 -0
  71. package/structs/HomeRegistryConfig.ts +60 -0
  72. package/structs/ProjectCatalogsConfig.ts +53 -0
  73. package/structs/ProjectDeploymentConfig.ts +56 -0
  74. package/structs/ProjectDeploymentFact.ts +106 -0
  75. package/structs/ProjectPublishingConfig.ts +78 -0
  76. package/structs/ProjectPublishingFact.ts +68 -0
  77. package/structs/ProjectPullingConfig.ts +52 -0
  78. package/structs/ProjectRack.ts +51 -0
  79. package/structs/ProjectRackConfig.ts +56 -0
  80. package/structs/RepositoryOriginDescriptor.ts +51 -0
  81. package/structs/RootKeyConfig.ts +64 -0
  82. package/structs/SigningKeyConfig.ts +64 -0
  83. package/structs/Workspace.ts +56 -0
  84. package/structs/WorkspaceCatalogs.ts +56 -0
  85. package/structs/WorkspaceCliConfig.ts +53 -0
  86. package/structs/WorkspaceConfig.ts +64 -0
  87. package/structs/WorkspaceConfigFile.ts +50 -0
  88. package/structs/WorkspaceConfigFileMeta.ts +70 -0
  89. package/structs/WorkspaceKey.ts +55 -0
  90. package/structs/WorkspaceKeyConfig.ts +56 -0
  91. package/structs/WorkspaceMappingsConfig.ts +56 -0
  92. package/structs/WorkspaceProject.ts +104 -0
  93. package/structs/WorkspaceProjectsConfig.ts +67 -0
  94. package/structs/WorkspaceShellConfig.ts +83 -0
  95. package/structs/patterns/README.md +2 -0
  96. package/structs/patterns/git-scm.com/ProjectPublishingFact.ts +46 -0
  97. package/tsconfig.json +33 -0
  98. package/workspace-rt.ts +152 -0
  99. package/workspace.yaml +3 -0
@@ -0,0 +1,218 @@
1
+
2
+ import { join } from 'path'
3
+ import { mkdir, writeFile, readFile, stat } from 'fs/promises'
4
+
5
+
6
+ export async function capsule({
7
+ encapsulate,
8
+ CapsulePropertyTypes,
9
+ makeImportStack
10
+ }: {
11
+ encapsulate: any
12
+ CapsulePropertyTypes: any
13
+ makeImportStack: any
14
+ }) {
15
+ return encapsulate({
16
+ '#@stream44.studio/encapsulate/spine-contracts/CapsuleSpineContract.v0': {
17
+ '#@stream44.studio/encapsulate/structs/Capsule': {},
18
+ '#': {
19
+ WorkspaceConfig: {
20
+ type: CapsulePropertyTypes.Mapping,
21
+ value: '@stream44.studio/t44/caps/WorkspaceConfig'
22
+ },
23
+ JsonSchema: {
24
+ type: CapsulePropertyTypes.Mapping,
25
+ value: '@stream44.studio/t44/caps/JsonSchemas'
26
+ },
27
+ RegisterSchemas: {
28
+ type: CapsulePropertyTypes.StructInit,
29
+ value: async function (this: any): Promise<void> {
30
+ if (this.schema?.schema) {
31
+ const version = this.schemaMinorVersion || '0'
32
+ await this.JsonSchema.registerSchema(this.capsuleName, this.schema.schema, version)
33
+ }
34
+ }
35
+ },
36
+ getFilepath: {
37
+ type: CapsulePropertyTypes.Function,
38
+ value: function (this: any, instanceName: string): string {
39
+ return join(
40
+ this.WorkspaceConfig.workspaceRootDir,
41
+ this.getRelativeFilepath(instanceName)
42
+ )
43
+ }
44
+ },
45
+ getRelativeFilepath: {
46
+ type: CapsulePropertyTypes.Function,
47
+ value: function (this: any, instanceName: string): string {
48
+ return join(
49
+ '.~o',
50
+ 'workspace.foundation',
51
+ capsule['#'].replace(/\//g, '~'),
52
+ this.capsuleName.replace(/\//g, '~'),
53
+ instanceName + '.json'
54
+ )
55
+ }
56
+ },
57
+ get: {
58
+ type: CapsulePropertyTypes.Function,
59
+ value: async function (this: any, instanceName: string, rawFilepaths?: string[]): Promise<{ data: any; stale: boolean } | null> {
60
+ const factFilepath = this.getFilepath(instanceName)
61
+
62
+ try {
63
+ const factStat = await stat(factFilepath)
64
+ const factMtime = factStat.mtimeMs
65
+
66
+ // Check if any raw filepaths are newer than our cached fact
67
+ let stale = false
68
+ if (rawFilepaths && rawFilepaths.length > 0) {
69
+ for (const rawPath of rawFilepaths) {
70
+ const fullRawPath = rawPath.startsWith('/')
71
+ ? rawPath
72
+ : join(this.WorkspaceConfig.workspaceRootDir, rawPath)
73
+ try {
74
+ const rawStat = await stat(fullRawPath)
75
+ if (rawStat.mtimeMs > factMtime) {
76
+ stale = true
77
+ break
78
+ }
79
+ } catch {
80
+ // Raw file doesn't exist, consider stale
81
+ stale = true
82
+ break
83
+ }
84
+ }
85
+ }
86
+
87
+ const content = await readFile(factFilepath, 'utf-8')
88
+ const parsed = JSON.parse(content)
89
+ const { $schema, $id, _ValidationFeedback, ...data } = parsed
90
+
91
+ return { data, stale }
92
+ } catch {
93
+ return null
94
+ }
95
+ }
96
+ },
97
+ set: {
98
+ type: CapsulePropertyTypes.Function,
99
+ value: async function (this: any, instanceName: string, data: any): Promise<void> {
100
+
101
+ const factDir = join(
102
+ this.WorkspaceConfig.workspaceRootDir,
103
+ '.~o',
104
+ 'workspace.foundation',
105
+ capsule['#'].replace(/\//g, '~'),
106
+ this.capsuleName.replace(/\//g, '~')
107
+ )
108
+
109
+ await mkdir(factDir, { recursive: true })
110
+
111
+ const factFilepath = join(factDir, instanceName + '.json')
112
+
113
+ // Check if schema defines updatedAt or createdAt
114
+ const schemaProperties = this.schema?.schema?.properties || {}
115
+ const hasUpdatedAt = 'updatedAt' in schemaProperties
116
+ const hasCreatedAt = 'createdAt' in schemaProperties
117
+
118
+ // Read existing data to check for changes and preserve timestamps
119
+ let existingData: any = null
120
+ try {
121
+ const existingContent = await readFile(factFilepath, 'utf-8')
122
+ const existingParsed = JSON.parse(existingContent)
123
+ const { $schema, $id, _ValidationFeedback, ...existing } = existingParsed
124
+ existingData = existing
125
+ } catch {
126
+ // File doesn't exist or can't be read
127
+ }
128
+
129
+ // Prepare output data
130
+ const outputData = { ...data }
131
+
132
+ // Track if we're adding missing timestamps
133
+ let addedCreatedAt = false
134
+ let addedUpdatedAt = false
135
+
136
+ // Set createdAt if schema defines it and it's not set
137
+ if (hasCreatedAt && !outputData.createdAt) {
138
+ if (existingData?.createdAt) {
139
+ // Preserve existing createdAt
140
+ outputData.createdAt = existingData.createdAt
141
+ } else {
142
+ // Set new createdAt (file exists but missing timestamp)
143
+ outputData.createdAt = new Date().toISOString()
144
+ addedCreatedAt = true
145
+ }
146
+ }
147
+
148
+ // Set updatedAt if schema defines it
149
+ if (hasUpdatedAt) {
150
+ if (!outputData.updatedAt && existingData?.updatedAt) {
151
+ // Preserve existing updatedAt for comparison
152
+ outputData.updatedAt = existingData.updatedAt
153
+ } else if (!outputData.updatedAt) {
154
+ // Set new updatedAt (file exists but missing timestamp, or new file)
155
+ outputData.updatedAt = new Date().toISOString()
156
+ if (existingData) {
157
+ addedUpdatedAt = true
158
+ }
159
+ }
160
+ }
161
+
162
+ const version = this.schemaMinorVersion || '0'
163
+ const output: Record<string, any> = {
164
+ $schema: 'https://json-schema.org/draft/2020-12/schema',
165
+ $id: this.capsuleName + '.v' + version,
166
+ ...outputData
167
+ }
168
+
169
+ // Check if content has changed
170
+ if (existingData) {
171
+ const existingOutput = {
172
+ $schema: 'https://json-schema.org/draft/2020-12/schema',
173
+ $id: this.capsuleName + '.v' + version,
174
+ ...existingData
175
+ }
176
+ const existingContent = JSON.stringify(existingOutput, null, 4)
177
+ const newContent = JSON.stringify(output, null, 4)
178
+
179
+ if (existingContent === newContent) {
180
+ // Content is identical, skip write
181
+ return
182
+ }
183
+
184
+ // Content changed - update updatedAt if schema defines it and it matches existing
185
+ // But only if we didn't just add it (which means data actually changed)
186
+ if (hasUpdatedAt && !addedUpdatedAt && !addedCreatedAt && outputData.updatedAt === existingData?.updatedAt) {
187
+ output.updatedAt = new Date().toISOString()
188
+ }
189
+ }
190
+
191
+ await writeFile(factFilepath, JSON.stringify(output, null, 4))
192
+ }
193
+ },
194
+ delete: {
195
+ type: CapsulePropertyTypes.Function,
196
+ value: async function (this: any, instanceName: string): Promise<void> {
197
+ const { unlink } = await import('fs/promises')
198
+ const factFilepath = this.getFilepath(instanceName)
199
+
200
+ try {
201
+ await unlink(factFilepath)
202
+ } catch (error: any) {
203
+ // Ignore if file doesn't exist
204
+ if (error.code !== 'ENOENT') {
205
+ throw error
206
+ }
207
+ }
208
+ }
209
+ }
210
+ }
211
+ }
212
+ }, {
213
+ importMeta: import.meta,
214
+ importStack: makeImportStack(),
215
+ capsuleName: capsule['#'],
216
+ })
217
+ }
218
+ capsule['#'] = '@stream44.studio/t44/caps/WorkspaceEntityFact'