@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.
Files changed (132) hide show
  1. package/LICENSE +165 -0
  2. package/README.md +13 -0
  3. package/dist/neutral/index.d.ts +1656 -0
  4. package/dist/neutral/index.mjs +3432 -0
  5. package/dist/neutral/index.mjs.map +1 -0
  6. package/package.json +78 -0
  7. package/src/Caller.ts +221 -0
  8. package/src/DappCallerBase.ts +52 -0
  9. package/src/DefaultsQueries.ts +27 -0
  10. package/src/OsCallerBase.ts +55 -0
  11. package/src/PubSubBridgeCaller.ts +30 -0
  12. package/src/XyOs.ts +125 -0
  13. package/src/XyOsBase.ts +119 -0
  14. package/src/XyOsDapp.ts +90 -0
  15. package/src/access-interfaces/ValidDappAccessInterfaces.ts +8 -0
  16. package/src/access-interfaces/index.ts +2 -0
  17. package/src/access-interfaces/registered-names/helpers/AccessNodeQueries.ts +59 -0
  18. package/src/access-interfaces/registered-names/helpers/index.ts +2 -0
  19. package/src/access-interfaces/registered-names/helpers/resource/AbstractXnsCaller.ts +38 -0
  20. package/src/access-interfaces/registered-names/helpers/resource/RegistrationsResource.ts +54 -0
  21. package/src/access-interfaces/registered-names/helpers/resource/RegistrationsResourceQueries.ts +22 -0
  22. package/src/access-interfaces/registered-names/helpers/resource/index.ts +3 -0
  23. package/src/access-interfaces/registered-names/index.ts +1 -0
  24. package/src/adapter/Base.ts +96 -0
  25. package/src/adapter/Network.ts +31 -0
  26. package/src/adapter/Settings.ts +30 -0
  27. package/src/adapter/index.ts +2 -0
  28. package/src/adapters/OsPubSubBridgeNetwork.ts +10 -0
  29. package/src/adapters/OsSettings.ts +7 -0
  30. package/src/adapters/OsXyoPublicNetwork.ts +9 -0
  31. package/src/adapters/index.ts +3 -0
  32. package/src/classes/cache/RunningAccessDappCache.ts +21 -0
  33. package/src/classes/cache/RunningDappCache.ts +50 -0
  34. package/src/classes/cache/index.ts +2 -0
  35. package/src/classes/dapp/DefaultsResource.ts +65 -0
  36. package/src/classes/dapp/access/Caller.ts +73 -0
  37. package/src/classes/dapp/access/Queries.ts +38 -0
  38. package/src/classes/dapp/access/Resource.ts +63 -0
  39. package/src/classes/dapp/access/index.ts +3 -0
  40. package/src/classes/dapp/index.ts +2 -0
  41. package/src/classes/index.ts +8 -0
  42. package/src/classes/lib/DappCreatorParams.ts +16 -0
  43. package/src/classes/lib/Insertable.ts +14 -0
  44. package/src/classes/lib/index.ts +2 -0
  45. package/src/classes/menu/Caller.ts +76 -0
  46. package/src/classes/menu/Queries.ts +59 -0
  47. package/src/classes/menu/Resource.ts +103 -0
  48. package/src/classes/menu/index.ts +2 -0
  49. package/src/classes/node/Creator.ts +96 -0
  50. package/src/classes/node/DefaultPayloads/DappAccessPayloads.ts +17 -0
  51. package/src/classes/node/DefaultPayloads/DefaultPayloads.ts +33 -0
  52. package/src/classes/node/DefaultPayloads/NodeInfoPayload.ts +27 -0
  53. package/src/classes/node/DefaultPayloads/SigningKeyPayloads.ts +85 -0
  54. package/src/classes/node/DefaultPayloads/index.ts +1 -0
  55. package/src/classes/node/ExternalModulePermissions/ExternalModulePermissions.ts +47 -0
  56. package/src/classes/node/ExternalModulePermissions/index.ts +1 -0
  57. package/src/classes/node/createDappContext.ts +52 -0
  58. package/src/classes/node/index.ts +3 -0
  59. package/src/classes/registration/DappRegistrationService.ts +146 -0
  60. package/src/classes/registration/DappRegistry.ts +121 -0
  61. package/src/classes/registration/ValidateDappAccessDiviner/Config.ts +19 -0
  62. package/src/classes/registration/ValidateDappAccessDiviner/Diviner.ts +132 -0
  63. package/src/classes/registration/ValidateDappAccessDiviner/index.ts +2 -0
  64. package/src/classes/registration/index.ts +2 -0
  65. package/src/classes/settings/Caller.ts +76 -0
  66. package/src/classes/settings/CallerBase.ts +24 -0
  67. package/src/classes/settings/Resource.ts +79 -0
  68. package/src/classes/settings/SettingsQueries.ts +38 -0
  69. package/src/classes/settings/badge/Caller.ts +30 -0
  70. package/src/classes/settings/badge/Queries.ts +18 -0
  71. package/src/classes/settings/badge/Resource.ts +54 -0
  72. package/src/classes/settings/badge/index.ts +2 -0
  73. package/src/classes/settings/index.ts +5 -0
  74. package/src/classes/system/ManageSystemDapps.ts +131 -0
  75. package/src/classes/system/Queries.ts +69 -0
  76. package/src/classes/system/index.ts +1 -0
  77. package/src/event/bus/Connection.ts +31 -0
  78. package/src/event/bus/EventBus.ts +145 -0
  79. package/src/event/bus/PubSubConnection.ts +28 -0
  80. package/src/event/bus/index.ts +3 -0
  81. package/src/event/connections/DappAccessRequest.ts +11 -0
  82. package/src/event/connections/DappsReady.ts +9 -0
  83. package/src/event/connections/ExposeDappRequest.ts +11 -0
  84. package/src/event/connections/OsPubSubNetworkReady.ts +12 -0
  85. package/src/event/connections/OsSettingsReady.ts +12 -0
  86. package/src/event/connections/OsXyoPublicReady.ts +12 -0
  87. package/src/event/connections/index.ts +6 -0
  88. package/src/event/index.ts +2 -0
  89. package/src/helpers/index.ts +1 -0
  90. package/src/helpers/monitor/XyOsMonitor.ts +52 -0
  91. package/src/helpers/monitor/index.ts +2 -0
  92. package/src/helpers/monitor/types.ts +5 -0
  93. package/src/index.ts +22 -0
  94. package/src/intent/Caller.ts +72 -0
  95. package/src/intent/Resource.ts +66 -0
  96. package/src/intent/index.ts +2 -0
  97. package/src/lib/ExternalStore.ts +7 -0
  98. package/src/lib/Listener.ts +1 -0
  99. package/src/lib/ModuleAccountPaths.ts +29 -0
  100. package/src/lib/ModuleNames.ts +3 -0
  101. package/src/lib/NameTransforms.ts +31 -0
  102. package/src/lib/PayloadStore.ts +98 -0
  103. package/src/lib/ResourceStores.ts +7 -0
  104. package/src/lib/index.ts +10 -0
  105. package/src/lib/initializeXns.ts +16 -0
  106. package/src/lib/isPayload.ts +24 -0
  107. package/src/lib/tokenPlacesSplit.ts +17 -0
  108. package/src/loadOsNode.ts +43 -0
  109. package/src/manifest/ManifestReplaceableTokens.ts +17 -0
  110. package/src/manifest/index.ts +2 -0
  111. package/src/manifest/os-node.manifest.json +132 -0
  112. package/src/manifests/dapp-window.manifest.json +36 -0
  113. package/src/manifests/index.ts +1 -0
  114. package/src/profileModuleEvents.ts +43 -0
  115. package/src/stack/Base.ts +153 -0
  116. package/src/stack/Manager.ts +48 -0
  117. package/src/stack/Map.ts +20 -0
  118. package/src/stack/OsPubSubNetworkStack.ts +70 -0
  119. package/src/stack/OsSettingsStack.ts +24 -0
  120. package/src/stack/XyoPublicNetworkStack.ts +32 -0
  121. package/src/stack/index.ts +6 -0
  122. package/src/types/global.d.ts +9 -0
  123. package/src/types/images.d.ts +5 -0
  124. package/src/utils/buildWalletSeedPhrasePayload.ts +41 -0
  125. package/src/utils/getApiDomain.ts +14 -0
  126. package/src/utils/index.ts +3 -0
  127. package/src/utils/renameObjKey.ts +22 -0
  128. package/src/wallet/DappSeedPhraseRepository.ts +95 -0
  129. package/src/wallet/SeedPhraseRepository.ts +32 -0
  130. package/src/wallet/index.ts +2 -0
  131. package/typedoc.json +5 -0
  132. 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,2 @@
1
+ export * from './Config.ts'
2
+ export * from './Diviner.ts'
@@ -0,0 +1,2 @@
1
+ export * from './DappRegistrationService.ts'
2
+ export * from './DappRegistry.ts'
@@ -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,2 @@
1
+ export * from './Caller.ts'
2
+ export * from './Resource.ts'
@@ -0,0 +1,5 @@
1
+ export * from './badge/index.ts'
2
+ export * from './Caller.ts'
3
+ export * from './CallerBase.ts'
4
+ export * from './Resource.ts'
5
+ export * from './SettingsQueries.ts'
@@ -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'