@misofm/partyos 0.1.0 → 0.3.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/README.md +62 -9
- package/dist/client.d.ts +8 -6
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +25 -12
- package/dist/client.js.map +1 -1
- package/dist/deployments.d.ts +9 -1
- package/dist/deployments.d.ts.map +1 -1
- package/dist/deployments.js +14 -1
- package/dist/deployments.js.map +1 -1
- package/dist/errors.d.ts +9 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +16 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +1 -1
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +3 -1
- package/dist/internal.js.map +1 -1
- package/dist/queries.d.ts +10 -16
- package/dist/queries.d.ts.map +1 -1
- package/dist/queries.js +41 -79
- package/dist/queries.js.map +1 -1
- package/dist/types.d.ts +12 -7
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +15 -1
- package/dist/types.js.map +1 -1
- package/package.json +8 -1
- package/src/client.ts +28 -12
- package/src/deployments.ts +17 -1
- package/src/errors.ts +18 -0
- package/src/index.ts +1 -0
- package/src/internal.ts +4 -2
- package/src/queries.ts +71 -88
- package/src/types.ts +13 -10
package/src/queries.ts
CHANGED
|
@@ -2,11 +2,23 @@
|
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
4
|
// Typed reads: fetch an on-chain object (or dynamic field) via the Core API, BCS-parse
|
|
5
|
-
// it through the generated struct, and map to the public camelCase types.
|
|
5
|
+
// it through the generated struct, and map to the public camelCase types. Every read
|
|
6
|
+
// requires the `SuiClient` service (see `@misofm/effect`) instead of taking a client
|
|
7
|
+
// parameter; not-found and type-mismatch paths are typed failures, not thrown errors.
|
|
6
8
|
|
|
7
|
-
import
|
|
9
|
+
import { Effect, Option, Stream } from "effect";
|
|
8
10
|
import { deriveDynamicFieldID, deriveObjectID, normalizeStructTag } from "@mysten/sui/utils";
|
|
9
11
|
import { bcs } from "@mysten/sui/bcs";
|
|
12
|
+
import {
|
|
13
|
+
assertObjectType,
|
|
14
|
+
decodeBcs,
|
|
15
|
+
getObjectContent,
|
|
16
|
+
getObjectsContent,
|
|
17
|
+
getOptionalObjectContent,
|
|
18
|
+
listDynamicFields,
|
|
19
|
+
type SuiClient,
|
|
20
|
+
} from "@misofm/effect";
|
|
21
|
+
import type { BcsDecodeError, ObjectNotFoundError, ObjectTypeMismatchError, SuiRpcError } from "@misofm/effect";
|
|
10
22
|
import {
|
|
11
23
|
MembershipKey as MembershipKeyBcs,
|
|
12
24
|
Party as PartyBcs,
|
|
@@ -14,87 +26,63 @@ import {
|
|
|
14
26
|
PendingMembershipKey as PendingMembershipKeyBcs,
|
|
15
27
|
} from "./contracts/partyos/party.ts";
|
|
16
28
|
import { keyBytes, mapParty } from "./internal.ts";
|
|
17
|
-
import
|
|
18
|
-
|
|
19
|
-
/** True for the Core API's "object does not exist" error (a missing dynamic field). */
|
|
20
|
-
export function isNotFound(e: unknown): boolean {
|
|
21
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
22
|
-
return /not\s*found|does not exist|no object/i.test(msg);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Reads an object's BCS content, or null when it does not exist. A missing
|
|
27
|
-
* object (e.g. an unset dynamic field) surfaces as a thrown "not found" from the
|
|
28
|
-
* Core API, not an empty result, so optional reads can treat it as absence.
|
|
29
|
-
*/
|
|
30
|
-
export async function getObjectContent(client: ClientWithCoreApi, objectId: string): Promise<Uint8Array | null> {
|
|
31
|
-
try {
|
|
32
|
-
const { object } = await client.core.getObject({ objectId, include: { content: true } });
|
|
33
|
-
return object?.content ?? null;
|
|
34
|
-
} catch (e) {
|
|
35
|
-
if (isNotFound(e)) return null;
|
|
36
|
-
throw e;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
29
|
+
import { Party } from "./types.ts";
|
|
39
30
|
|
|
40
31
|
/** The `Party` struct tag of one partyos deployment. */
|
|
41
32
|
export function partyType(partyPackageId: string): string {
|
|
42
33
|
return normalizeStructTag(`${partyPackageId}::party::Party`);
|
|
43
34
|
}
|
|
44
35
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
(type?.endsWith("::party::Party") ? " (a Party from a different partyos deployment)" : ""),
|
|
54
|
-
);
|
|
55
|
-
}
|
|
36
|
+
/** Decodes one object's BCS content into a `Party`, mapping the generated parse output first. */
|
|
37
|
+
function decodeParty(objectId: string, content: Uint8Array, expectedType: string) {
|
|
38
|
+
return decodeBcs(
|
|
39
|
+
{ parse: (bytes) => mapParty(objectId, PartyBcs.parse(bytes)) },
|
|
40
|
+
Party,
|
|
41
|
+
content,
|
|
42
|
+
{ type: expectedType, objectId },
|
|
43
|
+
);
|
|
56
44
|
}
|
|
57
45
|
|
|
58
46
|
/** Fetches and parses a shared `Party` object of this deployment's partyos package. */
|
|
59
|
-
export
|
|
60
|
-
client: ClientWithCoreApi,
|
|
47
|
+
export const getPartyById = Effect.fn("getPartyById")(function* (
|
|
61
48
|
partyId: string,
|
|
62
49
|
partyPackageId: string,
|
|
63
|
-
):
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
50
|
+
): Effect.fn.Return<
|
|
51
|
+
Party,
|
|
52
|
+
ObjectNotFoundError | ObjectTypeMismatchError | BcsDecodeError | SuiRpcError,
|
|
53
|
+
SuiClient
|
|
54
|
+
> {
|
|
55
|
+
const expected = partyType(partyPackageId);
|
|
56
|
+
const object = yield* getObjectContent(partyId);
|
|
57
|
+
// A Party from another partyos package (a previous generation) parses
|
|
58
|
+
// identically but no deployed package accepts it, so the object's type is
|
|
59
|
+
// checked against the deployment before its content is trusted.
|
|
60
|
+
yield* assertObjectType(partyId, normalizeStructTag(object.type), expected);
|
|
61
|
+
return yield* decodeParty(partyId, object.content, expected);
|
|
62
|
+
});
|
|
75
63
|
|
|
76
64
|
/**
|
|
77
65
|
* Fetches and parses multiple shared `Party` objects in one Core request.
|
|
78
66
|
* Missing objects are skipped; an object of another type is an error.
|
|
79
67
|
*/
|
|
80
|
-
export
|
|
81
|
-
client: ClientWithCoreApi,
|
|
68
|
+
export const getPartiesByIds = Effect.fn("getPartiesByIds")(function* (
|
|
82
69
|
partyIds: readonly string[],
|
|
83
70
|
partyPackageId: string,
|
|
84
|
-
):
|
|
71
|
+
): Effect.fn.Return<
|
|
72
|
+
Partial<Record<string, Party>>,
|
|
73
|
+
ObjectTypeMismatchError | BcsDecodeError | SuiRpcError,
|
|
74
|
+
SuiClient
|
|
75
|
+
> {
|
|
85
76
|
if (partyIds.length === 0) return {};
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
include: { content: true },
|
|
89
|
-
});
|
|
77
|
+
const expected = partyType(partyPackageId);
|
|
78
|
+
const contentById = yield* getObjectsContent([...new Set(partyIds)]);
|
|
90
79
|
const parties: Partial<Record<string, Party>> = {};
|
|
91
|
-
for (const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
parties[obj.objectId] = mapParty(obj.objectId, PartyBcs.parse(obj.content));
|
|
80
|
+
for (const [objectId, { content, type }] of contentById) {
|
|
81
|
+
yield* assertObjectType(objectId, normalizeStructTag(type), expected);
|
|
82
|
+
parties[objectId] = yield* decodeParty(objectId, content, expected);
|
|
95
83
|
}
|
|
96
84
|
return parties;
|
|
97
|
-
}
|
|
85
|
+
});
|
|
98
86
|
|
|
99
87
|
/** Derives a party's `PartyAdminCap` id (it is a `derived_object` off the party UID). */
|
|
100
88
|
export function derivePartyAdminCapId(partyId: string, partyPackageId: string): string {
|
|
@@ -108,38 +96,33 @@ export function derivePartyAdminCapId(partyId: string, partyPackageId: string):
|
|
|
108
96
|
// === Group membership reads ===
|
|
109
97
|
|
|
110
98
|
/** Collects the ids stored in every dynamic-field key of `parentId` whose type ends with `keySuffix`. */
|
|
111
|
-
|
|
112
|
-
client: ClientWithCoreApi,
|
|
99
|
+
function collectKeyIds(
|
|
113
100
|
parentId: string,
|
|
114
101
|
keySuffix: string,
|
|
115
102
|
codec: { parse(bytes: Uint8Array): readonly unknown[] },
|
|
116
|
-
):
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
127
|
-
cursor = page.hasNextPage ? page.cursor : null;
|
|
128
|
-
} while (cursor);
|
|
129
|
-
return ids;
|
|
103
|
+
): Effect.Effect<string[], SuiRpcError, SuiClient> {
|
|
104
|
+
return listDynamicFields(parentId).pipe(
|
|
105
|
+
Stream.filter((f) => typeof f.name?.type === "string" && f.name.type.endsWith(keySuffix) && f.name.bcs != null),
|
|
106
|
+
Stream.map((f) => {
|
|
107
|
+
const [id] = codec.parse(keyBytes(f.name!.bcs));
|
|
108
|
+
return String(id);
|
|
109
|
+
}),
|
|
110
|
+
Stream.runCollect,
|
|
111
|
+
Effect.map((chunk) => Array.from(chunk)),
|
|
112
|
+
);
|
|
130
113
|
}
|
|
131
114
|
|
|
132
115
|
/**
|
|
133
116
|
* The group ids a party currently belongs to, read from its `MembershipKey`
|
|
134
117
|
* dynamic fields (the member-side record). No indexer required.
|
|
135
118
|
*/
|
|
136
|
-
export
|
|
137
|
-
return collectKeyIds(
|
|
119
|
+
export function getMemberships(partyId: string): Effect.Effect<string[], SuiRpcError, SuiClient> {
|
|
120
|
+
return collectKeyIds(partyId, "::party::MembershipKey", MembershipKeyBcs);
|
|
138
121
|
}
|
|
139
122
|
|
|
140
123
|
/** The member ids invited to a group but not yet accepted (its `PendingInviteKey` fields). */
|
|
141
|
-
export
|
|
142
|
-
return collectKeyIds(
|
|
124
|
+
export function getPendingInvites(groupId: string): Effect.Effect<string[], SuiRpcError, SuiClient> {
|
|
125
|
+
return collectKeyIds(groupId, "::party::PendingInviteKey", PendingInviteKeyBcs);
|
|
143
126
|
}
|
|
144
127
|
|
|
145
128
|
/**
|
|
@@ -147,21 +130,21 @@ export async function getPendingInvites(client: ClientWithCoreApi, groupId: stri
|
|
|
147
130
|
* This reads the member-side `PendingMembershipKey` inbox index, so it only
|
|
148
131
|
* enumerates the target party's dynamic fields.
|
|
149
132
|
*/
|
|
150
|
-
export
|
|
151
|
-
return collectKeyIds(
|
|
133
|
+
export function getPendingMemberships(partyId: string): Effect.Effect<string[], SuiRpcError, SuiClient> {
|
|
134
|
+
return collectKeyIds(partyId, "::party::PendingMembershipKey", PendingMembershipKeyBcs);
|
|
152
135
|
}
|
|
153
136
|
|
|
154
137
|
/** Whether `memberId` currently holds a membership record for `groupId`. */
|
|
155
|
-
export
|
|
156
|
-
client: ClientWithCoreApi,
|
|
138
|
+
export const isMember = Effect.fn("isMember")(function* (
|
|
157
139
|
memberId: string,
|
|
158
140
|
groupId: string,
|
|
159
141
|
partyPackageId: string,
|
|
160
|
-
):
|
|
142
|
+
): Effect.fn.Return<boolean, SuiRpcError, SuiClient> {
|
|
161
143
|
const fieldId = deriveDynamicFieldID(
|
|
162
144
|
memberId,
|
|
163
145
|
`${partyPackageId}::party::MembershipKey`,
|
|
164
146
|
MembershipKeyBcs.serialize([groupId]).toBytes(),
|
|
165
147
|
);
|
|
166
|
-
|
|
167
|
-
|
|
148
|
+
const content = yield* getOptionalObjectContent(fieldId);
|
|
149
|
+
return Option.isSome(content);
|
|
150
|
+
});
|
package/src/types.ts
CHANGED
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
// Copyright (c) Miso Labs, Inc.
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
|
-
// Public, camelCase result types. The
|
|
5
|
-
// (snake_case, Move-shaped) parse output into
|
|
4
|
+
// Public, camelCase result types. The mapper in ./internal.ts turns the generated
|
|
5
|
+
// (snake_case, Move-shaped) parse output into the plain shape `Party` decodes from.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
import { Schema } from "effect";
|
|
8
8
|
|
|
9
|
-
export
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
export const PartyKind = Schema.Literals(["individual", "group"]);
|
|
10
|
+
export type PartyKind = typeof PartyKind.Type;
|
|
11
|
+
|
|
12
|
+
export class Party extends Schema.Class<Party>("@misofm/partyos/Party")({
|
|
13
|
+
id: Schema.String,
|
|
14
|
+
kind: PartyKind,
|
|
12
15
|
/** Human-readable name (not verified). */
|
|
13
|
-
name:
|
|
16
|
+
name: Schema.String,
|
|
14
17
|
/** Member party ids — present only when `kind === "group"`. */
|
|
15
|
-
members
|
|
18
|
+
members: Schema.optional(Schema.Array(Schema.String)),
|
|
16
19
|
/** Unix ms when the party was created. */
|
|
17
|
-
createdAtMs:
|
|
18
|
-
}
|
|
20
|
+
createdAtMs: Schema.Number,
|
|
21
|
+
}) {}
|