@misofm/platform 0.0.0 → 0.18.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 (168) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +686 -3
  3. package/dist/auth.d.ts +73 -0
  4. package/dist/auth.d.ts.map +1 -0
  5. package/dist/auth.js +193 -0
  6. package/dist/auth.js.map +1 -0
  7. package/dist/catalog.d.ts +48 -0
  8. package/dist/catalog.d.ts.map +1 -0
  9. package/dist/catalog.js +92 -0
  10. package/dist/catalog.js.map +1 -0
  11. package/dist/client.d.ts +462 -0
  12. package/dist/client.d.ts.map +1 -0
  13. package/dist/client.js +788 -0
  14. package/dist/client.js.map +1 -0
  15. package/dist/contracts.d.ts +34 -0
  16. package/dist/contracts.d.ts.map +1 -0
  17. package/dist/contracts.js +49 -0
  18. package/dist/contracts.js.map +1 -0
  19. package/dist/cover.d.ts +69 -0
  20. package/dist/cover.d.ts.map +1 -0
  21. package/dist/cover.js +132 -0
  22. package/dist/cover.js.map +1 -0
  23. package/dist/credits.d.ts +200 -0
  24. package/dist/credits.d.ts.map +1 -0
  25. package/dist/credits.js +465 -0
  26. package/dist/credits.js.map +1 -0
  27. package/dist/deployments.d.ts +224 -0
  28. package/dist/deployments.d.ts.map +1 -0
  29. package/dist/deployments.js +177 -0
  30. package/dist/deployments.js.map +1 -0
  31. package/dist/execute.d.ts +30 -0
  32. package/dist/execute.d.ts.map +1 -0
  33. package/dist/execute.js +31 -0
  34. package/dist/execute.js.map +1 -0
  35. package/dist/index.d.ts +21 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +52 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/internal.d.ts +13 -0
  40. package/dist/internal.d.ts.map +1 -0
  41. package/dist/internal.js +41 -0
  42. package/dist/internal.js.map +1 -0
  43. package/dist/mix.d.ts +144 -0
  44. package/dist/mix.d.ts.map +1 -0
  45. package/dist/mix.js +656 -0
  46. package/dist/mix.js.map +1 -0
  47. package/dist/pressing.d.ts +137 -0
  48. package/dist/pressing.d.ts.map +1 -0
  49. package/dist/pressing.js +390 -0
  50. package/dist/pressing.js.map +1 -0
  51. package/dist/publication.d.ts +216 -0
  52. package/dist/publication.d.ts.map +1 -0
  53. package/dist/publication.js +872 -0
  54. package/dist/publication.js.map +1 -0
  55. package/dist/queries.d.ts +2 -0
  56. package/dist/queries.d.ts.map +1 -0
  57. package/dist/queries.js +8 -0
  58. package/dist/queries.js.map +1 -0
  59. package/dist/read/artist.d.ts +18 -0
  60. package/dist/read/artist.d.ts.map +1 -0
  61. package/dist/read/artist.js +86 -0
  62. package/dist/read/artist.js.map +1 -0
  63. package/dist/read/catalog.d.ts +93 -0
  64. package/dist/read/catalog.d.ts.map +1 -0
  65. package/dist/read/catalog.js +441 -0
  66. package/dist/read/catalog.js.map +1 -0
  67. package/dist/read/client.d.ts +37 -0
  68. package/dist/read/client.d.ts.map +1 -0
  69. package/dist/read/client.js +50 -0
  70. package/dist/read/client.js.map +1 -0
  71. package/dist/read/config.d.ts +68 -0
  72. package/dist/read/config.d.ts.map +1 -0
  73. package/dist/read/config.js +63 -0
  74. package/dist/read/config.js.map +1 -0
  75. package/dist/read/genres.d.ts +7 -0
  76. package/dist/read/genres.d.ts.map +1 -0
  77. package/dist/read/genres.js +38 -0
  78. package/dist/read/genres.js.map +1 -0
  79. package/dist/read/index.d.ts +14 -0
  80. package/dist/read/index.d.ts.map +1 -0
  81. package/dist/read/index.js +23 -0
  82. package/dist/read/index.js.map +1 -0
  83. package/dist/read/internal/scalars.d.ts +23 -0
  84. package/dist/read/internal/scalars.d.ts.map +1 -0
  85. package/dist/read/internal/scalars.js +57 -0
  86. package/dist/read/internal/scalars.js.map +1 -0
  87. package/dist/read/internal/walrus.d.ts +6 -0
  88. package/dist/read/internal/walrus.d.ts.map +1 -0
  89. package/dist/read/internal/walrus.js +29 -0
  90. package/dist/read/internal/walrus.js.map +1 -0
  91. package/dist/read/receipts.d.ts +41 -0
  92. package/dist/read/receipts.d.ts.map +1 -0
  93. package/dist/read/receipts.js +398 -0
  94. package/dist/read/receipts.js.map +1 -0
  95. package/dist/read/types.d.ts +328 -0
  96. package/dist/read/types.d.ts.map +1 -0
  97. package/dist/read/types.js +21 -0
  98. package/dist/read/types.js.map +1 -0
  99. package/dist/read/wallet.d.ts +74 -0
  100. package/dist/read/wallet.d.ts.map +1 -0
  101. package/dist/read/wallet.js +578 -0
  102. package/dist/read/wallet.js.map +1 -0
  103. package/dist/read/works.d.ts +29 -0
  104. package/dist/read/works.d.ts.map +1 -0
  105. package/dist/read/works.js +191 -0
  106. package/dist/read/works.js.map +1 -0
  107. package/dist/recording-extensions.d.ts +62 -0
  108. package/dist/recording-extensions.d.ts.map +1 -0
  109. package/dist/recording-extensions.js +159 -0
  110. package/dist/recording-extensions.js.map +1 -0
  111. package/dist/release-extensions.d.ts +93 -0
  112. package/dist/release-extensions.d.ts.map +1 -0
  113. package/dist/release-extensions.js +190 -0
  114. package/dist/release-extensions.js.map +1 -0
  115. package/dist/release-graph.d.ts +65 -0
  116. package/dist/release-graph.d.ts.map +1 -0
  117. package/dist/release-graph.js +136 -0
  118. package/dist/release-graph.js.map +1 -0
  119. package/dist/share-template.d.ts +3 -0
  120. package/dist/share-template.d.ts.map +1 -0
  121. package/dist/share-template.js +46 -0
  122. package/dist/share-template.js.map +1 -0
  123. package/dist/share.d.ts +67 -0
  124. package/dist/share.d.ts.map +1 -0
  125. package/dist/share.js +157 -0
  126. package/dist/share.js.map +1 -0
  127. package/dist/transactions.d.ts +216 -0
  128. package/dist/transactions.d.ts.map +1 -0
  129. package/dist/transactions.js +314 -0
  130. package/dist/transactions.js.map +1 -0
  131. package/dist/vault.d.ts +364 -0
  132. package/dist/vault.d.ts.map +1 -0
  133. package/dist/vault.js +507 -0
  134. package/dist/vault.js.map +1 -0
  135. package/package.json +145 -5
  136. package/src/auth.ts +275 -0
  137. package/src/catalog.ts +189 -0
  138. package/src/client.ts +1320 -0
  139. package/src/contracts.ts +54 -0
  140. package/src/cover.ts +240 -0
  141. package/src/credits.ts +906 -0
  142. package/src/deployments.ts +357 -0
  143. package/src/execute.ts +52 -0
  144. package/src/index.ts +69 -0
  145. package/src/internal.ts +46 -0
  146. package/src/mix.ts +910 -0
  147. package/src/pressing.ts +705 -0
  148. package/src/publication.ts +1214 -0
  149. package/src/queries.ts +9 -0
  150. package/src/read/artist.ts +118 -0
  151. package/src/read/catalog.ts +668 -0
  152. package/src/read/client.ts +87 -0
  153. package/src/read/config.ts +138 -0
  154. package/src/read/genres.ts +37 -0
  155. package/src/read/index.ts +85 -0
  156. package/src/read/internal/scalars.ts +59 -0
  157. package/src/read/internal/walrus.ts +42 -0
  158. package/src/read/receipts.ts +476 -0
  159. package/src/read/types.ts +415 -0
  160. package/src/read/wallet.ts +687 -0
  161. package/src/read/works.ts +297 -0
  162. package/src/recording-extensions.ts +256 -0
  163. package/src/release-extensions.ts +311 -0
  164. package/src/release-graph.ts +219 -0
  165. package/src/share-template.ts +55 -0
  166. package/src/share.ts +261 -0
  167. package/src/transactions.ts +542 -0
  168. package/src/vault.ts +1009 -0
