@sanity-labs/client-fake-for-test 0.9.1 → 0.11.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 +3 -1
- package/dist/index.cjs +51 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -1
- package/dist/index.d.ts +10 -1
- package/dist/index.js +51 -18
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -108,7 +108,8 @@ export declare interface CreateTestClientOptions {
|
|
|
108
108
|
/**
|
|
109
109
|
* Opt-in access-control simulation. When set, the client exposes
|
|
110
110
|
* `request({ url })` that serves `/users/me` (host-aware), the
|
|
111
|
-
* configured ACL path,
|
|
111
|
+
* configured ACL path, `/projects/<projectId>` (members), and
|
|
112
|
+
* `/projects/<projectId>/users/<ids>`,
|
|
112
113
|
* AND every mutation is gated through the supplied `grants`
|
|
113
114
|
* — writes that the grants don't permit throw a `WriteAccessDeniedError`
|
|
114
115
|
* shaped like Sanity's 403. Lets tests demonstrate the full
|
|
@@ -594,6 +595,14 @@ export declare type PatchOp =
|
|
|
594
595
|
|
|
595
596
|
export declare type Perspective = "raw" | "published" | "drafts" | string[];
|
|
596
597
|
|
|
598
|
+
/** One row of the `/projects/<projectId>` members response. */
|
|
599
|
+
export declare interface ProjectMemberRecord {
|
|
600
|
+
id: string;
|
|
601
|
+
isRobot?: boolean;
|
|
602
|
+
/** Project-scoped roles in canonical order, independent of registration order. */
|
|
603
|
+
roles?: NonNullable<CurrentUser["roles"]>;
|
|
604
|
+
}
|
|
605
|
+
|
|
597
606
|
/**
|
|
598
607
|
* A row served by the project-users directory endpoint
|
|
599
608
|
* (`/projects/<projectId>/users/<id>`), mirroring Sanity's project user
|
package/dist/index.d.ts
CHANGED
|
@@ -108,7 +108,8 @@ export declare interface CreateTestClientOptions {
|
|
|
108
108
|
/**
|
|
109
109
|
* Opt-in access-control simulation. When set, the client exposes
|
|
110
110
|
* `request({ url })` that serves `/users/me` (host-aware), the
|
|
111
|
-
* configured ACL path,
|
|
111
|
+
* configured ACL path, `/projects/<projectId>` (members), and
|
|
112
|
+
* `/projects/<projectId>/users/<ids>`,
|
|
112
113
|
* AND every mutation is gated through the supplied `grants`
|
|
113
114
|
* — writes that the grants don't permit throw a `WriteAccessDeniedError`
|
|
114
115
|
* shaped like Sanity's 403. Lets tests demonstrate the full
|
|
@@ -594,6 +595,14 @@ export declare type PatchOp =
|
|
|
594
595
|
|
|
595
596
|
export declare type Perspective = "raw" | "published" | "drafts" | string[];
|
|
596
597
|
|
|
598
|
+
/** One row of the `/projects/<projectId>` members response. */
|
|
599
|
+
export declare interface ProjectMemberRecord {
|
|
600
|
+
id: string;
|
|
601
|
+
isRobot?: boolean;
|
|
602
|
+
/** Project-scoped roles in canonical order, independent of registration order. */
|
|
603
|
+
roles?: NonNullable<CurrentUser["roles"]>;
|
|
604
|
+
}
|
|
605
|
+
|
|
597
606
|
/**
|
|
598
607
|
* A row served by the project-users directory endpoint
|
|
599
608
|
* (`/projects/<projectId>/users/<id>`), mirroring Sanity's project user
|
package/dist/index.js
CHANGED
|
@@ -62,6 +62,28 @@ function defaultAclPath(projectId, dataset) {
|
|
|
62
62
|
function projectUserOf(identity, projectId) {
|
|
63
63
|
return identity.usersByProject?.[projectId] ?? identity.user;
|
|
64
64
|
}
|
|
65
|
+
function consistentProjectRecords(args) {
|
|
66
|
+
const byId = /* @__PURE__ */ new Map();
|
|
67
|
+
for (const record of args.records) {
|
|
68
|
+
const existing = byId.get(record.id);
|
|
69
|
+
if (existing !== void 0 && JSON.stringify(existing) !== JSON.stringify(record))
|
|
70
|
+
throw fakeError(
|
|
71
|
+
`request(): two registered identities resolve to project user "${record.id}" in project "${args.projectId}" with different records. The project records cannot be served unambiguously \u2014 fix the identity registrations so duplicate ids carry identical records.`
|
|
72
|
+
);
|
|
73
|
+
byId.set(record.id, record);
|
|
74
|
+
}
|
|
75
|
+
return byId;
|
|
76
|
+
}
|
|
77
|
+
function canonicalProjectRoles(roles) {
|
|
78
|
+
return roles.map((role) => ({
|
|
79
|
+
name: role.name,
|
|
80
|
+
...role.title !== void 0 ? { title: role.title } : {},
|
|
81
|
+
...role.description !== void 0 ? { description: role.description } : {}
|
|
82
|
+
})).toSorted((left, right) => {
|
|
83
|
+
const leftKey = JSON.stringify(left), rightKey = JSON.stringify(right);
|
|
84
|
+
return leftKey < rightKey ? -1 : leftKey > rightKey ? 1 : 0;
|
|
85
|
+
});
|
|
86
|
+
}
|
|
65
87
|
function serveProjectUsers(args) {
|
|
66
88
|
const { requestedIds, pathProjectId, identities } = args, toRecord = (identity) => {
|
|
67
89
|
const projectUser = projectUserOf(identity, pathProjectId);
|
|
@@ -71,33 +93,38 @@ function serveProjectUsers(args) {
|
|
|
71
93
|
displayName: projectUser.name ?? projectUser.id,
|
|
72
94
|
...projectUser.email !== void 0 ? { email: projectUser.email } : {}
|
|
73
95
|
};
|
|
74
|
-
}, byId =
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
throw fakeError(
|
|
79
|
-
`request(): two registered identities resolve to project user "${resolvedId}" in project "${pathProjectId}" with different records. The directory cannot answer unambiguously \u2014 fix the identity registrations so duplicate ids carry identical records.`
|
|
80
|
-
);
|
|
81
|
-
byId.set(resolvedId, identity);
|
|
82
|
-
}
|
|
83
|
-
const ids = requestedIds.split(","), [firstId] = ids;
|
|
96
|
+
}, byId = consistentProjectRecords({
|
|
97
|
+
projectId: pathProjectId,
|
|
98
|
+
records: identities.map(toRecord)
|
|
99
|
+
}), ids = requestedIds.split(","), [firstId] = ids;
|
|
84
100
|
if (ids.length === 1 && firstId !== void 0) {
|
|
85
|
-
const
|
|
86
|
-
if (
|
|
101
|
+
const record = byId.get(firstId);
|
|
102
|
+
if (record === void 0)
|
|
87
103
|
throw Object.assign(
|
|
88
104
|
fakeError(
|
|
89
105
|
`request(): no registered identity resolves to project user "${firstId}" in project "${pathProjectId}". Register it via createTestClient({ identities }) or addIdentity().`
|
|
90
106
|
),
|
|
91
107
|
{ statusCode: 404 }
|
|
92
108
|
);
|
|
93
|
-
return
|
|
109
|
+
return record;
|
|
94
110
|
}
|
|
95
111
|
return ids.flatMap((id) => {
|
|
96
|
-
const
|
|
97
|
-
return
|
|
112
|
+
const record = byId.get(id);
|
|
113
|
+
return record === void 0 ? [] : [record];
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
const PROJECT_USERS_PATH = /^\/projects\/([^/]+)\/users\/([^/]+)$/, PROJECT_PATH = /^\/projects\/([^/]+)$/;
|
|
117
|
+
function serveProject(args) {
|
|
118
|
+
const records = args.identities.map((identity) => {
|
|
119
|
+
const projectUser = projectUserOf(identity, args.pathProjectId);
|
|
120
|
+
return {
|
|
121
|
+
id: projectUser.id,
|
|
122
|
+
...projectUser.id.startsWith("p-") ? { isRobot: !0 } : {},
|
|
123
|
+
...projectUser.roles !== void 0 ? { roles: canonicalProjectRoles(projectUser.roles) } : {}
|
|
124
|
+
};
|
|
98
125
|
});
|
|
126
|
+
return { members: [...consistentProjectRecords({ projectId: args.pathProjectId, records }).values()] };
|
|
99
127
|
}
|
|
100
|
-
const PROJECT_USERS_PATH = /^\/projects\/([^/]+)\/users\/([^/]+)$/;
|
|
101
128
|
function makeRequestImpl(ctx) {
|
|
102
129
|
const { getAccess, projectId, dataset, useProjectHostname, listIdentities } = ctx;
|
|
103
130
|
return async (opts) => {
|
|
@@ -113,8 +140,14 @@ function makeRequestImpl(ctx) {
|
|
|
113
140
|
pathProjectId,
|
|
114
141
|
identities: listIdentities()
|
|
115
142
|
});
|
|
143
|
+
const project = path?.match(PROJECT_PATH), [, membersProjectId] = project ?? [];
|
|
144
|
+
if (membersProjectId !== void 0)
|
|
145
|
+
return serveProject({
|
|
146
|
+
pathProjectId: membersProjectId,
|
|
147
|
+
identities: listIdentities()
|
|
148
|
+
});
|
|
116
149
|
throw fakeError(
|
|
117
|
-
`request(): no handler for path "${path}". With access control configured, the fake serves "/users/me", "${aclPath}", and "/projects/<projectId>/users/<ids>". For any other endpoint, register a stub (client.stub("request", fn)) or intercept the HTTP call with a tool like MSW.`
|
|
150
|
+
`request(): no handler for path "${path}". With access control configured, the fake serves "/users/me", "${aclPath}", "/projects/<projectId>" (members), and "/projects/<projectId>/users/<ids>". For any other endpoint, register a stub (client.stub("request", fn)) or intercept the HTTP call with a tool like MSW.`
|
|
118
151
|
);
|
|
119
152
|
};
|
|
120
153
|
}
|
|
@@ -825,7 +858,7 @@ const ISSUES_URL = "https://github.com/sanity-labs/client-fake-for-test/issues",
|
|
|
825
858
|
},
|
|
826
859
|
request: {
|
|
827
860
|
what: "generic HTTP request hook \u2014 only wired up when the handle has an acting identity",
|
|
828
|
-
workaround: "Create the client with `accessControl`, or bind a registered token (`identities` + `token` / `withConfig({ token })`), to enable request() (it serves /users/me, the ACL path, and /projects/<projectId>/users/<ids>). Or register a stub."
|
|
861
|
+
workaround: "Create the client with `accessControl`, or bind a registered token (`identities` + `token` / `withConfig({ token })`), to enable request() (it serves /users/me, the ACL path, /projects/<projectId>, and /projects/<projectId>/users/<ids>). Or register a stub."
|
|
829
862
|
},
|
|
830
863
|
dryRun: {
|
|
831
864
|
what: "not a method \u2014 did you mean the `{ dryRun }` mutation option?",
|