@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.
- package/.dco-signatures +9 -0
- package/.github/workflows/dco.yaml +12 -0
- package/.github/workflows/gordian-open-integrity.yaml +13 -0
- package/.github/workflows/test.yaml +31 -0
- package/.o/GordianOpenIntegrity-CurrentLifehash.svg +1026 -0
- package/.o/GordianOpenIntegrity-InceptionLifehash.svg +1026 -0
- package/.o/GordianOpenIntegrity.yaml +21 -0
- package/.o/assets/Hero-Terminal44-v0.jpeg +0 -0
- package/.o/stream44.studio/assets/Icon-v1.svg +1170 -0
- package/.repo-identifier +1 -0
- package/DCO.md +34 -0
- package/LICENSE.txt +186 -0
- package/README.md +189 -0
- package/bin/activate +36 -0
- package/bin/activate.ts +30 -0
- package/bin/postinstall.sh +19 -0
- package/bin/shell +27 -0
- package/bin/t44 +27 -0
- package/caps/ConfigSchemaStruct.ts +55 -0
- package/caps/Home.ts +57 -0
- package/caps/HomeRegistry.ts +319 -0
- package/caps/HomeRegistryFile.ts +144 -0
- package/caps/JsonSchemas.ts +220 -0
- package/caps/OpenApiSchema.ts +67 -0
- package/caps/PackageDescriptor.ts +88 -0
- package/caps/ProjectCatalogs.ts +153 -0
- package/caps/ProjectDeployment.ts +426 -0
- package/caps/ProjectDevelopment.ts +257 -0
- package/caps/ProjectPublishing.ts +654 -0
- package/caps/ProjectPulling.ts +234 -0
- package/caps/ProjectRack.ts +155 -0
- package/caps/ProjectRepository.ts +332 -0
- package/caps/ProjectTest.ts +251 -0
- package/caps/ProjectTestLib.ts +257 -0
- package/caps/RootKey.ts +219 -0
- package/caps/SigningKey.ts +243 -0
- package/caps/TaskWorkflow.ts +192 -0
- package/caps/WorkspaceCli.ts +448 -0
- package/caps/WorkspaceConfig.ts +268 -0
- package/caps/WorkspaceConfig.yaml +87 -0
- package/caps/WorkspaceConfigFile.ts +902 -0
- package/caps/WorkspaceConnection.ts +329 -0
- package/caps/WorkspaceEntityConfig.ts +78 -0
- package/caps/WorkspaceEntityConfig.v0.ts +77 -0
- package/caps/WorkspaceEntityFact.ts +218 -0
- package/caps/WorkspaceInfo.ts +619 -0
- package/caps/WorkspaceInit.ts +30 -0
- package/caps/WorkspaceKey.ts +338 -0
- package/caps/WorkspaceModel.ts +373 -0
- package/caps/WorkspaceProjects.ts +636 -0
- package/caps/WorkspacePrompt.ts +430 -0
- package/caps/WorkspaceShell.sh +39 -0
- package/caps/WorkspaceShell.ts +104 -0
- package/caps/WorkspaceShell.yaml +64 -0
- package/caps/WorkspaceShellCli.ts +109 -0
- package/caps/patterns/README.md +2 -0
- package/caps/patterns/git-scm.com/ProjectPublishing.ts +507 -0
- package/caps/patterns/semver.org/ProjectPublishing.ts +458 -0
- package/docs/Overview.drawio +248 -0
- package/docs/Overview.svg +4 -0
- package/examples/01-Lifecycle/main.test.ts +223 -0
- package/lib/crypto.ts +53 -0
- package/lib/key.ts +381 -0
- package/lib/schema-console-renderer.ts +181 -0
- package/lib/schema-resolver.ts +349 -0
- package/lib/ucan.ts +137 -0
- package/package.json +91 -0
- package/standalone-rt.test.ts +150 -0
- package/standalone-rt.ts +140 -0
- package/structs/HomeRegistry.ts +55 -0
- package/structs/HomeRegistryConfig.ts +60 -0
- package/structs/ProjectCatalogsConfig.ts +53 -0
- package/structs/ProjectDeploymentConfig.ts +56 -0
- package/structs/ProjectDeploymentFact.ts +106 -0
- package/structs/ProjectPublishingConfig.ts +78 -0
- package/structs/ProjectPublishingFact.ts +68 -0
- package/structs/ProjectPullingConfig.ts +52 -0
- package/structs/ProjectRack.ts +51 -0
- package/structs/ProjectRackConfig.ts +56 -0
- package/structs/RepositoryOriginDescriptor.ts +51 -0
- package/structs/RootKeyConfig.ts +64 -0
- package/structs/SigningKeyConfig.ts +64 -0
- package/structs/Workspace.ts +56 -0
- package/structs/WorkspaceCatalogs.ts +56 -0
- package/structs/WorkspaceCliConfig.ts +53 -0
- package/structs/WorkspaceConfig.ts +64 -0
- package/structs/WorkspaceConfigFile.ts +50 -0
- package/structs/WorkspaceConfigFileMeta.ts +70 -0
- package/structs/WorkspaceKey.ts +55 -0
- package/structs/WorkspaceKeyConfig.ts +56 -0
- package/structs/WorkspaceMappingsConfig.ts +56 -0
- package/structs/WorkspaceProject.ts +104 -0
- package/structs/WorkspaceProjectsConfig.ts +67 -0
- package/structs/WorkspaceShellConfig.ts +83 -0
- package/structs/patterns/README.md +2 -0
- package/structs/patterns/git-scm.com/ProjectPublishingFact.ts +46 -0
- package/tsconfig.json +33 -0
- package/workspace-rt.ts +152 -0
- package/workspace.yaml +3 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { join } from 'node:path'
|
|
2
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises'
|
|
3
|
+
|
|
4
|
+
export async function capsule({
|
|
5
|
+
encapsulate,
|
|
6
|
+
CapsulePropertyTypes,
|
|
7
|
+
makeImportStack
|
|
8
|
+
}: {
|
|
9
|
+
encapsulate: any
|
|
10
|
+
CapsulePropertyTypes: any
|
|
11
|
+
makeImportStack: any
|
|
12
|
+
}) {
|
|
13
|
+
return encapsulate({
|
|
14
|
+
'#@stream44.studio/encapsulate/spine-contracts/CapsuleSpineContract.v0': {
|
|
15
|
+
'#@stream44.studio/encapsulate/structs/Capsule': {},
|
|
16
|
+
'#': {
|
|
17
|
+
WorkspaceConfig: {
|
|
18
|
+
type: CapsulePropertyTypes.Mapping,
|
|
19
|
+
value: '@stream44.studio/t44/caps/WorkspaceConfig'
|
|
20
|
+
},
|
|
21
|
+
url: {
|
|
22
|
+
type: CapsulePropertyTypes.Literal,
|
|
23
|
+
value: undefined,
|
|
24
|
+
},
|
|
25
|
+
schemas: {
|
|
26
|
+
type: CapsulePropertyTypes.Literal,
|
|
27
|
+
value: {}
|
|
28
|
+
},
|
|
29
|
+
ensureCached: {
|
|
30
|
+
type: CapsulePropertyTypes.Function,
|
|
31
|
+
value: async function (this: any): Promise<any | null> {
|
|
32
|
+
if (!this.url) return null
|
|
33
|
+
|
|
34
|
+
const cacheDir = join(
|
|
35
|
+
this.WorkspaceConfig.workspaceRootDir,
|
|
36
|
+
'.~o',
|
|
37
|
+
'workspace.foundation',
|
|
38
|
+
'OpenApiSchemas',
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
await mkdir(cacheDir, { recursive: true })
|
|
42
|
+
|
|
43
|
+
const schemaFile = join(cacheDir, this.url.replace(/\//g, '~'))
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
const cached = await readFile(schemaFile, 'utf-8')
|
|
47
|
+
return JSON.parse(cached)
|
|
48
|
+
} catch (error) {
|
|
49
|
+
const response = await fetch(this.url)
|
|
50
|
+
if (!response.ok) {
|
|
51
|
+
return null
|
|
52
|
+
}
|
|
53
|
+
const openApiSpec = await response.json()
|
|
54
|
+
await writeFile(schemaFile, JSON.stringify(openApiSpec, null, 2))
|
|
55
|
+
return openApiSpec
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}, {
|
|
62
|
+
importMeta: import.meta,
|
|
63
|
+
importStack: makeImportStack(),
|
|
64
|
+
capsuleName: capsule['#'],
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
capsule['#'] = '@stream44.studio/t44/caps/OpenApiSchema'
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
|
|
2
|
+
import { join } from 'path'
|
|
3
|
+
import { readFile, writeFile } from 'fs/promises'
|
|
4
|
+
|
|
5
|
+
export async function capsule({
|
|
6
|
+
encapsulate,
|
|
7
|
+
CapsulePropertyTypes,
|
|
8
|
+
makeImportStack
|
|
9
|
+
}: {
|
|
10
|
+
encapsulate: any
|
|
11
|
+
CapsulePropertyTypes: any
|
|
12
|
+
makeImportStack: any
|
|
13
|
+
}) {
|
|
14
|
+
return encapsulate({
|
|
15
|
+
'#@stream44.studio/encapsulate/spine-contracts/CapsuleSpineContract.v0': {
|
|
16
|
+
'#@stream44.studio/encapsulate/structs/Capsule': {},
|
|
17
|
+
'#': {
|
|
18
|
+
JsonSchema: {
|
|
19
|
+
type: CapsulePropertyTypes.Mapping,
|
|
20
|
+
value: '@stream44.studio/t44/caps/JsonSchemas'
|
|
21
|
+
},
|
|
22
|
+
RegisterSchemas: {
|
|
23
|
+
type: CapsulePropertyTypes.StructInit,
|
|
24
|
+
value: async function (this: any): Promise<void> {
|
|
25
|
+
if (this.schema?.schema) {
|
|
26
|
+
const version = this.schemaMinorVersion || '0'
|
|
27
|
+
await this.JsonSchema.registerSchema(this.capsuleName, this.schema.schema, version)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
_readPackageJson: {
|
|
32
|
+
type: CapsulePropertyTypes.Function,
|
|
33
|
+
value: async function (this: any, packageJsonPath: string): Promise<any> {
|
|
34
|
+
const content = await readFile(packageJsonPath, 'utf-8')
|
|
35
|
+
return JSON.parse(content)
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
_writePackageJson: {
|
|
39
|
+
type: CapsulePropertyTypes.Function,
|
|
40
|
+
value: async function (this: any, packageJsonPath: string, pkg: any): Promise<void> {
|
|
41
|
+
await writeFile(packageJsonPath, JSON.stringify(pkg, null, 4) + '\n')
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
_resolveConfigSection: {
|
|
45
|
+
type: CapsulePropertyTypes.Function,
|
|
46
|
+
value: function (this: any, pkg: any): any {
|
|
47
|
+
const structKey = this.capsuleName
|
|
48
|
+
if (!pkg.config) return undefined
|
|
49
|
+
if (!pkg.config.o) return undefined
|
|
50
|
+
if (!pkg.config.o['workspace.foundation']) return undefined
|
|
51
|
+
return pkg.config.o['workspace.foundation'][structKey] || undefined
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
get: {
|
|
55
|
+
type: CapsulePropertyTypes.Function,
|
|
56
|
+
value: async function (this: any, packageJsonPath: string): Promise<any | null> {
|
|
57
|
+
try {
|
|
58
|
+
const pkg = await this._readPackageJson(packageJsonPath)
|
|
59
|
+
return this._resolveConfigSection(pkg) || null
|
|
60
|
+
} catch {
|
|
61
|
+
return null
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
set: {
|
|
66
|
+
type: CapsulePropertyTypes.Function,
|
|
67
|
+
value: async function (this: any, packageJsonPath: string, data: any): Promise<void> {
|
|
68
|
+
const pkg = await this._readPackageJson(packageJsonPath)
|
|
69
|
+
const structKey = this.capsuleName
|
|
70
|
+
|
|
71
|
+
if (!pkg.config) pkg.config = {}
|
|
72
|
+
if (!pkg.config.o) pkg.config.o = {}
|
|
73
|
+
if (!pkg.config.o['workspace.foundation']) pkg.config.o['workspace.foundation'] = {}
|
|
74
|
+
|
|
75
|
+
pkg.config.o['workspace.foundation'][structKey] = data
|
|
76
|
+
|
|
77
|
+
await this._writePackageJson(packageJsonPath, pkg)
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}, {
|
|
83
|
+
importMeta: import.meta,
|
|
84
|
+
importStack: makeImportStack(),
|
|
85
|
+
capsuleName: capsule['#'],
|
|
86
|
+
})
|
|
87
|
+
}
|
|
88
|
+
capsule['#'] = '@stream44.studio/t44/caps/PackageDescriptor'
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
|
|
2
|
+
export async function capsule({
|
|
3
|
+
encapsulate,
|
|
4
|
+
CapsulePropertyTypes,
|
|
5
|
+
makeImportStack
|
|
6
|
+
}: {
|
|
7
|
+
encapsulate: any
|
|
8
|
+
CapsulePropertyTypes: any
|
|
9
|
+
makeImportStack: any
|
|
10
|
+
}) {
|
|
11
|
+
return encapsulate({
|
|
12
|
+
'#@stream44.studio/encapsulate/spine-contracts/CapsuleSpineContract.v0': {
|
|
13
|
+
'#@stream44.studio/encapsulate/structs/Capsule': {},
|
|
14
|
+
'#@stream44.studio/t44/structs/ProjectCatalogsConfig': {
|
|
15
|
+
as: '$ProjectCatalogsConfig',
|
|
16
|
+
},
|
|
17
|
+
'#': {
|
|
18
|
+
HomeRegistry: {
|
|
19
|
+
type: CapsulePropertyTypes.Mapping,
|
|
20
|
+
value: '@stream44.studio/t44/caps/HomeRegistry'
|
|
21
|
+
},
|
|
22
|
+
list: {
|
|
23
|
+
type: CapsulePropertyTypes.GetterFunction,
|
|
24
|
+
value: async function (this: any): Promise<Record<string, any>> {
|
|
25
|
+
const catalogsConfig = await this.$ProjectCatalogsConfig.config
|
|
26
|
+
return catalogsConfig?.catalogs || {}
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
validate: {
|
|
30
|
+
type: CapsulePropertyTypes.Function,
|
|
31
|
+
value: async function (this: any): Promise<boolean> {
|
|
32
|
+
const catalogsConfig = await this.$ProjectCatalogsConfig.config
|
|
33
|
+
const catalogs = catalogsConfig?.catalogs
|
|
34
|
+
|
|
35
|
+
if (!catalogs || typeof catalogs !== 'object') {
|
|
36
|
+
return true
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const chalk = (await import('chalk')).default
|
|
40
|
+
|
|
41
|
+
for (const [catalogName, catalogConfig] of Object.entries(catalogs)) {
|
|
42
|
+
if (!catalogConfig || typeof catalogConfig !== 'object') {
|
|
43
|
+
console.log(chalk.red(`\nā Invalid catalog '${catalogName}': must be an object.\n`))
|
|
44
|
+
return false
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const fileName = catalogName.replace(/\//g, '~')
|
|
48
|
+
const typedConfig = catalogConfig as Record<string, any>
|
|
49
|
+
|
|
50
|
+
const existing = await this.HomeRegistry.getCatalog(fileName) || {}
|
|
51
|
+
const now = new Date().toISOString()
|
|
52
|
+
|
|
53
|
+
// Deep merge: config keys into existing, preserving existing nested data
|
|
54
|
+
let changed = false
|
|
55
|
+
for (const [key, value] of Object.entries(typedConfig)) {
|
|
56
|
+
if (key === 'repositories' && typeof value === 'object' && typeof existing[key] === 'object') {
|
|
57
|
+
// Merge repositories: add missing repos, keep existing repo data
|
|
58
|
+
for (const [repoName, repoConfig] of Object.entries(value as Record<string, any>)) {
|
|
59
|
+
if (!existing[key][repoName]) {
|
|
60
|
+
existing[key][repoName] = repoConfig || {}
|
|
61
|
+
changed = true
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
} else if (JSON.stringify(existing[key]) !== JSON.stringify(value)) {
|
|
65
|
+
existing[key] = value
|
|
66
|
+
changed = true
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (changed) {
|
|
71
|
+
existing.updatedAt = now
|
|
72
|
+
}
|
|
73
|
+
if (!existing.createdAt) {
|
|
74
|
+
existing.createdAt = now
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const filePath = await this.HomeRegistry.setCatalog(fileName, existing)
|
|
78
|
+
if (changed) {
|
|
79
|
+
console.log(chalk.green(` ā Catalog '${catalogName}' ā ${filePath}`))
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return true
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
updateCatalogRepository: {
|
|
87
|
+
type: CapsulePropertyTypes.Function,
|
|
88
|
+
value: async function (this: any, {
|
|
89
|
+
repoName,
|
|
90
|
+
providerKey,
|
|
91
|
+
providerData,
|
|
92
|
+
}: {
|
|
93
|
+
repoName: string
|
|
94
|
+
providerKey: string
|
|
95
|
+
providerData: Record<string, any>
|
|
96
|
+
}): Promise<void> {
|
|
97
|
+
const catalogsConfig = await this.$ProjectCatalogsConfig.config
|
|
98
|
+
const catalogs = catalogsConfig?.catalogs
|
|
99
|
+
|
|
100
|
+
if (!catalogs || typeof catalogs !== 'object') return
|
|
101
|
+
|
|
102
|
+
for (const [catalogName, catalogConfig] of Object.entries(catalogs)) {
|
|
103
|
+
const typedConfig = catalogConfig as Record<string, any>
|
|
104
|
+
const repositories = typedConfig.repositories
|
|
105
|
+
if (!repositories || typeof repositories !== 'object') continue
|
|
106
|
+
if (!(repoName in repositories)) continue
|
|
107
|
+
|
|
108
|
+
const fileName = catalogName.replace(/\//g, '~')
|
|
109
|
+
const existing = await this.HomeRegistry.getCatalog(fileName)
|
|
110
|
+
if (!existing) continue
|
|
111
|
+
|
|
112
|
+
if (!existing.repositories) existing.repositories = {}
|
|
113
|
+
if (!existing.repositories[repoName]) existing.repositories[repoName] = {}
|
|
114
|
+
const repoEntry = existing.repositories[repoName]
|
|
115
|
+
const now = new Date().toISOString()
|
|
116
|
+
|
|
117
|
+
const existingEntity = repoEntry[providerKey]
|
|
118
|
+
const entityCreatedAt = existingEntity?.createdAt || now
|
|
119
|
+
|
|
120
|
+
// Check if data actually changed (compare without timestamps, order-independent)
|
|
121
|
+
const { createdAt: _ec, updatedAt: _eu, ...existingData } = existingEntity || {}
|
|
122
|
+
const stableStringify = (obj: any): string => JSON.stringify(obj, (_, v) =>
|
|
123
|
+
v && typeof v === 'object' && !Array.isArray(v)
|
|
124
|
+
? Object.keys(v).sort().reduce((o: any, k) => { o[k] = v[k]; return o }, {})
|
|
125
|
+
: v
|
|
126
|
+
)
|
|
127
|
+
const dataChanged = stableStringify(existingData) !== stableStringify(providerData)
|
|
128
|
+
|
|
129
|
+
repoEntry[providerKey] = {
|
|
130
|
+
...providerData,
|
|
131
|
+
createdAt: entityCreatedAt,
|
|
132
|
+
updatedAt: dataChanged ? now : (existingEntity?.updatedAt || now),
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (!repoEntry.createdAt) repoEntry.createdAt = now
|
|
136
|
+
if (dataChanged) {
|
|
137
|
+
repoEntry.updatedAt = now
|
|
138
|
+
existing.updatedAt = now
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
await this.HomeRegistry.setCatalog(fileName, existing)
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}, {
|
|
148
|
+
importMeta: import.meta,
|
|
149
|
+
importStack: makeImportStack(),
|
|
150
|
+
capsuleName: capsule['#'],
|
|
151
|
+
})
|
|
152
|
+
}
|
|
153
|
+
capsule['#'] = '@stream44.studio/t44/caps/ProjectCatalogs'
|