@@ -0,0 +1,54 @@
1
+ // Copyright (c) Miso Labs, Inc.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ // Barrel for the codegen-generated, ABI-bound bindings (BCS structs + type-safe
5
+ // Move calls). Re-exported from the package root as the `contracts` namespace.
6
+ //
7
+ // PLATFORM + EXTENSION packages. The protocol core (`miso`: Composition,
8
+ // Recording, Release, Track) generates into `@misofm/protocol` and is
9
+ // re-exported from ITS `contracts` namespace — import it from there rather than
10
+ // mirroring it here.
11
+
12
+ // Record identity/issuance and Record Shop primary-sale mechanics. The
13
+ // package-restricted witness constructor is intentionally not exported here.
14
+ export * as record from "@misofm/protocol/contracts/miso_record/record";
15
+ export * as pressing from "@misofm/protocol/contracts/miso_record/pressing";
16
+ export * as listing from "@misofm/protocol/contracts/miso_record_shop/listing";
17
+
18
+ // Generic royalty-pool primitive, raw-cap Actions, and the three safe crank plugins.
19
+ export * as royaltyPool from "@misofm/protocol/contracts/royalty_pool/pool";
20
+ export * as royaltyPoolStake from "@misofm/protocol/contracts/royalty_pool/stake";
21
+ export * as vault from "@misofm/protocol/contracts/vault/vault";
22
+ export * as compositionRoyaltyPool from "@misofm/protocol/contracts/composition_royalty_pool/composition_royalty_pool";
23
+ export * as recordingRoyaltyPool from "@misofm/protocol/contracts/recording_royalty_pool/recording_royalty_pool";
24
+ export * as partyWallet from "@misofm/protocol/contracts/party_wallet/party_wallet";
25
+ export * as routedStake from "@misofm/protocol/contracts/routed_stake/routed_stake";
26
+ export * as compositionRoutedStake from "@misofm/protocol/contracts/composition_routed_stake/composition_routed_stake";
27
+ export * as compositionRoyaltyPoolPlugin from "@misofm/protocol/contracts/composition_royalty_pool_plugin/composition_royalty_pool_plugin";
28
+ export * as recordingRoyaltyPoolPlugin from "@misofm/protocol/contracts/recording_royalty_pool_plugin/recording_royalty_pool_plugin";
29
+ export * as releaseRevenueDistributorPlugin from "@misofm/protocol/contracts/release_revenue_distributor_plugin/release_revenue_distributor_plugin";
30
+
31
+ // Cover art (the CoverArt value type + the release attachment extension).
32
+ export * as coverArt from "@misofm/protocol/contracts/cover_art/cover_art";
33
+ export * as releaseCoverArt from "@misofm/protocol/contracts/release_cover_art/release_cover_art";
34
+
35
+ // Release presentation, discovery, buyer-content, and runtime economics.
36
+ export * as genre from "@misofm/protocol/contracts/genre/genre";
37
+ export * as releaseDescription from "@misofm/protocol/contracts/release_description/release_description";
38
+ export * as releaseDspLink from "@misofm/protocol/contracts/release_dsp_link/release_dsp_link";
39
+ export * as releaseGenre from "@misofm/protocol/contracts/release_genre/release_genre";
40
+ export * as releaseKind from "@misofm/protocol/contracts/release_kind/release_kind";
41
+ export * as releaseRevenueDistributor from "@misofm/protocol/contracts/release_revenue_distributor/release_revenue_distributor";
42
+ export * as recordingAdvisory from "@misofm/protocol/contracts/recording_advisory/recording_advisory";
43
+ export * as recordingLanguage from "@misofm/protocol/contracts/recording_language/recording_language";
44
+ export * as recordingMasterReference from "@misofm/protocol/contracts/recording_master_reference/recording_master_reference";
45
+ export * as recordingStreamingTranscode from "@misofm/protocol/contracts/recording_streaming_transcode/recording_streaming_transcode";
46
+ export * as recordingPreview from "@misofm/protocol/contracts/recording_preview/recording_preview";
47
+
48
+ // Credits extensions (per-work credit stores + their role vocabularies).
49
+ export * as compositionCredits from "@misofm/protocol/contracts/composition_credits/composition_credits";
50
+ export * as compositionPartyRole from "@misofm/protocol/contracts/composition_credits/composition_party_role";
51
+ export * as recordingCredits from "@misofm/protocol/contracts/recording_credits/recording_credits";
52
+ export * as recordingPartyRole from "@misofm/protocol/contracts/recording_credits/recording_party_role";
53
+ export * as releaseCredits from "@misofm/protocol/contracts/release_credits/release_credits";
54
+ export * as releasePartyRole from "@misofm/protocol/contracts/release_credits/release_party_role";
package/src/cover.ts ADDED
@@ -0,0 +1,240 @@
1
+ // Copyright (c) Miso Labs, Inc.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ // Release cover art. A cover is a still image (optionally an animation) stored as
5
+ // a Walrus blob and referenced on-chain via `ori::WalrusData`. We build the ref
6
+ // (`ori::walrus_data::new_blob`, a raw call — ori is an external dep), wrap it in a
7
+ // `cover_art::CoverArt`, and attach it to the Release with
8
+ // `release_cover_art::set_cover` (gated by the ReleaseAdminCap).
9
+ //
10
+ // Blob ids are passed as `u256` (decimal string or bigint) — the CLI converts the
11
+ // base64url Walrus blob id to u256 before calling.
12
+ //
13
+ // The `Release` being covered is a protocol object; the cover is a platform
14
+ // opinion attached to it through the release's cap-gated `uid_mut` hook, which is
15
+ // why this module ships from `@misofm/platform` rather than the protocol SDK.
16
+
17
+ import type { ClientWithCoreApi } from "@mysten/sui/client";
18
+ import { bcs } from "@mysten/sui/bcs";
19
+ import { deriveDynamicFieldID } from "@mysten/sui/utils";
20
+ import type { Transaction, TransactionObjectArgument } from "@mysten/sui/transactions";
21
+ import type { TxThunk } from "./transactions.ts";
22
+ import { asU64, directAdminCap, invokeWithAdminCap, type AdminCapAuthority, type ObjectInput, type U64Input } from "./vault.ts";
23
+ import { OPTION_NONE, OPTION_SOME } from "./internal.ts";
24
+ import * as coverArt from "@misofm/protocol/contracts/cover_art/cover_art";
25
+ import * as releaseCoverArt from "@misofm/protocol/contracts/release_cover_art/release_cover_art";
26
+
27
+ type ReleaseAuthorityInput =
28
+ | { readonly authority: AdminCapAuthority; readonly releaseAdminCapId?: never }
29
+ | { readonly authority?: never; readonly releaseAdminCapId: string };
30
+ function releaseAuthorityOf(input: ReleaseAuthorityInput): AdminCapAuthority {
31
+ if (input.authority !== undefined) return input.authority;
32
+ if (input.releaseAdminCapId !== undefined) return directAdminCap(input.releaseAdminCapId);
33
+ throw new Error("release authority is required");
34
+ }
35
+
36
+ interface SetReleaseCoverParamsBase {
37
+ /** The `Release` object to attach the cover to. */
38
+ releaseId: ObjectInput;
39
+ /** Walrus blob id of the still cover image, as `u256` (decimal string or bigint). */
40
+ stillBlobId: bigint | string;
41
+ /** Optional animated-cover Walrus blob id (`u256`); omit for a still-only cover. */
42
+ animatedBlobId?: bigint | string | null;
43
+ /** `cover_art` package — home of the `CoverArt` value type (`cover_art::new`). */
44
+ coverArtPackageId: string;
45
+ /** `release_cover_art` package — home of the `set_cover` extension entry point. */
46
+ releaseCoverArtPackageId: string;
47
+ /** `ori` package (home of `walrus_data::new_blob` / the `WalrusData` type). */
48
+ oriPackageId: string;
49
+ }
50
+
51
+ function object(tx: Transaction, value: ObjectInput): TransactionObjectArgument {
52
+ return typeof value === "string" ? tx.object(value) : value;
53
+ }
54
+ export type SetReleaseCoverParams = SetReleaseCoverParamsBase & ReleaseAuthorityInput;
55
+
56
+ export type SetReleaseTrackCoverParams = SetReleaseCoverParams & {
57
+ /** Zero-based index in the release's flattened tracklist. */
58
+ trackIndex: U64Input;
59
+ };
60
+
61
+ function buildCover(tx: Parameters<TxThunk>[0], p: SetReleaseCoverParams) {
62
+ const walrusType = `${p.oriPackageId}::walrus_data::WalrusData`;
63
+ const blob = (id: bigint | string) =>
64
+ tx.moveCall({
65
+ target: `${p.oriPackageId}::walrus_data::new_blob`,
66
+ arguments: [tx.pure.u256(id)],
67
+ });
68
+
69
+ const still = blob(p.stillBlobId);
70
+ const animated =
71
+ p.animatedBlobId == null
72
+ ? tx.moveCall({ target: OPTION_NONE, typeArguments: [walrusType] })
73
+ : tx.moveCall({
74
+ target: OPTION_SOME,
75
+ typeArguments: [walrusType],
76
+ arguments: [blob(p.animatedBlobId)],
77
+ });
78
+
79
+ return tx.add(
80
+ coverArt._new({
81
+ package: p.coverArtPackageId,
82
+ arguments: [still, animated],
83
+ }),
84
+ );
85
+ }
86
+
87
+ /** Sets (or replaces) a release's album-level cover from Walrus blob ids. */
88
+ export function setReleaseCover(p: SetReleaseCoverParams): TxThunk {
89
+ return (tx) => {
90
+ const cover = buildCover(tx, p);
91
+ invokeWithAdminCap(tx, releaseAuthorityOf(p), {
92
+ target: `${p.releaseCoverArtPackageId}::release_cover_art::set_cover`,
93
+ arguments: [object(tx, p.releaseId), cover],
94
+ adminCapIndex: 1,
95
+ });
96
+ };
97
+ }
98
+
99
+ /** Sets (or replaces) one track's cover from Walrus blob ids. */
100
+ export function setReleaseTrackCover(p: SetReleaseTrackCoverParams): TxThunk {
101
+ return (tx) => {
102
+ const cover = buildCover(tx, p);
103
+ invokeWithAdminCap(tx, releaseAuthorityOf(p), {
104
+ target: `${p.releaseCoverArtPackageId}::release_cover_art::set_track_cover`,
105
+ arguments: [object(tx, p.releaseId), tx.pure.u64(asU64("trackIndex", p.trackIndex)), cover],
106
+ adminCapIndex: 1,
107
+ });
108
+ };
109
+ }
110
+
111
+ // ── Reads ────────────────────────────────────────────────────────────────────
112
+
113
+ /**
114
+ * A normalized reference to a cover image's Walrus data. Blob ids are returned as
115
+ * `u256` decimal strings (the on-chain form); callers convert to a base64url
116
+ * aggregator URL with `@unconfirmed/ori` (`u256ToB64Url` / `walrusDataUrl`).
117
+ */
118
+ export type CoverImageRef =
119
+ | { kind: "blob"; blobId: string }
120
+ | {
121
+ kind: "quiltPatch";
122
+ quiltId: string;
123
+ version: number;
124
+ startIndex: number;
125
+ endIndex: number;
126
+ };
127
+
128
+ /** A release's album-level cover: a still image and an optional animation. */
129
+ export interface ReleaseCoverView {
130
+ still: CoverImageRef;
131
+ animated: CoverImageRef | null;
132
+ }
133
+
134
+ // The cover is a dynamic field on the release UID under an empty `ExtensionKey()`
135
+ // (Move's implicit `dummy_field: bool` → one `false` byte). The stored value is a
136
+ // `Field { id, name: ExtensionKey, value: ReleaseCoverArt }`.
137
+ const CoverArtField = bcs.struct("Field", {
138
+ id: bcs.Address,
139
+ name: releaseCoverArt.ExtensionKey,
140
+ value: releaseCoverArt.ReleaseCoverArt,
141
+ });
142
+ const COVER_ART_KEY_BYTES = releaseCoverArt.ExtensionKey.serialize([
143
+ false,
144
+ ]).toBytes();
145
+
146
+ /** A parsed `ori::WalrusData` value (a MoveEnum: `Blob` or `QuiltPatch`). */
147
+ type ParsedWalrusData =
148
+ | { $kind: "Blob"; Blob: [string | number | bigint, unknown] }
149
+ | {
150
+ $kind: "QuiltPatch";
151
+ QuiltPatch: [string | number | bigint, number, number, number];
152
+ };
153
+
154
+ function toCoverImageRef(wd: ParsedWalrusData): CoverImageRef {
155
+ if (wd.$kind === "Blob") return { kind: "blob", blobId: String(wd.Blob[0]) };
156
+ const [quiltId, version, startIndex, endIndex] = wd.QuiltPatch;
157
+ return {
158
+ kind: "quiltPatch",
159
+ quiltId: String(quiltId),
160
+ version,
161
+ startIndex,
162
+ endIndex,
163
+ };
164
+ }
165
+
166
+ /**
167
+ * Reads a release's album-level cover (the `release_cover_art` extension), or
168
+ * `null` if no cover is attached. Derives the `ExtensionKey` dynamic field on the
169
+ * release, parses the `ReleaseCoverArt`, and returns the still (+ optional
170
+ * animation) as normalized Walrus refs for release displays.
171
+ */
172
+ export async function getReleaseCover(
173
+ client: ClientWithCoreApi,
174
+ releaseId: string,
175
+ releaseCoverArtPackageId: string,
176
+ ): Promise<ReleaseCoverView | null> {
177
+ return (
178
+ (
179
+ await getReleaseCoversByIds(client, [releaseId], releaseCoverArtPackageId)
180
+ )[releaseId] ?? null
181
+ );
182
+ }
183
+
184
+ export function parseReleaseCoverContent(
185
+ content: Uint8Array,
186
+ ): ReleaseCoverView | null {
187
+ const cover = CoverArtField.parse(content).value.cover as {
188
+ still: ParsedWalrusData;
189
+ animated: ParsedWalrusData | null;
190
+ } | null;
191
+ if (!cover) return null;
192
+
193
+ return {
194
+ still: toCoverImageRef(cover.still),
195
+ animated: cover.animated ? toCoverImageRef(cover.animated) : null,
196
+ };
197
+ }
198
+
199
+ /** Deterministic dynamic-field id for the configured release-cover package. */
200
+ export function releaseCoverFieldId(
201
+ releaseId: string,
202
+ releaseCoverArtPackageId: string,
203
+ ): string {
204
+ return deriveDynamicFieldID(
205
+ releaseId,
206
+ `${releaseCoverArtPackageId}::release_cover_art::ExtensionKey`,
207
+ COVER_ART_KEY_BYTES,
208
+ );
209
+ }
210
+
211
+ /**
212
+ * Read covers for many releases from the configured package in one Core request.
213
+ */
214
+ export async function getReleaseCoversByIds(
215
+ client: ClientWithCoreApi,
216
+ releaseIdsInput: readonly string[],
217
+ releaseCoverArtPackageId: string,
218
+ ): Promise<Partial<Record<string, ReleaseCoverView>>> {
219
+ const releaseIds = [...new Set(releaseIdsInput)];
220
+ const targets = releaseIds.map((releaseId) => ({
221
+ releaseId,
222
+ fieldId: releaseCoverFieldId(releaseId, releaseCoverArtPackageId),
223
+ }));
224
+ if (targets.length === 0) return {};
225
+
226
+ const { objects } = await client.core.getObjects({
227
+ objectIds: targets.map((target) => target.fieldId),
228
+ include: { content: true },
229
+ });
230
+ const out: Partial<Record<string, ReleaseCoverView>> = {};
231
+ objects.forEach((object, index) => {
232
+ const target = targets[index];
233
+ if (!target || object instanceof Error || !object.content) return;
234
+ const cover = parseReleaseCoverContent(object.content);
235
+ if (cover) {
236
+ out[target.releaseId] = cover;
237
+ }
238
+ });
239
+ return out;
240
+ }