@xyo-network/os-runtime 3.0.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.
- package/LICENSE +165 -0
- package/README.md +13 -0
- package/dist/neutral/index.d.ts +1656 -0
- package/dist/neutral/index.mjs +3432 -0
- package/dist/neutral/index.mjs.map +1 -0
- package/package.json +78 -0
- package/src/Caller.ts +221 -0
- package/src/DappCallerBase.ts +52 -0
- package/src/DefaultsQueries.ts +27 -0
- package/src/OsCallerBase.ts +55 -0
- package/src/PubSubBridgeCaller.ts +30 -0
- package/src/XyOs.ts +125 -0
- package/src/XyOsBase.ts +119 -0
- package/src/XyOsDapp.ts +90 -0
- package/src/access-interfaces/ValidDappAccessInterfaces.ts +8 -0
- package/src/access-interfaces/index.ts +2 -0
- package/src/access-interfaces/registered-names/helpers/AccessNodeQueries.ts +59 -0
- package/src/access-interfaces/registered-names/helpers/index.ts +2 -0
- package/src/access-interfaces/registered-names/helpers/resource/AbstractXnsCaller.ts +38 -0
- package/src/access-interfaces/registered-names/helpers/resource/RegistrationsResource.ts +54 -0
- package/src/access-interfaces/registered-names/helpers/resource/RegistrationsResourceQueries.ts +22 -0
- package/src/access-interfaces/registered-names/helpers/resource/index.ts +3 -0
- package/src/access-interfaces/registered-names/index.ts +1 -0
- package/src/adapter/Base.ts +96 -0
- package/src/adapter/Network.ts +31 -0
- package/src/adapter/Settings.ts +30 -0
- package/src/adapter/index.ts +2 -0
- package/src/adapters/OsPubSubBridgeNetwork.ts +10 -0
- package/src/adapters/OsSettings.ts +7 -0
- package/src/adapters/OsXyoPublicNetwork.ts +9 -0
- package/src/adapters/index.ts +3 -0
- package/src/classes/cache/RunningAccessDappCache.ts +21 -0
- package/src/classes/cache/RunningDappCache.ts +50 -0
- package/src/classes/cache/index.ts +2 -0
- package/src/classes/dapp/DefaultsResource.ts +65 -0
- package/src/classes/dapp/access/Caller.ts +73 -0
- package/src/classes/dapp/access/Queries.ts +38 -0
- package/src/classes/dapp/access/Resource.ts +63 -0
- package/src/classes/dapp/access/index.ts +3 -0
- package/src/classes/dapp/index.ts +2 -0
- package/src/classes/index.ts +8 -0
- package/src/classes/lib/DappCreatorParams.ts +16 -0
- package/src/classes/lib/Insertable.ts +14 -0
- package/src/classes/lib/index.ts +2 -0
- package/src/classes/menu/Caller.ts +76 -0
- package/src/classes/menu/Queries.ts +59 -0
- package/src/classes/menu/Resource.ts +103 -0
- package/src/classes/menu/index.ts +2 -0
- package/src/classes/node/Creator.ts +96 -0
- package/src/classes/node/DefaultPayloads/DappAccessPayloads.ts +17 -0
- package/src/classes/node/DefaultPayloads/DefaultPayloads.ts +33 -0
- package/src/classes/node/DefaultPayloads/NodeInfoPayload.ts +27 -0
- package/src/classes/node/DefaultPayloads/SigningKeyPayloads.ts +85 -0
- package/src/classes/node/DefaultPayloads/index.ts +1 -0
- package/src/classes/node/ExternalModulePermissions/ExternalModulePermissions.ts +47 -0
- package/src/classes/node/ExternalModulePermissions/index.ts +1 -0
- package/src/classes/node/createDappContext.ts +52 -0
- package/src/classes/node/index.ts +3 -0
- package/src/classes/registration/DappRegistrationService.ts +146 -0
- package/src/classes/registration/DappRegistry.ts +121 -0
- package/src/classes/registration/ValidateDappAccessDiviner/Config.ts +19 -0
- package/src/classes/registration/ValidateDappAccessDiviner/Diviner.ts +132 -0
- package/src/classes/registration/ValidateDappAccessDiviner/index.ts +2 -0
- package/src/classes/registration/index.ts +2 -0
- package/src/classes/settings/Caller.ts +76 -0
- package/src/classes/settings/CallerBase.ts +24 -0
- package/src/classes/settings/Resource.ts +79 -0
- package/src/classes/settings/SettingsQueries.ts +38 -0
- package/src/classes/settings/badge/Caller.ts +30 -0
- package/src/classes/settings/badge/Queries.ts +18 -0
- package/src/classes/settings/badge/Resource.ts +54 -0
- package/src/classes/settings/badge/index.ts +2 -0
- package/src/classes/settings/index.ts +5 -0
- package/src/classes/system/ManageSystemDapps.ts +131 -0
- package/src/classes/system/Queries.ts +69 -0
- package/src/classes/system/index.ts +1 -0
- package/src/event/bus/Connection.ts +31 -0
- package/src/event/bus/EventBus.ts +145 -0
- package/src/event/bus/PubSubConnection.ts +28 -0
- package/src/event/bus/index.ts +3 -0
- package/src/event/connections/DappAccessRequest.ts +11 -0
- package/src/event/connections/DappsReady.ts +9 -0
- package/src/event/connections/ExposeDappRequest.ts +11 -0
- package/src/event/connections/OsPubSubNetworkReady.ts +12 -0
- package/src/event/connections/OsSettingsReady.ts +12 -0
- package/src/event/connections/OsXyoPublicReady.ts +12 -0
- package/src/event/connections/index.ts +6 -0
- package/src/event/index.ts +2 -0
- package/src/helpers/index.ts +1 -0
- package/src/helpers/monitor/XyOsMonitor.ts +52 -0
- package/src/helpers/monitor/index.ts +2 -0
- package/src/helpers/monitor/types.ts +5 -0
- package/src/index.ts +22 -0
- package/src/intent/Caller.ts +72 -0
- package/src/intent/Resource.ts +66 -0
- package/src/intent/index.ts +2 -0
- package/src/lib/ExternalStore.ts +7 -0
- package/src/lib/Listener.ts +1 -0
- package/src/lib/ModuleAccountPaths.ts +29 -0
- package/src/lib/ModuleNames.ts +3 -0
- package/src/lib/NameTransforms.ts +31 -0
- package/src/lib/PayloadStore.ts +98 -0
- package/src/lib/ResourceStores.ts +7 -0
- package/src/lib/index.ts +10 -0
- package/src/lib/initializeXns.ts +16 -0
- package/src/lib/isPayload.ts +24 -0
- package/src/lib/tokenPlacesSplit.ts +17 -0
- package/src/loadOsNode.ts +43 -0
- package/src/manifest/ManifestReplaceableTokens.ts +17 -0
- package/src/manifest/index.ts +2 -0
- package/src/manifest/os-node.manifest.json +132 -0
- package/src/manifests/dapp-window.manifest.json +36 -0
- package/src/manifests/index.ts +1 -0
- package/src/profileModuleEvents.ts +43 -0
- package/src/stack/Base.ts +153 -0
- package/src/stack/Manager.ts +48 -0
- package/src/stack/Map.ts +20 -0
- package/src/stack/OsPubSubNetworkStack.ts +70 -0
- package/src/stack/OsSettingsStack.ts +24 -0
- package/src/stack/XyoPublicNetworkStack.ts +32 -0
- package/src/stack/index.ts +6 -0
- package/src/types/global.d.ts +9 -0
- package/src/types/images.d.ts +5 -0
- package/src/utils/buildWalletSeedPhrasePayload.ts +41 -0
- package/src/utils/getApiDomain.ts +14 -0
- package/src/utils/index.ts +3 -0
- package/src/utils/renameObjKey.ts +22 -0
- package/src/wallet/DappSeedPhraseRepository.ts +95 -0
- package/src/wallet/SeedPhraseRepository.ts +32 -0
- package/src/wallet/index.ts +2 -0
- package/typedoc.json +5 -0
- package/xy.config.ts +10 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import type { Address } from '@xylabs/hex'
|
|
2
|
+
import { HDWallet } from '@xyo-network/account'
|
|
3
|
+
import { AbstractDiviner } from '@xyo-network/diviner-abstract'
|
|
4
|
+
import type { NodeManifestPayload, PackageManifestPayload } from '@xyo-network/manifest'
|
|
5
|
+
import { ManifestWrapper, PackageManifestPayloadSchema } from '@xyo-network/manifest'
|
|
6
|
+
import type { NodeInstance } from '@xyo-network/node-model'
|
|
7
|
+
import type {
|
|
8
|
+
DappPackageManifestPayload,
|
|
9
|
+
DappParams,
|
|
10
|
+
RegisteredDappAccess,
|
|
11
|
+
UnregisteredDappAccess } from '@xyo-network/os-model'
|
|
12
|
+
import {
|
|
13
|
+
isDappPackageManifestPayload,
|
|
14
|
+
isUnregisteredDappAccess,
|
|
15
|
+
RegisteredDappAccessSchema,
|
|
16
|
+
} from '@xyo-network/os-model'
|
|
17
|
+
import { PayloadBuilder } from '@xyo-network/payload-builder'
|
|
18
|
+
import type { WithMeta } from '@xyo-network/payload-model'
|
|
19
|
+
|
|
20
|
+
import { ValidDappAccessInterfaces } from '../../../access-interfaces/index.ts'
|
|
21
|
+
import type {
|
|
22
|
+
FailedAccessor,
|
|
23
|
+
ValidateDappAccessDivinerIn,
|
|
24
|
+
ValidateDappAccessDivinerOut,
|
|
25
|
+
ValidateDappAccessDivinerParams } from './Config.ts'
|
|
26
|
+
import {
|
|
27
|
+
FailedAccessorSchema,
|
|
28
|
+
ValidateDappAccessDivinerConfigSchema,
|
|
29
|
+
} from './Config.ts'
|
|
30
|
+
|
|
31
|
+
// Validate the accessors of a dapp against the dapp access interfaces
|
|
32
|
+
export class ValidateDappAccessDiviner<TParams extends ValidateDappAccessDivinerParams = ValidateDappAccessDivinerParams> extends AbstractDiviner<
|
|
33
|
+
TParams,
|
|
34
|
+
ValidateDappAccessDivinerIn,
|
|
35
|
+
ValidateDappAccessDivinerOut
|
|
36
|
+
> {
|
|
37
|
+
static override configSchemas = [ValidateDappAccessDivinerConfigSchema]
|
|
38
|
+
|
|
39
|
+
protected override async divineHandler(payloads?: ValidateDappAccessDivinerIn[]): Promise<ValidateDappAccessDivinerOut[]> {
|
|
40
|
+
const dappManifest = payloads?.filter(isDappPackageManifestPayload) as DappPackageManifestPayload[]
|
|
41
|
+
const accessors = payloads?.filter(isUnregisteredDappAccess) as UnregisteredDappAccess[]
|
|
42
|
+
const dappParams = this.params.dappParams
|
|
43
|
+
|
|
44
|
+
if (dappManifest.length > 1) {
|
|
45
|
+
throw new Error('Only one dapp manifest payload is allowed')
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (!dappManifest || dappManifest.length === 0 || accessors?.length === 0) {
|
|
49
|
+
return []
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const registeredAccessors: WithMeta<RegisteredDappAccess>[] = []
|
|
53
|
+
const failedAccessors: WithMeta<FailedAccessor>[] = []
|
|
54
|
+
|
|
55
|
+
for (const access of accessors) {
|
|
56
|
+
try {
|
|
57
|
+
if (access) {
|
|
58
|
+
const dappAccessInterface = ValidDappAccessInterfaces[access.name]
|
|
59
|
+
if (!dappAccessInterface) {
|
|
60
|
+
console.warn('Invalid dapp access interface', access.name)
|
|
61
|
+
continue
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const dappManifestToTest = dappPackageManifestToPackageManifest(dappManifest[0])
|
|
65
|
+
const dappChildren = await getNodeChildren(dappManifestToTest, dappParams)
|
|
66
|
+
const interfaceChildren = await getNodeChildren(dappAccessInterface.manifest, dappParams)
|
|
67
|
+
const valid = compareChildren(interfaceChildren, dappChildren)
|
|
68
|
+
if (valid) {
|
|
69
|
+
const payload = await PayloadBuilder.build<RegisteredDappAccess>({
|
|
70
|
+
...access,
|
|
71
|
+
schema: RegisteredDappAccessSchema,
|
|
72
|
+
timestamp: Date.now(),
|
|
73
|
+
})
|
|
74
|
+
registeredAccessors.push(payload)
|
|
75
|
+
} else {
|
|
76
|
+
const failedRegistration = await PayloadBuilder.build<FailedAccessor>({
|
|
77
|
+
accessor: access,
|
|
78
|
+
errorMessage: 'Invalid dapp access interface',
|
|
79
|
+
schema: FailedAccessorSchema,
|
|
80
|
+
})
|
|
81
|
+
failedAccessors.push(failedRegistration)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
} catch (e) {
|
|
85
|
+
failedAccessors.push(
|
|
86
|
+
await PayloadBuilder.build<FailedAccessor>({ accessor: access, errorMessage: (e as Error).message, schema: FailedAccessorSchema }),
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return [...registeredAccessors, ...failedAccessors]
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const compareChildren = (interfaceChildren: Record<Address, string | null>[], dappChildren: Record<Address, string | null>[]) => {
|
|
96
|
+
return interfaceChildren.every(interfaceChild =>
|
|
97
|
+
Object.values(interfaceChild).every(interfaceChildName =>
|
|
98
|
+
dappChildren.some(dappChild => Object.values(dappChild).includes(interfaceChildName)),
|
|
99
|
+
),
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const dappPackageManifestToPackageManifest = (dappPackageManifest: DappPackageManifestPayload): PackageManifestPayload => {
|
|
104
|
+
return {
|
|
105
|
+
...dappPackageManifest,
|
|
106
|
+
schema: PackageManifestPayloadSchema,
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const getChildrenFromNode = async (node: NodeInstance) => {
|
|
111
|
+
const nodeManifest = (await node.state())?.[0] as NodeManifestPayload
|
|
112
|
+
return nodeManifest.status?.children
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const getNodeChildren = async (manifestToTest: PackageManifestPayload, dappParams: DappParams) => {
|
|
116
|
+
const testNodes = await getNodeToTest(manifestToTest, dappParams)
|
|
117
|
+
|
|
118
|
+
const children: Record<Address, string | null>[] = []
|
|
119
|
+
for (const node of testNodes) {
|
|
120
|
+
const nodeChildren = await getChildrenFromNode(node)
|
|
121
|
+
if (nodeChildren) {
|
|
122
|
+
children.push(nodeChildren)
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return children
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const getNodeToTest = async (manifestToTest: PackageManifestPayload, dappParams: DappParams) => {
|
|
130
|
+
const dappManifestWrapper = new ManifestWrapper(manifestToTest, await HDWallet.random(), dappParams.locator)
|
|
131
|
+
return await dappManifestWrapper.loadNodes()
|
|
132
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { assertEx } from '@xylabs/assert'
|
|
2
|
+
import { isEqual } from '@xylabs/lodash'
|
|
3
|
+
import type { NodeBackground, WalletSeedPhrase, XyOsContext } from '@xyo-network/os-model'
|
|
4
|
+
import type { WithMeta } from '@xyo-network/payload-model'
|
|
5
|
+
|
|
6
|
+
import { buildUserWalletSeedPhrasePayload } from '../../utils/index.ts'
|
|
7
|
+
import { OsSettingsCallerBase } from './CallerBase.ts'
|
|
8
|
+
import { OsSettingsQueries } from './SettingsQueries.ts'
|
|
9
|
+
|
|
10
|
+
export class OsSettingsCaller extends OsSettingsCallerBase {
|
|
11
|
+
constructor(context: XyOsContext) {
|
|
12
|
+
super(context)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Add a node background to the settings
|
|
17
|
+
* @param {WithMeta<NodeBackground>} payload
|
|
18
|
+
*/
|
|
19
|
+
async addNodeBackground(payload: WithMeta<NodeBackground>) {
|
|
20
|
+
const archivist = await this.getOsSettingsArchivist()
|
|
21
|
+
await archivist.insert([payload])
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Add a wallet seed phrase to the settings
|
|
26
|
+
* @param {WalletSeedPhrase} payload
|
|
27
|
+
*/
|
|
28
|
+
async addWalletSeedPhrase(payload: WalletSeedPhrase) {
|
|
29
|
+
// NOTE: Since setting the seed phrase in OS takes precedence
|
|
30
|
+
// over the BIOS when the two phrases differ, we'll set the seed
|
|
31
|
+
// phrase in the OS first. That way if the latter saves fail we can
|
|
32
|
+
// still recover on next boot.
|
|
33
|
+
|
|
34
|
+
// Save seed phrase to OS settings
|
|
35
|
+
const archivist = await this.getOsSettingsArchivist()
|
|
36
|
+
await archivist.insert([payload])
|
|
37
|
+
|
|
38
|
+
// Save seed phrase to BIOS
|
|
39
|
+
const phrase = payload.mnemonic.mnemonic.join(' ')
|
|
40
|
+
await this.context.kernel?.bios?.seedPhraseStore.set('user', phrase)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Get the latest node background
|
|
45
|
+
*/
|
|
46
|
+
async getLatestNodeBackground(): Promise<NodeBackground | null> {
|
|
47
|
+
const diviner = await this.getOsSettingsPayloadDiviner()
|
|
48
|
+
const [result] = (await OsSettingsQueries.getNodeBackground(diviner)) as WithMeta<NodeBackground>[]
|
|
49
|
+
return result ?? null
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async getLatestUserWallet(): Promise<WalletSeedPhrase> {
|
|
53
|
+
// Get the seed phrase from the OS settings
|
|
54
|
+
const diviner = await this.getOsSettingsPayloadDiviner()
|
|
55
|
+
const storedSeedPhrasePayload = (await OsSettingsQueries.getLatestUserWallet(diviner)).pop()
|
|
56
|
+
// Get the seed phrase from the BIOS
|
|
57
|
+
const biosWalletSeedPhrase = assertEx(await this.context.kernel?.bios?.seedPhraseStore.get('user'), () => 'No user seed phrase found from BIOS')
|
|
58
|
+
const biosWalletSeedPhraseParts = biosWalletSeedPhrase.split(' ')
|
|
59
|
+
// If there is no stored seed phrase in the OS, use the seed phrase from the BIOS
|
|
60
|
+
if (!storedSeedPhrasePayload?.mnemonic?.mnemonic) {
|
|
61
|
+
// Create new wallet payload from BIOS seed phrase
|
|
62
|
+
const { payload: biosSeedPhrasePayload } = await buildUserWalletSeedPhrasePayload(biosWalletSeedPhrase)
|
|
63
|
+
// Store the BIOS seed phrase in the OS settings
|
|
64
|
+
await this.addWalletSeedPhrase(biosSeedPhrasePayload)
|
|
65
|
+
// Return BIOS seed phrase payload
|
|
66
|
+
return biosSeedPhrasePayload
|
|
67
|
+
} else if (!isEqual(storedSeedPhrasePayload.mnemonic.mnemonic, biosWalletSeedPhraseParts)) {
|
|
68
|
+
// If there is a seed phrase in the OS settings that does not match the BIOS seed phrase
|
|
69
|
+
// Update bios with existing user seed phrase
|
|
70
|
+
const updatedBiosSeedPhrase = storedSeedPhrasePayload.mnemonic.mnemonic.join(' ')
|
|
71
|
+
await this.context.kernel?.bios?.seedPhraseStore.set('user', updatedBiosSeedPhrase)
|
|
72
|
+
}
|
|
73
|
+
// Return stored seed phrase
|
|
74
|
+
return storedSeedPhrasePayload
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { assertEx } from '@xylabs/assert'
|
|
2
|
+
import { asArchivistInstance } from '@xyo-network/archivist-model'
|
|
3
|
+
import { asDivinerInstance } from '@xyo-network/diviner-model'
|
|
4
|
+
import type { XyOsContext } from '@xyo-network/os-model'
|
|
5
|
+
|
|
6
|
+
const OsSettingsArchivistModuleName = 'OsSettingsNode:OsSettingsArchivist'
|
|
7
|
+
const OsSettingsArchivistPayloadDivinerModuleName = 'OsSettingsNode:OsSettingsArchivistPayloadDiviner'
|
|
8
|
+
|
|
9
|
+
export class OsSettingsCallerBase {
|
|
10
|
+
constructor(protected context: XyOsContext) {}
|
|
11
|
+
|
|
12
|
+
async getOsSettingsArchivist() {
|
|
13
|
+
const mod = assertEx(await this.context.root.resolve(OsSettingsArchivistModuleName), () => `${OsSettingsArchivistModuleName} not found`)
|
|
14
|
+
return asArchivistInstance(mod, () => `${OsSettingsArchivistModuleName} is not an archivist`)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async getOsSettingsPayloadDiviner() {
|
|
18
|
+
const mod = assertEx(
|
|
19
|
+
await this.context.root.resolve(OsSettingsArchivistPayloadDivinerModuleName),
|
|
20
|
+
() => `${OsSettingsArchivistPayloadDivinerModuleName} not found`,
|
|
21
|
+
)
|
|
22
|
+
return asDivinerInstance(mod, () => `${OsSettingsArchivistPayloadDivinerModuleName} is not a diviner`)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { assertEx } from '@xylabs/assert'
|
|
2
|
+
import type { NodeBackground, WalletSeedPhrase, XyOsContext } from '@xyo-network/os-model'
|
|
3
|
+
import { isNodeBackground, isWalletSeedPhrase } from '@xyo-network/os-model'
|
|
4
|
+
|
|
5
|
+
import type { Listener, ResourceStore } from '../../lib/index.ts'
|
|
6
|
+
import { PayloadStore } from '../../lib/index.ts'
|
|
7
|
+
import { OsSettingsCallerBase } from './CallerBase.ts'
|
|
8
|
+
import { OsSettingsQueries } from './SettingsQueries.ts'
|
|
9
|
+
|
|
10
|
+
export type OsSettingsResourceViews = 'wallets' | 'latestUserWallet' | 'nodeBackground'
|
|
11
|
+
|
|
12
|
+
export class OsSettingsResource extends OsSettingsCallerBase implements ResourceStore<OsSettingsResourceViews> {
|
|
13
|
+
private _latestUserWalletListener: PayloadStore<WalletSeedPhrase> | undefined
|
|
14
|
+
private _nodeBackgroundListener: PayloadStore<NodeBackground> | undefined
|
|
15
|
+
private _walletsListener: PayloadStore<WalletSeedPhrase> | undefined
|
|
16
|
+
|
|
17
|
+
constructor(context: XyOsContext) {
|
|
18
|
+
super(context)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
get latestUserWalletListener() {
|
|
22
|
+
return assertEx(this._latestUserWalletListener, () => 'Latest user wallet listener not set')
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
get nodeBackgroundListener() {
|
|
26
|
+
return assertEx(this._nodeBackgroundListener, () => 'Node background listener not set')
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
get subscriptions() {
|
|
30
|
+
return {
|
|
31
|
+
latestUserWallet: (cb: Listener) => this.walletsListener.subscribe(cb),
|
|
32
|
+
nodeBackground: (cb: Listener) => this.nodeBackgroundListener.subscribe(cb),
|
|
33
|
+
wallets: (cb: Listener) => this.walletsListener.subscribe(cb),
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
get views() {
|
|
38
|
+
return {
|
|
39
|
+
latestUserWallet: () => this.latestUserWalletListener.latest,
|
|
40
|
+
nodeBackground: () => this.nodeBackgroundListener.latest,
|
|
41
|
+
wallets: () => this.walletsListener.latest,
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
get walletsListener() {
|
|
46
|
+
return assertEx(this._walletsListener, () => 'Wallets listener not set')
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
cleanupListeners() {
|
|
50
|
+
this.walletsListener.cleanupListeners()
|
|
51
|
+
this.latestUserWalletListener.cleanupListeners()
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* start listeners
|
|
56
|
+
*/
|
|
57
|
+
async start() {
|
|
58
|
+
const archivist = await this.getOsSettingsArchivist()
|
|
59
|
+
const diviner = await this.getOsSettingsPayloadDiviner()
|
|
60
|
+
|
|
61
|
+
this._walletsListener = await PayloadStore.create<WalletSeedPhrase>({
|
|
62
|
+
archivist,
|
|
63
|
+
getLatest: async () => await OsSettingsQueries.getWallets(diviner),
|
|
64
|
+
idFunction: isWalletSeedPhrase,
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
this._latestUserWalletListener = await PayloadStore.create<WalletSeedPhrase>({
|
|
68
|
+
archivist,
|
|
69
|
+
getLatest: async () => await OsSettingsQueries.getLatestUserWallet(diviner),
|
|
70
|
+
idFunction: isWalletSeedPhrase,
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
this._nodeBackgroundListener = await PayloadStore.create<NodeBackground>({
|
|
74
|
+
archivist,
|
|
75
|
+
getLatest: async () => await OsSettingsQueries.getNodeBackground(diviner),
|
|
76
|
+
idFunction: isNodeBackground,
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { DivinerInstance } from '@xyo-network/diviner-model'
|
|
2
|
+
import { PayloadDivinerQuerySchema } from '@xyo-network/diviner-payload-model'
|
|
3
|
+
import type { NodeBackground, WalletSeedPhrase } from '@xyo-network/os-model'
|
|
4
|
+
import { NodeBackgroundSchema, WalletSeedPhraseSchema } from '@xyo-network/os-model'
|
|
5
|
+
import type { WithMeta } from '@xyo-network/payload-model'
|
|
6
|
+
|
|
7
|
+
const NO_RESULTS: WithMeta<WalletSeedPhrase>[] = []
|
|
8
|
+
|
|
9
|
+
export const OsSettingsQueries = {
|
|
10
|
+
async getLatestUserWallet(diviner: DivinerInstance) {
|
|
11
|
+
const query = {
|
|
12
|
+
limit: 1,
|
|
13
|
+
order: 'desc',
|
|
14
|
+
schema: PayloadDivinerQuerySchema,
|
|
15
|
+
schemas: [WalletSeedPhraseSchema],
|
|
16
|
+
}
|
|
17
|
+
const results = (await diviner.divine([query])) as WithMeta<WalletSeedPhrase>[]
|
|
18
|
+
const wallet = results.find(payload => payload.$meta.label === 'userWallet')
|
|
19
|
+
return wallet ? [wallet] : NO_RESULTS
|
|
20
|
+
},
|
|
21
|
+
async getNodeBackground(diviner: DivinerInstance) {
|
|
22
|
+
const query = {
|
|
23
|
+
limit: 1,
|
|
24
|
+
order: 'desc',
|
|
25
|
+
schema: PayloadDivinerQuerySchema,
|
|
26
|
+
schemas: [NodeBackgroundSchema],
|
|
27
|
+
}
|
|
28
|
+
return (await diviner.divine([query])) as WithMeta<NodeBackground>[]
|
|
29
|
+
},
|
|
30
|
+
async getWallets(diviner: DivinerInstance) {
|
|
31
|
+
const query = {
|
|
32
|
+
order: 'desc',
|
|
33
|
+
schema: PayloadDivinerQuerySchema,
|
|
34
|
+
schemas: [WalletSeedPhraseSchema],
|
|
35
|
+
}
|
|
36
|
+
return (await diviner.divine([query])) as WithMeta<WalletSeedPhrase>[]
|
|
37
|
+
},
|
|
38
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ValidOsAchievements } from '@xyo-network/os-model'
|
|
2
|
+
import { OsBadgeSchema } from '@xyo-network/os-model'
|
|
3
|
+
|
|
4
|
+
import { OsSettingsCallerBase } from '../CallerBase.ts'
|
|
5
|
+
import { OsBadgeQueries } from './Queries.ts'
|
|
6
|
+
|
|
7
|
+
export class OsBadgeCaller extends OsSettingsCallerBase {
|
|
8
|
+
/**
|
|
9
|
+
* @param {ValidOsAchievements} achievement
|
|
10
|
+
*/
|
|
11
|
+
async getBadge(achievement: ValidOsAchievements) {
|
|
12
|
+
const diviner = await this.getOsSettingsPayloadDiviner()
|
|
13
|
+
const results = await OsBadgeQueries.getBadge(achievement, diviner)
|
|
14
|
+
return results[0] ?? null
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @param {ValidOsAchievements} achievement
|
|
19
|
+
*/
|
|
20
|
+
async saveBadge(achievement: ValidOsAchievements) {
|
|
21
|
+
const badge = {
|
|
22
|
+
achievement,
|
|
23
|
+
schema: OsBadgeSchema,
|
|
24
|
+
timestamp: Date.now(),
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const archivist = await this.getOsSettingsArchivist()
|
|
28
|
+
return await archivist.insert([badge])
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { DivinerInstance } from '@xyo-network/diviner-model'
|
|
2
|
+
import { PayloadDivinerQuerySchema } from '@xyo-network/diviner-payload-model'
|
|
3
|
+
import type { OsBadge } from '@xyo-network/os-model'
|
|
4
|
+
import { OsBadgeSchema } from '@xyo-network/os-model'
|
|
5
|
+
|
|
6
|
+
export const OsBadgeQueries = {
|
|
7
|
+
getBadge: async (achievement: string, diviner: DivinerInstance) => {
|
|
8
|
+
const query = {
|
|
9
|
+
achievement,
|
|
10
|
+
limit: 1,
|
|
11
|
+
order: 'desc',
|
|
12
|
+
schema: PayloadDivinerQuerySchema,
|
|
13
|
+
schemas: [OsBadgeSchema],
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return (await diviner.divine([query])) as OsBadge[]
|
|
17
|
+
},
|
|
18
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { assertEx } from '@xylabs/assert'
|
|
2
|
+
import type { OsBadge, XyOsContext } from '@xyo-network/os-model'
|
|
3
|
+
import { isBadgeWithMeta } from '@xyo-network/os-model'
|
|
4
|
+
|
|
5
|
+
import type { ResourceStore } from '../../../lib/index.ts'
|
|
6
|
+
import { PayloadStore } from '../../../lib/index.ts'
|
|
7
|
+
import { OsSettingsCallerBase } from '../CallerBase.ts'
|
|
8
|
+
import { OsBadgeQueries } from './Queries.ts'
|
|
9
|
+
|
|
10
|
+
export type OsBadgeResourceViews = 'savedSeedPhrase'
|
|
11
|
+
|
|
12
|
+
export class OsBadgeResource extends OsSettingsCallerBase implements ResourceStore<OsBadgeResourceViews> {
|
|
13
|
+
static readonly views: OsBadgeResourceViews[] = ['savedSeedPhrase']
|
|
14
|
+
|
|
15
|
+
private _savedSeedPhrase: PayloadStore<OsBadge> | undefined
|
|
16
|
+
|
|
17
|
+
constructor(xyOsContext: XyOsContext) {
|
|
18
|
+
super(xyOsContext)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
get savedSeedPhrase() {
|
|
22
|
+
return assertEx(this._savedSeedPhrase, () => 'Saved seed phrase listener not set')
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
get subscriptions() {
|
|
26
|
+
return {
|
|
27
|
+
savedSeedPhrase: (cb: () => void) => this.savedSeedPhrase.subscribe(cb),
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
get views() {
|
|
32
|
+
return {
|
|
33
|
+
savedSeedPhrase: () => this.savedSeedPhrase.latest,
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
cleanupListeners() {
|
|
38
|
+
this.savedSeedPhrase.cleanupListeners()
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* start listeners
|
|
43
|
+
**/
|
|
44
|
+
async start() {
|
|
45
|
+
const diviner = await this.getOsSettingsPayloadDiviner()
|
|
46
|
+
const archivist = await this.getOsSettingsArchivist()
|
|
47
|
+
|
|
48
|
+
this._savedSeedPhrase = await PayloadStore.create<OsBadge>({
|
|
49
|
+
archivist,
|
|
50
|
+
getLatest: async () => await OsBadgeQueries.getBadge('savedSeedPhrase', diviner),
|
|
51
|
+
idFunction: isBadgeWithMeta,
|
|
52
|
+
})
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import type { ArchivistInstance } from '@xyo-network/archivist-model'
|
|
2
|
+
import type { ModuleFactoryLocator } from '@xyo-network/module-factory-locator'
|
|
3
|
+
import type { DappConfig, DappIcon, DappParams, DappSet, PayloadWithVersion, XyOsContext } from '@xyo-network/os-model'
|
|
4
|
+
import { PayloadBuilder } from '@xyo-network/payload-builder'
|
|
5
|
+
import type { WithMeta } from '@xyo-network/payload-model'
|
|
6
|
+
import semver from 'semver'
|
|
7
|
+
|
|
8
|
+
import { OsCallerBase } from '../../OsCallerBase.ts'
|
|
9
|
+
import { SystemDappQueries } from './Queries.ts'
|
|
10
|
+
|
|
11
|
+
export type ErrorListener = (failedPayloads: DappConfig[]) => void
|
|
12
|
+
export type DappSetsAndPayloads = { dappPayloads: (DappConfig | DappIcon)[]; dappSets: DappSet[] }
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Manage the installation of system dapps from payloads and return the built sets
|
|
16
|
+
*/
|
|
17
|
+
export class ManageSystemDapps extends OsCallerBase {
|
|
18
|
+
private onErrorCallbacks: ErrorListener[] = []
|
|
19
|
+
|
|
20
|
+
constructor(
|
|
21
|
+
context: XyOsContext,
|
|
22
|
+
private defaultSystemNames: string[],
|
|
23
|
+
private defaultSystemDapps: PayloadWithVersion[],
|
|
24
|
+
private defaultSystemDappParams: Record<string, DappParams>,
|
|
25
|
+
private locator?: ModuleFactoryLocator,
|
|
26
|
+
private developmentMode?: boolean,
|
|
27
|
+
) {
|
|
28
|
+
super(context)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Add a callback to listen for errors throwing during system dapp registration
|
|
33
|
+
* @param {ErrorListener} errorListener
|
|
34
|
+
*/
|
|
35
|
+
addErrorListener(errorListener: ErrorListener) {
|
|
36
|
+
this.onErrorCallbacks.push(errorListener)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Installs the dapps into the OS and builds the appropriate DappSet
|
|
41
|
+
*/
|
|
42
|
+
async install(): Promise<DappSet[]> {
|
|
43
|
+
await this.insertPayloads()
|
|
44
|
+
return await this.latestSets()
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private async freshInstall(archivist: ArchivistInstance): Promise<void> {
|
|
48
|
+
await archivist.insert(this.defaultSystemDapps)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private async insertPayloads(): Promise<boolean> {
|
|
52
|
+
const archivist = this.developmentMode ? await this.getDappsArchivistDevelopment() : await this.getDappsArchivist()
|
|
53
|
+
try {
|
|
54
|
+
const allPayloads = (await archivist.all()) as WithMeta<DappIcon | DappConfig>[]
|
|
55
|
+
if (allPayloads.length === 0) {
|
|
56
|
+
await this.freshInstall(archivist)
|
|
57
|
+
return true
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// only insert unseen system dapp payloads
|
|
61
|
+
for (const systemDappPayload of this.defaultSystemDapps) {
|
|
62
|
+
try {
|
|
63
|
+
const validSemver = semver.valid(systemDappPayload.version)
|
|
64
|
+
if (!validSemver) throw new Error('semver is a valid type (string) but was not able to be parsed')
|
|
65
|
+
} catch (e) {
|
|
66
|
+
console.error(`${systemDappPayload.version} does not appear to be a valid semver value`, e)
|
|
67
|
+
continue
|
|
68
|
+
}
|
|
69
|
+
const [existing] = await archivist.get([await PayloadBuilder.dataHash(systemDappPayload)])
|
|
70
|
+
if (existing) continue
|
|
71
|
+
await archivist.insert([systemDappPayload])
|
|
72
|
+
}
|
|
73
|
+
return true
|
|
74
|
+
} catch (e) {
|
|
75
|
+
console.error('error installing system dapps')
|
|
76
|
+
throw e
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private async latestSets() {
|
|
81
|
+
const diviner = this.developmentMode ? await this.getDappsArchivistPayloadDivinerDevelopment() : await this.getDappsArchivistPayloadDiviner()
|
|
82
|
+
const dappSets: DappSet[] = []
|
|
83
|
+
const failedPayloads: DappConfig[] = []
|
|
84
|
+
|
|
85
|
+
for (const name of this.defaultSystemNames) {
|
|
86
|
+
try {
|
|
87
|
+
const dappConfig = await SystemDappQueries.getLatestConfig(diviner, name)
|
|
88
|
+
if (!dappConfig) {
|
|
89
|
+
console.error(`No config found for dapp ${name}`)
|
|
90
|
+
continue
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const dappIcon = await SystemDappQueries.getLatestIcon(diviner, name, dappConfig.version)
|
|
94
|
+
if (!dappIcon) {
|
|
95
|
+
console.error(`No icon found for dapp ${dappConfig.name}`)
|
|
96
|
+
failedPayloads.push(dappConfig)
|
|
97
|
+
continue
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const dappAccess = await SystemDappQueries.getDappAccess(diviner, name, dappConfig.version)
|
|
101
|
+
|
|
102
|
+
const dappParams = this.defaultSystemDappParams[dappConfig.name]
|
|
103
|
+
if (!dappParams) {
|
|
104
|
+
console.error(`No params found for dapp ${dappConfig.name}. Did you forget to add it to OsDappParams?`)
|
|
105
|
+
failedPayloads.push(dappConfig)
|
|
106
|
+
continue
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const dappWidgetConfigs = await SystemDappQueries.getWidgetConfigs(diviner, name, dappConfig.version)
|
|
110
|
+
|
|
111
|
+
dappSets.push({
|
|
112
|
+
dapp: {
|
|
113
|
+
accessors: dappAccess,
|
|
114
|
+
config: dappConfig,
|
|
115
|
+
icon: dappIcon,
|
|
116
|
+
params: dappParams,
|
|
117
|
+
widgetConfigs: dappWidgetConfigs,
|
|
118
|
+
},
|
|
119
|
+
})
|
|
120
|
+
} catch {
|
|
121
|
+
console.error(`Failed to build dapp ${name}`)
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (failedPayloads.length > 0) {
|
|
126
|
+
for (const cb of this.onErrorCallbacks) cb(failedPayloads)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return dappSets
|
|
130
|
+
}
|
|
131
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { DivinerInstance } from '@xyo-network/diviner-model'
|
|
2
|
+
import { PayloadDivinerQuerySchema } from '@xyo-network/diviner-payload-model'
|
|
3
|
+
import type {
|
|
4
|
+
DappConfig,
|
|
5
|
+
DappIcon,
|
|
6
|
+
DappId,
|
|
7
|
+
DappWidgetConfig,
|
|
8
|
+
UnregisteredDappAccess } from '@xyo-network/os-model'
|
|
9
|
+
import {
|
|
10
|
+
DappConfigSchema,
|
|
11
|
+
DappIconSchema,
|
|
12
|
+
DappWidgetConfigSchema,
|
|
13
|
+
UnregisteredDappAccessSchema,
|
|
14
|
+
} from '@xyo-network/os-model'
|
|
15
|
+
import { PayloadBuilder } from '@xyo-network/payload-builder'
|
|
16
|
+
import type { WithMeta } from '@xyo-network/payload-model'
|
|
17
|
+
|
|
18
|
+
export const SystemDappQueries = {
|
|
19
|
+
async getDappAccess(diviner: DivinerInstance, name: string, version: string) {
|
|
20
|
+
const accessQuery = {
|
|
21
|
+
order: 'desc',
|
|
22
|
+
registeringDappId: name,
|
|
23
|
+
schema: PayloadDivinerQuerySchema,
|
|
24
|
+
schemas: [UnregisteredDappAccessSchema],
|
|
25
|
+
version: version,
|
|
26
|
+
}
|
|
27
|
+
return (await diviner.divine([accessQuery])) as WithMeta<UnregisteredDappAccess>[]
|
|
28
|
+
},
|
|
29
|
+
async getLatestConfig(diviner: DivinerInstance, name: string) {
|
|
30
|
+
const configQuery = {
|
|
31
|
+
limit: 1,
|
|
32
|
+
name,
|
|
33
|
+
order: 'desc',
|
|
34
|
+
schema: PayloadDivinerQuerySchema,
|
|
35
|
+
schemas: [DappConfigSchema],
|
|
36
|
+
}
|
|
37
|
+
const [dappConfig] = (await diviner.divine([configQuery])) as WithMeta<DappConfig>[]
|
|
38
|
+
return dappConfig
|
|
39
|
+
},
|
|
40
|
+
async getLatestIcon(diviner: DivinerInstance, name: string, version: string) {
|
|
41
|
+
const iconQuery = {
|
|
42
|
+
name,
|
|
43
|
+
order: 'desc',
|
|
44
|
+
schema: PayloadDivinerQuerySchema,
|
|
45
|
+
schemas: [DappIconSchema],
|
|
46
|
+
version,
|
|
47
|
+
}
|
|
48
|
+
const [dappIcon] = (await diviner.divine([iconQuery])) as WithMeta<DappIcon>[]
|
|
49
|
+
return dappIcon
|
|
50
|
+
},
|
|
51
|
+
async getWidgetConfigs(diviner: DivinerInstance, name: DappId, version: string): Promise<WithMeta<DappWidgetConfig>[]> {
|
|
52
|
+
const widgetConfigQuery = {
|
|
53
|
+
dappId: name,
|
|
54
|
+
order: 'desc',
|
|
55
|
+
schema: PayloadDivinerQuerySchema,
|
|
56
|
+
schemas: [DappWidgetConfigSchema],
|
|
57
|
+
version,
|
|
58
|
+
}
|
|
59
|
+
const widgetConfigs = (await diviner.divine([widgetConfigQuery])) as WithMeta<DappWidgetConfig>[]
|
|
60
|
+
|
|
61
|
+
const uniqueConfigs: Record<string, WithMeta<DappWidgetConfig>> = {}
|
|
62
|
+
for (const widgetConfig of widgetConfigs) {
|
|
63
|
+
const hash = await PayloadBuilder.dataHash(widgetConfig)
|
|
64
|
+
uniqueConfigs[widgetConfig.mode ?? hash] = widgetConfig
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return Object.values(uniqueConfigs)
|
|
68
|
+
},
|
|
69
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './ManageSystemDapps.ts'
|