@astrale-domains/shell 0.1.0-beta.8 → 0.1.1
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 +264 -64
- package/dist/domain.d.ts +225 -0
- package/dist/domain.js +96 -0
- package/dist/functions/application/index.js +1 -0
- package/dist/functions/application/list-entrypoints.js +6 -0
- package/dist/functions/group/assign-member-to-group.js +4 -0
- package/dist/functions/group/change-group-membership.js +34 -0
- package/dist/functions/group/group-membership-errors.js +23 -0
- package/dist/functions/group/index.js +2 -0
- package/dist/functions/group/unassign-member-from-group.js +4 -0
- package/dist/functions/namespace/index.js +3 -0
- package/dist/functions/namespace/link.js +30 -0
- package/dist/functions/namespace/namespace-errors.js +28 -0
- package/dist/functions/namespace/namespace-name.js +13 -0
- package/dist/functions/namespace/rename.js +31 -0
- package/dist/functions/namespace/unlink.js +21 -0
- package/dist/functions/user/index.js +2 -0
- package/dist/functions/user/invite-user.js +39 -0
- package/dist/functions/user/record-profile.js +18 -0
- package/dist/functions/view-access/assign-views.js +86 -0
- package/dist/functions/view-access/index.js +1 -0
- package/dist/functions/view-access/view-access-errors.js +28 -0
- package/dist/index.d.ts +1 -3
- package/dist/index.js +1 -3
- package/dist/integrations/index.d.ts +1 -0
- package/dist/integrations/index.js +1 -0
- package/dist/integrations/integrations.d.ts +9 -0
- package/dist/integrations/integrations.js +2 -0
- package/dist/integrations/user-invitation/index.d.ts +4 -0
- package/dist/integrations/user-invitation/index.js +3 -0
- package/dist/integrations/user-invitation/invitation-request-failure.d.ts +5 -0
- package/dist/integrations/user-invitation/invitation-request-failure.js +9 -0
- package/dist/integrations/user-invitation/invite-user-request.d.ts +6 -0
- package/dist/integrations/user-invitation/invite-user-request.js +1 -0
- package/dist/integrations/user-invitation/request-user-invitation.d.ts +8 -0
- package/dist/integrations/user-invitation/request-user-invitation.js +9 -0
- package/dist/mutations/group/index.js +1 -1
- package/dist/mutations/group/set-group-membership.js +29 -0
- package/dist/mutations/namespace/alias-write.js +22 -0
- package/dist/mutations/namespace/index.js +3 -0
- package/dist/mutations/namespace/link-alias.js +15 -0
- package/dist/mutations/namespace/rename-alias.js +13 -0
- package/dist/mutations/namespace/unlink-alias.js +13 -0
- package/dist/mutations/user/index.js +1 -1
- package/dist/mutations/user/record-user-profile.js +13 -0
- package/dist/mutations/view-access/index.js +1 -0
- package/dist/mutations/view-access/set-view-access.js +29 -0
- package/dist/providers/admin-enrollment/create-admin-enrollment-provider.js +78 -0
- package/dist/providers/admin-enrollment/decode-admin-invitation-response.js +20 -0
- package/dist/providers/admin-enrollment/index.js +1 -0
- package/dist/providers/admin-enrollment/read-enrollment-response.js +34 -0
- package/dist/queries/application/index.js +1 -0
- package/dist/queries/application/read-installed-entrypoints.js +28 -0
- package/dist/queries/group/index.js +2 -0
- package/dist/queries/group/read-group-membership-endpoints.js +18 -0
- package/dist/queries/group/read-group-membership.js +35 -0
- package/dist/queries/namespace/index.js +2 -0
- package/dist/queries/namespace/read-designated-node.js +12 -0
- package/dist/queries/namespace/read-view-aliases.js +46 -0
- package/dist/queries/user/index.js +1 -5
- package/dist/queries/user/read-user-profile.js +9 -0
- package/dist/queries/view-access/index.js +1 -0
- package/dist/queries/view-access/read-view-access.js +35 -0
- package/dist/runtime.d.ts +2175 -3
- package/dist/runtime.js +24 -14
- package/dist/schema/index.d.ts +7 -6
- package/dist/schema/index.js +6 -6
- package/dist/schema/modules/application/functions/index.d.ts +1 -0
- package/dist/schema/modules/application/functions/index.js +1 -0
- package/dist/schema/modules/application/functions/list-entrypoints.d.ts +15 -0
- package/dist/schema/modules/application/functions/list-entrypoints.js +16 -0
- package/dist/schema/modules/application/index.d.ts +2 -0
- package/dist/schema/modules/application/index.js +2 -0
- package/dist/schema/modules/application/types/entrypoint-entry.d.ts +8 -0
- package/dist/schema/modules/application/types/entrypoint-entry.js +7 -0
- package/dist/schema/modules/application/types/index.d.ts +1 -0
- package/dist/schema/modules/application/types/index.js +1 -0
- package/dist/schema/modules/group/classes/group.d.ts +24 -0
- package/dist/schema/modules/group/classes/group.js +11 -0
- package/dist/schema/modules/group/classes/index.d.ts +2 -0
- package/dist/schema/modules/group/classes/index.js +2 -0
- package/dist/schema/modules/group/classes/member-of-group.d.ts +30 -0
- package/dist/schema/modules/group/classes/member-of-group.js +12 -0
- package/dist/schema/modules/group/core/administrators-group.d.ts +1 -0
- package/dist/schema/modules/group/core/administrators-group.js +6 -0
- package/dist/schema/modules/group/core/applications-group.d.ts +1 -0
- package/dist/schema/modules/group/core/applications-group.js +6 -0
- package/dist/schema/modules/group/core/index.d.ts +3 -0
- package/dist/schema/modules/group/core/index.js +3 -0
- package/dist/schema/modules/group/core/members-group.d.ts +1 -0
- package/dist/schema/modules/group/core/members-group.js +6 -0
- package/dist/schema/modules/group/functions/assign-member-to-group.js +11 -0
- package/dist/schema/modules/group/functions/index.js +2 -0
- package/dist/schema/modules/group/functions/unassign-member-from-group.js +11 -0
- package/dist/schema/modules/group/index.d.ts +3 -0
- package/dist/schema/modules/group/index.js +3 -0
- package/dist/schema/modules/group/policies/belongs-to-group.d.ts +1 -0
- package/dist/schema/modules/group/policies/belongs-to-group.js +5 -0
- package/dist/schema/modules/group/policies/index.d.ts +1 -0
- package/dist/schema/modules/group/policies/index.js +1 -0
- package/dist/schema/modules/group/types/group-membership-input.js +8 -0
- package/dist/schema/modules/group/types/index.js +1 -0
- package/dist/schema/modules/namespace/classes/alias.d.ts +31 -0
- package/dist/schema/modules/namespace/classes/alias.js +13 -0
- package/dist/schema/modules/namespace/classes/index.d.ts +2 -0
- package/dist/schema/modules/namespace/classes/index.js +2 -0
- package/dist/schema/modules/namespace/classes/namespace.d.ts +31 -0
- package/dist/schema/modules/namespace/classes/namespace.js +10 -0
- package/dist/schema/modules/namespace/core/index.d.ts +1 -0
- package/dist/schema/modules/namespace/core/index.js +1 -0
- package/dist/schema/modules/namespace/core/views-namespace.d.ts +2 -0
- package/dist/schema/modules/namespace/core/views-namespace.js +7 -0
- package/dist/schema/modules/namespace/functions/index.js +3 -0
- package/dist/schema/modules/namespace/functions/link.js +11 -0
- package/dist/schema/modules/namespace/functions/rename.js +11 -0
- package/dist/schema/modules/namespace/functions/unlink.js +11 -0
- package/dist/schema/modules/namespace/index.d.ts +2 -0
- package/dist/schema/modules/namespace/index.js +2 -0
- package/dist/schema/modules/namespace/types/index.js +1 -0
- package/dist/schema/modules/namespace/types/namespace-name.js +7 -0
- package/dist/schema/modules/user/classes/index.d.ts +1 -0
- package/dist/schema/modules/user/classes/index.js +1 -0
- package/dist/schema/modules/user/classes/user.d.ts +34 -0
- package/dist/schema/modules/user/classes/user.js +21 -0
- package/dist/schema/modules/user/functions/index.js +2 -0
- package/dist/schema/modules/user/functions/invite-user.js +12 -0
- package/dist/schema/modules/user/functions/record-profile.js +14 -0
- package/dist/schema/modules/user/index.d.ts +2 -0
- package/dist/schema/modules/user/index.js +2 -0
- package/dist/schema/modules/user/types/index.d.ts +2 -0
- package/dist/schema/modules/user/types/index.js +2 -0
- package/dist/schema/modules/user/types/invitation-summary.d.ts +16 -0
- package/dist/schema/modules/user/types/invitation-summary.js +15 -0
- package/dist/schema/modules/user/types/user-profile.d.ts +7 -0
- package/dist/schema/modules/user/types/user-profile.js +7 -0
- package/dist/schema/modules/view-access/classes/index.d.ts +1 -0
- package/dist/schema/modules/view-access/classes/index.js +1 -0
- package/dist/schema/modules/view-access/classes/view-access.d.ts +32 -0
- package/dist/schema/modules/view-access/classes/view-access.js +18 -0
- package/dist/schema/modules/view-access/core/index.d.ts +1 -0
- package/dist/schema/modules/view-access/core/index.js +1 -0
- package/dist/schema/modules/view-access/core/view-only-group.d.ts +5 -0
- package/dist/schema/modules/view-access/core/view-only-group.js +10 -0
- package/dist/schema/modules/view-access/functions/assign-views.d.ts +16 -0
- package/dist/schema/modules/view-access/functions/assign-views.js +17 -0
- package/dist/schema/modules/view-access/functions/index.d.ts +1 -0
- package/dist/schema/modules/view-access/functions/index.js +1 -0
- package/dist/schema/modules/view-access/index.d.ts +3 -0
- package/dist/schema/modules/view-access/index.js +3 -0
- package/dist/schema/modules/view-access/types/index.js +1 -0
- package/dist/schema/modules/view-access/types/view-assignment.js +16 -0
- package/dist/schema/policies/administers-shell-class.js +11 -0
- package/dist/schema/policies/index.js +4 -0
- package/dist/schema/policies/is-self.js +5 -0
- package/dist/schema/policies/owns-class.js +7 -0
- package/dist/schema/policies/uses-function.js +5 -0
- package/dist/schema/schema.d.ts +833 -0
- package/dist/schema/schema.js +35 -0
- package/dist/schema/views/application-view.js +4 -0
- package/dist/schema/views/index.js +1 -0
- package/dist/utils/canonical-node-path/index.js +1 -0
- package/dist/utils/canonical-node-path/parse-canonical-node-path.js +8 -0
- package/package.json +50 -10
- package/dist/actions/index.js +0 -1
- package/dist/actions/user/index.js +0 -1
- package/dist/actions/user/resolve-home.js +0 -19
- package/dist/application.d.ts +0 -5
- package/dist/application.js +0 -29
- package/dist/mutations/group/membership.js +0 -21
- package/dist/mutations/space/access.js +0 -25
- package/dist/mutations/space/default.js +0 -25
- package/dist/mutations/space/index.js +0 -2
- package/dist/mutations/user/repair.js +0 -85
- package/dist/queries/space/access.js +0 -14
- package/dist/queries/space/admin.js +0 -27
- package/dist/queries/space/index.js +0 -3
- package/dist/queries/space/member.js +0 -24
- package/dist/queries/user/groups.js +0 -24
- package/dist/queries/user/home.js +0 -28
- package/dist/queries/user/owned-spaces.js +0 -24
- package/dist/queries/user/profile.js +0 -17
- package/dist/queries/user/setup.js +0 -18
- package/dist/schema/domain/index.d.ts +0 -26911
- package/dist/schema/domain/index.js +0 -40
- package/dist/schema/group/class.d.ts +0 -3288
- package/dist/schema/group/class.js +0 -7
- package/dist/schema/group/core.d.ts +0 -14
- package/dist/schema/group/core.js +0 -14
- package/dist/schema/group/index.d.ts +0 -6
- package/dist/schema/group/index.js +0 -6
- package/dist/schema/group/methods.d.ts +0 -2059
- package/dist/schema/group/methods.js +0 -21
- package/dist/schema/group/policies.d.ts +0 -1
- package/dist/schema/group/policies.js +0 -6
- package/dist/schema/group/relationships.d.ts +0 -4313
- package/dist/schema/group/relationships.js +0 -8
- package/dist/schema/group/view.d.ts +0 -3291
- package/dist/schema/group/view.js +0 -6
- package/dist/schema/home/index.d.ts +0 -1
- package/dist/schema/home/index.js +0 -1
- package/dist/schema/home/view.d.ts +0 -3
- package/dist/schema/home/view.js +0 -4
- package/dist/schema/setup/functions.d.ts +0 -47
- package/dist/schema/setup/functions.js +0 -27
- package/dist/schema/setup/index.d.ts +0 -1
- package/dist/schema/setup/index.js +0 -1
- package/dist/schema/space/class.d.ts +0 -2313
- package/dist/schema/space/class.js +0 -11
- package/dist/schema/space/core.d.ts +0 -22520
- package/dist/schema/space/core.js +0 -15
- package/dist/schema/space/index.d.ts +0 -6
- package/dist/schema/space/index.js +0 -6
- package/dist/schema/space/methods.d.ts +0 -2089
- package/dist/schema/space/methods.js +0 -30
- package/dist/schema/space/policies.d.ts +0 -4
- package/dist/schema/space/policies.js +0 -21
- package/dist/schema/space/relationships.d.ts +0 -23213
- package/dist/schema/space/relationships.js +0 -24
- package/dist/schema/space/view.d.ts +0 -2316
- package/dist/schema/space/view.js +0 -6
- package/dist/schema/user/class.d.ts +0 -1015
- package/dist/schema/user/class.js +0 -11
- package/dist/schema/user/index.d.ts +0 -3
- package/dist/schema/user/index.js +0 -3
- package/dist/schema/user/methods.d.ts +0 -14
- package/dist/schema/user/methods.js +0 -11
- package/dist/schema/user/view.d.ts +0 -1017
- package/dist/schema/user/view.js +0 -6
- package/dist/workflows/group/assign.js +0 -3
- package/dist/workflows/group/change.js +0 -31
- package/dist/workflows/group/index.js +0 -3
- package/dist/workflows/group/unassign.js +0 -3
- package/dist/workflows/index.js +0 -3
- package/dist/workflows/setup/admin.js +0 -3
- package/dist/workflows/setup/index.js +0 -5
- package/dist/workflows/setup/member.js +0 -3
- package/dist/workflows/setup/plan.js +0 -88
- package/dist/workflows/setup/provision.js +0 -57
- package/dist/workflows/setup/run.js +0 -74
- package/dist/workflows/space/access.js +0 -41
- package/dist/workflows/space/add.js +0 -3
- package/dist/workflows/space/default.js +0 -37
- package/dist/workflows/space/index.js +0 -4
- package/dist/workflows/space/remove.js +0 -3
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export const written = Object.freeze({ ok: true });
|
|
2
|
+
/** Turn the Kernel refusals an alias write expects into its Namespace refusal; others propagate. */
|
|
3
|
+
export function rejectAliasWrite(refusals) {
|
|
4
|
+
return (failure) => {
|
|
5
|
+
if (failure.outcomeKnown) {
|
|
6
|
+
if (refusals.name !== undefined &&
|
|
7
|
+
failure.code === 'MUTATION_POST_STATE_INVALID' &&
|
|
8
|
+
'rule' in failure &&
|
|
9
|
+
failure.rule === 'duplicate-key') {
|
|
10
|
+
return refused(refusals.name);
|
|
11
|
+
}
|
|
12
|
+
if (failure.code === 'MUTATION_PRECONDITION_FAILED')
|
|
13
|
+
return refused(refusals.edge);
|
|
14
|
+
if (failure.code === 'MUTATION_LOCATOR_INVALID')
|
|
15
|
+
return refused(refusals.node);
|
|
16
|
+
}
|
|
17
|
+
throw failure;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function refused(refusal) {
|
|
21
|
+
return Object.freeze({ ok: false, refusal });
|
|
22
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { defineMutation } from '@astrale-os/sdk/mutation';
|
|
2
|
+
import { rejectAliasWrite, written } from './alias-write.js';
|
|
3
|
+
/** Create the alias `name` from a Namespace to the one Node a Path reaches. */
|
|
4
|
+
export const linkAlias = defineMutation()((domain) => ({
|
|
5
|
+
id: 'shell.namespace.alias.link',
|
|
6
|
+
build({ namespace, name, target }, mutation) {
|
|
7
|
+
mutation.createEdge({ class: domain.classes.alias, source: namespace, target, slug: name });
|
|
8
|
+
},
|
|
9
|
+
project: () => written,
|
|
10
|
+
reject: rejectAliasWrite({
|
|
11
|
+
name: 'NAME_TAKEN',
|
|
12
|
+
edge: 'TARGET_ALREADY_ALIASED',
|
|
13
|
+
node: 'TARGET_NOT_FOUND',
|
|
14
|
+
}),
|
|
15
|
+
}));
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { defineMutation } from '@astrale-os/sdk/mutation';
|
|
2
|
+
import { rejectAliasWrite, written } from './alias-write.js';
|
|
3
|
+
/** Rename the alias `name` of one Node in a Namespace to `to`; the Node it names stays the same. */
|
|
4
|
+
export const renameAlias = defineMutation()((domain) => ({
|
|
5
|
+
id: 'shell.namespace.alias.rename',
|
|
6
|
+
build({ namespace, target, name, to }, mutation) {
|
|
7
|
+
const alias = { class: domain.classes.alias, source: namespace, target };
|
|
8
|
+
mutation.expect.edge({ ...alias, slug: name });
|
|
9
|
+
mutation.updateEdge({ ...alias, slug: to });
|
|
10
|
+
},
|
|
11
|
+
project: () => written,
|
|
12
|
+
reject: rejectAliasWrite({ name: 'NAME_TAKEN', edge: 'NAME_NOT_FOUND', node: 'NAME_NOT_FOUND' }),
|
|
13
|
+
}));
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { defineMutation } from '@astrale-os/sdk/mutation';
|
|
2
|
+
import { rejectAliasWrite, written } from './alias-write.js';
|
|
3
|
+
/** Delete the alias `name` of one Node in a Namespace: Edge identity gives the Node at most one. */
|
|
4
|
+
export const unlinkAlias = defineMutation()((domain) => ({
|
|
5
|
+
id: 'shell.namespace.alias.unlink',
|
|
6
|
+
build({ namespace, target, name }, mutation) {
|
|
7
|
+
const alias = { class: domain.classes.alias, source: namespace, target };
|
|
8
|
+
mutation.expect.edge({ ...alias, slug: name });
|
|
9
|
+
mutation.deleteEdge(alias);
|
|
10
|
+
},
|
|
11
|
+
project: () => written,
|
|
12
|
+
reject: rejectAliasWrite({ edge: 'NAME_NOT_FOUND', node: 'NAME_NOT_FOUND' }),
|
|
13
|
+
}));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { recordUserProfile } from './record-user-profile.js';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { defineMutation } from '@astrale-os/sdk/mutation';
|
|
2
|
+
/** Set the email and display name an account recorded about itself. */
|
|
3
|
+
export const recordUserProfile = defineMutation()((domain) => ({
|
|
4
|
+
id: 'shell.user.profile.record',
|
|
5
|
+
build({ user, set }, mutation) {
|
|
6
|
+
const props = {
|
|
7
|
+
...(set.email === undefined ? {} : { claimedEmail: set.email }),
|
|
8
|
+
...(set.displayName === undefined ? {} : { claimedName: set.displayName }),
|
|
9
|
+
};
|
|
10
|
+
mutation.updateNode({ node: { id: user }, class: domain.classes.User, props: { set: props } });
|
|
11
|
+
},
|
|
12
|
+
project: () => ({}),
|
|
13
|
+
}));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { setViewAccess } from './set-view-access.js';
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { defineMutation } from '@astrale-os/sdk/mutation';
|
|
2
|
+
/** Give and withdraw `view_access` Edges in one guarded write. */
|
|
3
|
+
export const setViewAccess = defineMutation()((domain) => ({
|
|
4
|
+
id: 'shell.view-access.set',
|
|
5
|
+
build(input, mutation) {
|
|
6
|
+
mutation.expect.query(input.guard);
|
|
7
|
+
const edge = ({ identity, view }) => ({
|
|
8
|
+
class: domain.classes.view_access,
|
|
9
|
+
source: identity,
|
|
10
|
+
target: view,
|
|
11
|
+
});
|
|
12
|
+
for (const access of input.give)
|
|
13
|
+
mutation.createEdge(edge(access));
|
|
14
|
+
for (const access of input.withdraw)
|
|
15
|
+
mutation.deleteEdge(edge(access));
|
|
16
|
+
},
|
|
17
|
+
project: () => ({ ok: true }),
|
|
18
|
+
reject: (failure) => {
|
|
19
|
+
if (failure.outcomeKnown) {
|
|
20
|
+
if (failure.code === 'MUTATION_PRECONDITION_FAILED' || failure.code === 'MUTATION_CONFLICT')
|
|
21
|
+
return { ok: false, refusal: 'CHANGED' };
|
|
22
|
+
// An endpoint reaches no Node of a Class view_access accepts.
|
|
23
|
+
if (failure.code === 'MUTATION_LOCATOR_INVALID')
|
|
24
|
+
return { ok: false, refusal: 'ENDPOINT_NOT_FOUND' };
|
|
25
|
+
throw failure;
|
|
26
|
+
}
|
|
27
|
+
return { ok: false, refusal: 'UNKNOWN' };
|
|
28
|
+
},
|
|
29
|
+
}));
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { TransportError } from '@astrale-os/sdk/client';
|
|
2
|
+
import { defineProvider } from '@astrale-os/sdk/integration';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { enrollment, InvitationRequestFailure } from '../../integrations/user-invitation/index.js';
|
|
5
|
+
import { decodeAdminInvitationResponse } from './decode-admin-invitation-response.js';
|
|
6
|
+
import { readReply } from './read-enrollment-response.js';
|
|
7
|
+
// Environment is a representation boundary, unlike already-admitted handler input.
|
|
8
|
+
const configuration = z.object({ ENROLLMENT_INGRESS_URL: z.string().url() });
|
|
9
|
+
export function enrollmentProvider(environment) {
|
|
10
|
+
const input = configuration.parse(environment);
|
|
11
|
+
const endpoint = new URL(input.ENROLLMENT_INGRESS_URL);
|
|
12
|
+
if (endpoint.protocol !== 'https:' ||
|
|
13
|
+
endpoint.username ||
|
|
14
|
+
endpoint.password ||
|
|
15
|
+
endpoint.hash ||
|
|
16
|
+
endpoint.search) {
|
|
17
|
+
throw new TypeError('Enrollment ingress must be an HTTPS URL without credentials, query or hash.');
|
|
18
|
+
}
|
|
19
|
+
const audience = endpoint.href;
|
|
20
|
+
return defineProvider(enrollment, {
|
|
21
|
+
async invite(input, execution) {
|
|
22
|
+
if (execution.auth === undefined)
|
|
23
|
+
throw new Error('Authenticated Shell invocation required.');
|
|
24
|
+
// Mint per attempt, using only the current invocation's Domain-self identity.
|
|
25
|
+
const credential = await execution.auth.mint({ audience, ttlSeconds: 60 });
|
|
26
|
+
execution.signal.throwIfAborted();
|
|
27
|
+
const remaining = execution.deadline - Date.now();
|
|
28
|
+
if (remaining <= 0) {
|
|
29
|
+
throw TransportError.invocation('Enrollment deadline exceeded.', {
|
|
30
|
+
phase: 'timeout',
|
|
31
|
+
delivery: 'not-sent',
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
// No automatic HTTP retry. A timeout after send has an unknown effect.
|
|
35
|
+
const signal = AbortSignal.any([execution.signal, AbortSignal.timeout(remaining)]);
|
|
36
|
+
const response = await fetch(audience, {
|
|
37
|
+
method: 'POST',
|
|
38
|
+
// Workers reject redirect:error before sending. Manual preserves the refusal:
|
|
39
|
+
// every 3xx is rejected below, without forwarding the proof to its Location.
|
|
40
|
+
redirect: 'manual',
|
|
41
|
+
headers: {
|
|
42
|
+
Authorization: `Bearer ${credential}`,
|
|
43
|
+
'Content-Type': 'application/json',
|
|
44
|
+
Accept: 'application/json',
|
|
45
|
+
'Idempotency-Key': input.requestId,
|
|
46
|
+
},
|
|
47
|
+
body: JSON.stringify(input),
|
|
48
|
+
signal,
|
|
49
|
+
}).catch((cause) => {
|
|
50
|
+
if (!(cause instanceof TypeError) && !(cause instanceof DOMException))
|
|
51
|
+
throw cause;
|
|
52
|
+
throw TransportError.invocation('Admin enrollment transport failed.', {
|
|
53
|
+
phase: signal.aborted ? 'timeout' : 'unknown',
|
|
54
|
+
delivery: 'unknown',
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
if (!response.ok) {
|
|
58
|
+
await response.body?.cancel();
|
|
59
|
+
if (response.status === 409)
|
|
60
|
+
throw new InvitationRequestFailure('INVITATION_CONFLICT');
|
|
61
|
+
if (response.status === 400 || response.status === 422) {
|
|
62
|
+
throw new InvitationRequestFailure('INVITATION_REJECTED');
|
|
63
|
+
}
|
|
64
|
+
// Shell admission already happened. A refused service credential is not a new
|
|
65
|
+
// authorization decision about the caller, and no upstream body is exposed.
|
|
66
|
+
throw TransportError.invocation('Admin enrollment request was not completed.', {
|
|
67
|
+
phase: 'receive',
|
|
68
|
+
delivery: 'unknown',
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
const result = decodeAdminInvitationResponse(await readReply(response));
|
|
72
|
+
if (result.requestId !== input.requestId || result.user !== input.user) {
|
|
73
|
+
throw new TypeError('Admin enrollment response does not match the request.');
|
|
74
|
+
}
|
|
75
|
+
return { invitationId: result.invitationId, user: result.user, state: result.state };
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
// Codec du JSON HTTP entrant ; la valeur admise satisfait le contrat de l'Integration.
|
|
3
|
+
const httpReply = z.object({
|
|
4
|
+
requestId: z.string().min(1),
|
|
5
|
+
invitationId: z.string().min(1),
|
|
6
|
+
user: z.string().min(1),
|
|
7
|
+
state: z.enum([
|
|
8
|
+
'pending',
|
|
9
|
+
'accepted',
|
|
10
|
+
'registering',
|
|
11
|
+
'registered',
|
|
12
|
+
'completed',
|
|
13
|
+
'cancelled',
|
|
14
|
+
'expired',
|
|
15
|
+
'failed',
|
|
16
|
+
]),
|
|
17
|
+
});
|
|
18
|
+
export function decodeAdminInvitationResponse(value) {
|
|
19
|
+
return httpReply.parse(value);
|
|
20
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { enrollmentProvider } from './create-admin-enrollment-provider.js';
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export async function readReply(response) {
|
|
2
|
+
if (!response.headers.get('content-type')?.toLowerCase().startsWith('application/json')) {
|
|
3
|
+
await response.body?.cancel();
|
|
4
|
+
throw new TypeError('Expected a JSON enrollment response.');
|
|
5
|
+
}
|
|
6
|
+
const reader = response.body?.getReader();
|
|
7
|
+
if (!reader)
|
|
8
|
+
throw new TypeError('Missing enrollment response body.');
|
|
9
|
+
const chunks = [];
|
|
10
|
+
let size = 0;
|
|
11
|
+
try {
|
|
12
|
+
for (;;) {
|
|
13
|
+
const { done, value } = await reader.read();
|
|
14
|
+
if (done)
|
|
15
|
+
break;
|
|
16
|
+
size += value.byteLength;
|
|
17
|
+
if (size > 16384) {
|
|
18
|
+
await reader.cancel();
|
|
19
|
+
throw new TypeError('Enrollment response exceeds 16 KiB.');
|
|
20
|
+
}
|
|
21
|
+
chunks.push(value);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
finally {
|
|
25
|
+
reader.releaseLock();
|
|
26
|
+
}
|
|
27
|
+
const bytes = new Uint8Array(size);
|
|
28
|
+
let offset = 0;
|
|
29
|
+
for (const chunk of chunks) {
|
|
30
|
+
bytes.set(chunk, offset);
|
|
31
|
+
offset += chunk.byteLength;
|
|
32
|
+
}
|
|
33
|
+
return JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(bytes));
|
|
34
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { readInstalledEntrypoints } from './read-installed-entrypoints.js';
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { defineQuery } from '@astrale-os/sdk/query';
|
|
2
|
+
const VIEW_MEMBER = /^view\.([A-Za-z][A-Za-z0-9_]*)$/u;
|
|
3
|
+
/**
|
|
4
|
+
* Every installed Domain entrypoint this authority can read, in origin order: each View with an
|
|
5
|
+
* outgoing `entrypoint` Edge, through its `of_domain` locator, which names the View
|
|
6
|
+
* (`view.<name>`) and reaches its Domain, with the origin and the display name.
|
|
7
|
+
*/
|
|
8
|
+
export const readInstalledEntrypoints = defineQuery()('shell.application.entrypoints', {
|
|
9
|
+
build: (_, { kernel: { View, entrypoint, of_domain } }) => View.where((view) => [
|
|
10
|
+
view.via(entrypoint, { direction: 'outgoing' }).exists(),
|
|
11
|
+
view.via(of_domain, { direction: 'outgoing' }).exists(),
|
|
12
|
+
])
|
|
13
|
+
.with((view) => ({
|
|
14
|
+
locator: view.via(of_domain, { direction: 'outgoing' }).withEdge().get(),
|
|
15
|
+
}))
|
|
16
|
+
// Shell may only traverse Views: their own values would come back withheld.
|
|
17
|
+
.pick()
|
|
18
|
+
.all({ max: 10_000 }),
|
|
19
|
+
output: (rows) => rows
|
|
20
|
+
.map(({ locator }) => {
|
|
21
|
+
const member = VIEW_MEMBER.exec(locator.$edge.member);
|
|
22
|
+
if (member === null)
|
|
23
|
+
throw new TypeError('Installed entrypoint locator names no View.');
|
|
24
|
+
const { origin, name } = locator;
|
|
25
|
+
return { origin, name, view: `${origin}:view.${member[1]}` };
|
|
26
|
+
})
|
|
27
|
+
.sort((left, right) => left.origin.localeCompare(right.origin)),
|
|
28
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Path } from '@astrale-os/sdk/graph/path';
|
|
2
|
+
import { defineQuery, Query, queryResult, } from '@astrale-os/sdk/query';
|
|
3
|
+
export const readGroupMembershipEndpoints = defineQuery()(() => ({
|
|
4
|
+
id: 'shell.group.membership.endpoints',
|
|
5
|
+
page: { size: 2 },
|
|
6
|
+
pagination: { maximumPages: 2 },
|
|
7
|
+
build: ({ member, group }) => Query.from({ nodes: [Path.id(member), Path.id(group)] }).select({
|
|
8
|
+
kind: 'nodes',
|
|
9
|
+
projection: { kind: 'reference' },
|
|
10
|
+
}),
|
|
11
|
+
project(pages, input, { ast, complete }) {
|
|
12
|
+
const ids = queryResult.completeNodeIds(pages, 'Shell membership endpoints');
|
|
13
|
+
return {
|
|
14
|
+
visible: ids.includes(input.member) && ids.includes(input.group),
|
|
15
|
+
guard: { query: ast, equals: complete },
|
|
16
|
+
};
|
|
17
|
+
},
|
|
18
|
+
}));
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Query, defineQuery, queryResult, } from '@astrale-os/sdk/query';
|
|
2
|
+
export const readGroupMembership = defineQuery()((domain) => ({
|
|
3
|
+
id: 'shell.group.membership.read',
|
|
4
|
+
build({ member, group }) {
|
|
5
|
+
const edges = Query.from({
|
|
6
|
+
edges: [domain.classes.member_of_group, domain.dependencies.kernel.classes.extends_with],
|
|
7
|
+
});
|
|
8
|
+
return edges
|
|
9
|
+
.filter({ binding: edges.source, predicate: { kind: 'identity.equal', identity: member } })
|
|
10
|
+
.filter({ binding: edges.target, predicate: { kind: 'identity.equal', identity: group } })
|
|
11
|
+
.select({
|
|
12
|
+
kind: 'edges',
|
|
13
|
+
projection: { edge: 'value', source: 'reference', target: 'reference' },
|
|
14
|
+
});
|
|
15
|
+
},
|
|
16
|
+
pagination: { maximumPages: 4 },
|
|
17
|
+
project(pages, _input, { ast, complete }) {
|
|
18
|
+
let membership = false;
|
|
19
|
+
let authority = false;
|
|
20
|
+
for (const page of pages) {
|
|
21
|
+
for (const item of queryResult.edgeItems(page, 'Shell group membership')) {
|
|
22
|
+
if (item.edge.kind !== 'value')
|
|
23
|
+
throw new TypeError('Expected an Edge value.');
|
|
24
|
+
if (item.edge.value.class === domain.classes.member_of_group.key)
|
|
25
|
+
membership = true;
|
|
26
|
+
else if (item.edge.value.class === domain.dependencies.kernel.classes.extends_with.key) {
|
|
27
|
+
authority = true;
|
|
28
|
+
}
|
|
29
|
+
else
|
|
30
|
+
throw new TypeError('Unexpected Edge Class.');
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return { membership, authority, guard: { query: ast, equals: complete } };
|
|
34
|
+
},
|
|
35
|
+
}));
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { defineQuery, Query, queryResult } from '@astrale-os/sdk/query';
|
|
2
|
+
/**
|
|
3
|
+
* The one Node a Path designates for the authority that reads it, or nothing: a Node it cannot
|
|
4
|
+
* see, and a name no alias holds, read the same.
|
|
5
|
+
*/
|
|
6
|
+
export const readDesignatedNode = defineQuery()(() => ({
|
|
7
|
+
id: 'shell.namespace.designated-node',
|
|
8
|
+
page: { size: 1 },
|
|
9
|
+
pagination: { maximumPages: 1 },
|
|
10
|
+
build: ({ path }) => Query.from({ nodes: [path] }).select({ kind: 'nodes', projection: { kind: 'reference' } }),
|
|
11
|
+
project: (pages) => queryResult.completeNodeIds(pages, 'Shell designated Node')[0],
|
|
12
|
+
}));
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Path } from '@astrale-os/sdk/graph/path';
|
|
2
|
+
import { defineQuery, Query, queryResult, } from '@astrale-os/sdk/query';
|
|
3
|
+
/** `/:shell.astrale.ai:core.views`, the Namespace `/v/<name>` reads. */
|
|
4
|
+
const viewsNamespace = (origin) => Path.project({ origin: origin, kind: 'core', name: 'views' });
|
|
5
|
+
/**
|
|
6
|
+
* The `views` Namespace, which names are given in; nothing on a Shell release without it. It stays
|
|
7
|
+
* V6: its v7 target is a Core path, which a PostgreSQL Host refuses as a Path with layers.
|
|
8
|
+
*/
|
|
9
|
+
export const readViewsNamespace = defineQuery()((domain) => ({
|
|
10
|
+
id: 'shell.namespace.views',
|
|
11
|
+
page: { size: 1 },
|
|
12
|
+
pagination: { maximumPages: 1 },
|
|
13
|
+
build: () => Query.from({ nodes: [viewsNamespace(domain.origin)] }).select({
|
|
14
|
+
kind: 'nodes',
|
|
15
|
+
projection: { kind: 'reference' },
|
|
16
|
+
}),
|
|
17
|
+
project: (pages) => queryResult.completeNodeIds(pages, 'Shell views Namespace')[0],
|
|
18
|
+
}));
|
|
19
|
+
/** Every name of the `views` Namespace, with the View it opens, in name order. */
|
|
20
|
+
export const readViewAliases = defineQuery()((domain) => ({
|
|
21
|
+
id: 'shell.namespace.view-aliases',
|
|
22
|
+
page: { size: 256 },
|
|
23
|
+
pagination: { maximumPages: 8 },
|
|
24
|
+
build: () => {
|
|
25
|
+
const named = Query.from({ nodes: [viewsNamespace(domain.origin)] }).expand({
|
|
26
|
+
via: [domain.classes.alias],
|
|
27
|
+
direction: 'outgoing',
|
|
28
|
+
});
|
|
29
|
+
return named.select({
|
|
30
|
+
kind: 'edges',
|
|
31
|
+
binding: named.edge,
|
|
32
|
+
projection: { edge: 'value', source: 'reference', target: 'reference' },
|
|
33
|
+
});
|
|
34
|
+
},
|
|
35
|
+
project(pages) {
|
|
36
|
+
const aliases = [];
|
|
37
|
+
for (const page of pages) {
|
|
38
|
+
for (const item of queryResult.edgeItems(page, 'Shell view aliases')) {
|
|
39
|
+
if (item.edge.kind !== 'value' || item.edge.value.slug === undefined)
|
|
40
|
+
continue;
|
|
41
|
+
aliases.push({ view: item.edge.value.target, name: item.edge.value.slug });
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return aliases.sort((a, b) => a.name.localeCompare(b.name));
|
|
45
|
+
},
|
|
46
|
+
}));
|
|
@@ -1,5 +1 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export { observeHome } from './home.js';
|
|
3
|
-
export { observeOwnedSpaces } from './owned-spaces.js';
|
|
4
|
-
export { readUserProfile } from './profile.js';
|
|
5
|
-
export { observeUserSetup } from './setup.js';
|
|
1
|
+
export { readUserProfile } from './read-user-profile.js';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { defineQuery } from '@astrale-os/sdk/query';
|
|
2
|
+
/** The email and display name one account recorded; nothing for an account it cannot read. */
|
|
3
|
+
export const readUserProfile = defineQuery()('shell.user.profile', {
|
|
4
|
+
build: ({ user }, { User }) => User.where((account) => account.id.eq(user)).first(),
|
|
5
|
+
output: (account) => ({
|
|
6
|
+
...(account?.claimedEmail === undefined ? {} : { email: account.claimedEmail }),
|
|
7
|
+
...(account?.claimedName === undefined ? {} : { displayName: account.claimedName }),
|
|
8
|
+
}),
|
|
9
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { readViewAccess } from './read-view-access.js';
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Path } from '@astrale-os/sdk/graph/path';
|
|
2
|
+
import { Query, defineQuery, queryResult, } from '@astrale-os/sdk/query';
|
|
3
|
+
/** Every `view_access` Edge from the identities, kept whole as a guard for the write it decides. */
|
|
4
|
+
export const readViewAccess = defineQuery()((domain) => ({
|
|
5
|
+
id: 'shell.view-access.read',
|
|
6
|
+
build({ identities }) {
|
|
7
|
+
const held = Query.from({
|
|
8
|
+
nodes: [Path.id(identities[0]), ...identities.slice(1).map((identity) => Path.id(identity))],
|
|
9
|
+
}).expand({
|
|
10
|
+
via: [domain.classes.view_access],
|
|
11
|
+
direction: 'outgoing',
|
|
12
|
+
});
|
|
13
|
+
return held.select({
|
|
14
|
+
kind: 'edges',
|
|
15
|
+
binding: held.edge,
|
|
16
|
+
projection: { edge: 'value', source: 'reference', target: 'reference' },
|
|
17
|
+
});
|
|
18
|
+
},
|
|
19
|
+
// A guard checks at most 4,096 Edges: 16 pages of 256.
|
|
20
|
+
pagination: { maximumPages: 16 },
|
|
21
|
+
project(pages, _input, { ast, complete }) {
|
|
22
|
+
const given = new Map();
|
|
23
|
+
for (const page of pages) {
|
|
24
|
+
for (const item of queryResult.edgeItems(page, 'Shell view access')) {
|
|
25
|
+
if (item.edge.kind !== 'value')
|
|
26
|
+
throw new TypeError('Expected an Edge value.');
|
|
27
|
+
const { source, target } = item.edge.value;
|
|
28
|
+
const views = given.get(source) ?? new Set();
|
|
29
|
+
views.add(target);
|
|
30
|
+
given.set(source, views);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return { given, guard: { query: ast, equals: complete } };
|
|
34
|
+
},
|
|
35
|
+
}));
|