@assemora/auth 0.1.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 (55) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +69 -0
  3. package/dist/actors.d.ts +20 -0
  4. package/dist/actors.d.ts.map +1 -0
  5. package/dist/actors.js +37 -0
  6. package/dist/actors.js.map +1 -0
  7. package/dist/authorization.d.ts +36 -0
  8. package/dist/authorization.d.ts.map +1 -0
  9. package/dist/authorization.js +93 -0
  10. package/dist/authorization.js.map +1 -0
  11. package/dist/commands.d.ts +232 -0
  12. package/dist/commands.d.ts.map +1 -0
  13. package/dist/commands.js +390 -0
  14. package/dist/commands.js.map +1 -0
  15. package/dist/credentials.d.ts +16 -0
  16. package/dist/credentials.d.ts.map +1 -0
  17. package/dist/credentials.js +43 -0
  18. package/dist/credentials.js.map +1 -0
  19. package/dist/index.d.ts +35 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +35 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/models.d.ts +136 -0
  24. package/dist/models.d.ts.map +1 -0
  25. package/dist/models.js +92 -0
  26. package/dist/models.js.map +1 -0
  27. package/dist/module.d.ts +39 -0
  28. package/dist/module.d.ts.map +1 -0
  29. package/dist/module.js +95 -0
  30. package/dist/module.js.map +1 -0
  31. package/dist/ownership.d.ts +67 -0
  32. package/dist/ownership.d.ts.map +1 -0
  33. package/dist/ownership.js +51 -0
  34. package/dist/ownership.js.map +1 -0
  35. package/dist/permissions.d.ts +40 -0
  36. package/dist/permissions.d.ts.map +1 -0
  37. package/dist/permissions.js +86 -0
  38. package/dist/permissions.js.map +1 -0
  39. package/dist/policies.d.ts +120 -0
  40. package/dist/policies.d.ts.map +1 -0
  41. package/dist/policies.js +92 -0
  42. package/dist/policies.js.map +1 -0
  43. package/dist/queries.d.ts +166 -0
  44. package/dist/queries.d.ts.map +1 -0
  45. package/dist/queries.js +236 -0
  46. package/dist/queries.js.map +1 -0
  47. package/dist/sessions.d.ts +40 -0
  48. package/dist/sessions.d.ts.map +1 -0
  49. package/dist/sessions.js +39 -0
  50. package/dist/sessions.js.map +1 -0
  51. package/dist/tokens.d.ts +40 -0
  52. package/dist/tokens.d.ts.map +1 -0
  53. package/dist/tokens.js +60 -0
  54. package/dist/tokens.js.map +1 -0
  55. package/package.json +42 -0
@@ -0,0 +1,51 @@
1
+ /**
2
+ * The sections whose entry names *are* subjects.
3
+ *
4
+ * A model is registered under its table and a resource under its name, and both of
5
+ * those are what a subject is spelled as — `articles`, `pages`, `media`.
6
+ */
7
+ const NAMING_SECTIONS = ['resources', 'models', 'singletons'];
8
+ /**
9
+ * The sections whose entry names *contain* a subject.
10
+ *
11
+ * A command or a query is `menu.list`, and the subject the authorizer takes from it is
12
+ * everything before the last dot. These carry the registering module in the descriptor
13
+ * itself, so they are read rather than attributed.
14
+ */
15
+ const GROUPING_SECTIONS = ['commands', 'queries'];
16
+ /** What `subjectOf` takes as the subject of `menu.list`. Kept identical to it. */
17
+ const groupOf = (name) => name.slice(0, Math.max(name.lastIndexOf('.'), 0));
18
+ /**
19
+ * Whether `module` declared `subject`, and may therefore write a policy for it.
20
+ *
21
+ * @param registry the application's, for the attribution — the module that registered
22
+ * each entry, which is what `forModule` records.
23
+ */
24
+ export const ownsSubject = (registry, module, subject) => {
25
+ if (subject === module || subject.startsWith(`${module}.`))
26
+ return true;
27
+ if (NAMING_SECTIONS.some((section) => registry.registeredBy(section, subject) === module)) {
28
+ return true;
29
+ }
30
+ return GROUPING_SECTIONS.some((section) => registry
31
+ .section(section)
32
+ .some((entry) => entry.module === module && groupOf(entry.name) === subject));
33
+ };
34
+ /**
35
+ * Why a subject was refused, in the terms the person reading it can act on.
36
+ *
37
+ * It names all three things the refusal is about — who asked, for what, and what would
38
+ * have had to be true — because a configuration error read at boot is read once, by
39
+ * somebody who did not write the module that caused it.
40
+ */
41
+ export const foreignSubject = (module, subject) => `Module "${module}" registered a policy for "${subject}", which it does not declare. ` +
42
+ `A policy is a grant: it lets a caller through where a permission would have been ` +
43
+ `required, so a module may only write one for a subject of its own. "${module}" would ` +
44
+ `have to name "${subject}" as a model or a resource, declare a command or query in the "${subject}." group, or be named "${subject}" itself. ` +
45
+ `If this policy belongs to the application rather than to a module, pass it as ` +
46
+ `auth({ policies: [...] }) at the composition root, where the application speaks for itself.`;
47
+ /** The same, for a policy that reached the registry through no module at all. */
48
+ export const unattributedSubject = (subject) => `A policy for "${subject}" was registered outside any module, so nothing declares it ` +
49
+ `and nothing answers for it. Register it on the module that owns the subject with ` +
50
+ `.policies(...), or — if it is the application's — pass it as auth({ policies: [...] }).`;
51
+ //# sourceMappingURL=ownership.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ownership.js","sourceRoot":"","sources":["../src/ownership.ts"],"names":[],"mappings":"AAkDA;;;;;GAKG;AACH,MAAM,eAAe,GAAG,CAAC,WAAW,EAAE,QAAQ,EAAE,YAAY,CAAU,CAAA;AAEtE;;;;;;GAMG;AACH,MAAM,iBAAiB,GAAG,CAAC,UAAU,EAAE,SAAS,CAAU,CAAA;AAE1D,kFAAkF;AAClF,MAAM,OAAO,GAAG,CAAC,IAAY,EAAU,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;AAE3F;;;;;GAKG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,QAAwB,EAAE,MAAc,EAAE,OAAe,EAAW,EAAE;IAChG,IAAI,OAAO,KAAK,MAAM,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,MAAM,GAAG,CAAC;QAAE,OAAO,IAAI,CAAA;IAEvE,IAAI,eAAe,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,QAAQ,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,MAAM,CAAC,EAAE,CAAC;QAC1F,OAAO,IAAI,CAAA;IACb,CAAC;IAED,OAAO,iBAAiB,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CACxC,QAAQ;SACL,OAAO,CAAC,OAAO,CAAC;SAChB,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,KAAK,MAAM,IAAI,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,OAAO,CAAC,CAC/E,CAAA;AACH,CAAC,CAAA;AAED;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,MAAc,EAAE,OAAe,EAAU,EAAE,CACxE,WAAW,MAAM,8BAA8B,OAAO,gCAAgC;IACtF,mFAAmF;IACnF,uEAAuE,MAAM,UAAU;IACvF,iBAAiB,OAAO,kEAAkE,OAAO,0BAA0B,OAAO,YAAY;IAC9I,gFAAgF;IAChF,6FAA6F,CAAA;AAE/F,iFAAiF;AACjF,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,OAAe,EAAU,EAAE,CAC7D,iBAAiB,OAAO,8DAA8D;IACtF,mFAAmF;IACnF,yFAAyF,CAAA"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * What an actor is allowed to do (SPEC.md §50).
3
+ *
4
+ * A user's permissions come from the roles they hold; an API token and an agent
5
+ * carry their own list. All three answer the same question the same way, which is
6
+ * why a policy never has to know which kind of actor it is looking at.
7
+ *
8
+ * All three are also asked a question before that one: whether this is still an
9
+ * actor at all. It is asked *here* rather than where a credential is accepted,
10
+ * because not every path presents a credential. A session is checked when the cookie
11
+ * is read (`sessionActor`) and a bearer token when the header is (`tokenActor`), but
12
+ * a job carries an actor sealed into an envelope and is replayed by a worker hours
13
+ * later with nothing to present — as does anything else that stores an identity and
14
+ * acts on it afterwards. This function is the one narrow place every one of those
15
+ * paths goes through, so this is where liveness has to be decided (ADR-0023).
16
+ *
17
+ * The cost is a row read: resolving a user's permissions is four queries rather than
18
+ * three, and the command pipeline resolves them twice — once for the permission
19
+ * check and once for the policy rule (ADR-0015). It buys the only revocation the
20
+ * framework has, so it is paid on every command. A deactivated actor short-circuits
21
+ * to one query, which is the case where it matters that it is cheap.
22
+ */
23
+ import type { Actor } from '@assemora/core';
24
+ /** Grants everything. Given to the first administrator, and to nobody casually. */
25
+ export declare const WILDCARD = "*";
26
+ export type PermissionSet = ReadonlySet<string>;
27
+ /** Everything this actor may do, whoever they are. */
28
+ export declare const permissionsOf: (actor: Actor | undefined) => Promise<PermissionSet>;
29
+ /**
30
+ * Whether a permission is held, exactly or by a wildcard above it.
31
+ *
32
+ * `articles.update` is held by `articles.update`, by `articles.*`, and by `*`. The
33
+ * grouped form is what makes a role like "editor" writable at all — `content.*` says
34
+ * something a list of forty names does not — and it matches how the names themselves
35
+ * are built, since a command name *is* a permission name (ADR-0015).
36
+ *
37
+ * Only whole segments match: `articles.*` does not grant `articlesecret.read`.
38
+ */
39
+ export declare const holds: (permissions: PermissionSet, permission: string) => boolean;
40
+ //# sourceMappingURL=permissions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../src/permissions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAK3C,mFAAmF;AACnF,eAAO,MAAM,QAAQ,MAAM,CAAA;AAE3B,MAAM,MAAM,aAAa,GAAG,WAAW,CAAC,MAAM,CAAC,CAAA;AAmE/C,sDAAsD;AACtD,eAAO,MAAM,aAAa,UAAiB,KAAK,GAAG,SAAS,KAAG,OAAO,CAAC,aAAa,CAWnF,CAAA;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,KAAK,gBAAiB,aAAa,cAAc,MAAM,KAAG,OAUtE,CAAA"}
@@ -0,0 +1,86 @@
1
+ import { Agent, ApiToken, Permission, RolePermission, User, UserRole } from './models.js';
2
+ import { isLive } from './tokens.js';
3
+ /** Grants everything. Given to the first administrator, and to nobody casually. */
4
+ export const WILDCARD = '*';
5
+ const EMPTY = new Set();
6
+ /**
7
+ * Whether the person behind an actor may still act.
8
+ *
9
+ * An absent row is deliberately *not* a revocation. The framework has no user
10
+ * deletion — `active: false` is the whole of how a person is cut off — so an absence
11
+ * is never something this system decided; it is an application whose identities live
12
+ * in an SSO directory and whose roles are assigned here, and denying on absence
13
+ * would make `assemora_user_roles` unusable for it. `active: false` is a statement,
14
+ * and it is the one that binds.
15
+ */
16
+ const userIsLive = async (userId) => {
17
+ const user = await User.find(userId);
18
+ return user === null || user.active;
19
+ };
20
+ const permissionsOfUser = async (userId) => {
21
+ // Asked first, so a user who may not act costs one read instead of four.
22
+ if (!(await userIsLive(userId)))
23
+ return EMPTY;
24
+ const roles = await UserRole.where('userId', userId);
25
+ if (roles.length === 0)
26
+ return EMPTY;
27
+ const links = await RolePermission.whereIn('roleId', roles.map((role) => role.roleId));
28
+ if (links.length === 0)
29
+ return EMPTY;
30
+ const permissions = await Permission.whereIn('id', links.map((link) => link.permissionId));
31
+ return new Set(permissions.map((permission) => permission.name));
32
+ };
33
+ const permissionsOfAgent = async (agentId) => {
34
+ const agent = await Agent.find(agentId);
35
+ if (agent === null || !agent.enabled)
36
+ return EMPTY;
37
+ return new Set(agent.permissions);
38
+ };
39
+ const permissionsOfApiToken = async (tokenId) => {
40
+ const token = await ApiToken.find(tokenId);
41
+ // `tokenActor` refuses an expired token at the header, but a token id restored
42
+ // from anywhere else — a queue, a stored context — never passes that check, and a
43
+ // token whose expiry has come and gone is not a credential any more.
44
+ if (token === null || !isLive(token.expiresAt))
45
+ return EMPTY;
46
+ // A token issued *for* a person is that person's access under another name.
47
+ // Deactivating them has to take it with it, or the one revocation gesture the
48
+ // framework has quietly leaves a door open (SPEC.md §49).
49
+ if (token.userId !== null && !(await userIsLive(token.userId)))
50
+ return EMPTY;
51
+ return new Set(token.permissions);
52
+ };
53
+ /** Everything this actor may do, whoever they are. */
54
+ export const permissionsOf = async (actor) => {
55
+ if (actor === undefined)
56
+ return EMPTY;
57
+ switch (actor.type) {
58
+ case 'user':
59
+ return permissionsOfUser(actor.id);
60
+ case 'agent':
61
+ return permissionsOfAgent(actor.id);
62
+ case 'api':
63
+ return permissionsOfApiToken(actor.id);
64
+ }
65
+ };
66
+ /**
67
+ * Whether a permission is held, exactly or by a wildcard above it.
68
+ *
69
+ * `articles.update` is held by `articles.update`, by `articles.*`, and by `*`. The
70
+ * grouped form is what makes a role like "editor" writable at all — `content.*` says
71
+ * something a list of forty names does not — and it matches how the names themselves
72
+ * are built, since a command name *is* a permission name (ADR-0015).
73
+ *
74
+ * Only whole segments match: `articles.*` does not grant `articlesecret.read`.
75
+ */
76
+ export const holds = (permissions, permission) => {
77
+ if (permissions.has(WILDCARD) || permissions.has(permission))
78
+ return true;
79
+ const segments = permission.split('.');
80
+ for (let depth = segments.length - 1; depth > 0; depth -= 1) {
81
+ if (permissions.has(`${segments.slice(0, depth).join('.')}.*`))
82
+ return true;
83
+ }
84
+ return false;
85
+ };
86
+ //# sourceMappingURL=permissions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permissions.js","sourceRoot":"","sources":["../src/permissions.ts"],"names":[],"mappings":"AAwBA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,cAAc,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AACzF,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEpC,mFAAmF;AACnF,MAAM,CAAC,MAAM,QAAQ,GAAG,GAAG,CAAA;AAI3B,MAAM,KAAK,GAAkB,IAAI,GAAG,EAAE,CAAA;AAEtC;;;;;;;;;GASG;AACH,MAAM,UAAU,GAAG,KAAK,EAAE,MAAc,EAAoB,EAAE;IAC5D,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;IAEpC,OAAO,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,MAAM,CAAA;AACrC,CAAC,CAAA;AAED,MAAM,iBAAiB,GAAG,KAAK,EAAE,MAAc,EAA0B,EAAE;IACzE,yEAAyE;IACzE,IAAI,CAAC,CAAC,MAAM,UAAU,CAAC,MAAM,CAAC,CAAC;QAAE,OAAO,KAAK,CAAA;IAE7C,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;IAEpD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAA;IAEpC,MAAM,KAAK,GAAG,MAAM,cAAc,CAAC,OAAO,CACxC,QAAQ,EACR,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CACjC,CAAA;IAED,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAA;IAEpC,MAAM,WAAW,GAAG,MAAM,UAAU,CAAC,OAAO,CAC1C,IAAI,EACJ,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,CACvC,CAAA;IAED,OAAO,IAAI,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAA;AAClE,CAAC,CAAA;AAED,MAAM,kBAAkB,GAAG,KAAK,EAAE,OAAe,EAA0B,EAAE;IAC3E,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IAEvC,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO;QAAE,OAAO,KAAK,CAAA;IAElD,OAAO,IAAI,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,CAAA;AACnC,CAAC,CAAA;AAED,MAAM,qBAAqB,GAAG,KAAK,EAAE,OAAe,EAA0B,EAAE;IAC9E,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IAE1C,+EAA+E;IAC/E,kFAAkF;IAClF,qEAAqE;IACrE,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC;QAAE,OAAO,KAAK,CAAA;IAE5D,4EAA4E;IAC5E,8EAA8E;IAC9E,0DAA0D;IAC1D,IAAI,KAAK,CAAC,MAAM,KAAK,IAAI,IAAI,CAAC,CAAC,MAAM,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAAE,OAAO,KAAK,CAAA;IAE5E,OAAO,IAAI,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,CAAA;AACnC,CAAC,CAAA;AAED,sDAAsD;AACtD,MAAM,CAAC,MAAM,aAAa,GAAG,KAAK,EAAE,KAAwB,EAA0B,EAAE;IACtF,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAA;IAErC,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;QACnB,KAAK,MAAM;YACT,OAAO,iBAAiB,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;QACpC,KAAK,OAAO;YACV,OAAO,kBAAkB,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;QACrC,KAAK,KAAK;YACR,OAAO,qBAAqB,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;IAC1C,CAAC;AACH,CAAC,CAAA;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,WAA0B,EAAE,UAAkB,EAAW,EAAE;IAC/E,IAAI,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC;QAAE,OAAO,IAAI,CAAA;IAEzE,MAAM,QAAQ,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAEtC,KAAK,IAAI,KAAK,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,KAAK,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QAC5D,IAAI,WAAW,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAA;IAC7E,CAAC;IAED,OAAO,KAAK,CAAA;AACd,CAAC,CAAA"}
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Policies (SPEC.md §51).
3
+ *
4
+ * A policy answers one question — may this actor do this to this thing — and the
5
+ * same answer is given to Studio, REST, the SDK, the CLI and MCP, because they all
6
+ * arrive through the same Command Bus and the same Query Bus.
7
+ */
8
+ import { type Actor, type AssemoraContext } from '@assemora/core';
9
+ export type PolicyActor = Actor | undefined;
10
+ export type PolicyContext<R = unknown> = {
11
+ readonly actor: PolicyActor;
12
+ /** The row being acted on, once it has been read. `undefined` before that. */
13
+ readonly record: R;
14
+ readonly context: AssemoraContext;
15
+ /** Whether the actor holds a named permission (SPEC.md §50). */
16
+ can(permission: string): boolean;
17
+ };
18
+ export type PolicyRule<R = unknown> = (context: PolicyContext<R>) => boolean | Promise<boolean>;
19
+ /**
20
+ * Rules by action. `read`, `create`, `update` and `delete` are the ones the CRUD
21
+ * commands ask about; any other name is asked for by whoever defines it.
22
+ */
23
+ export type PolicyRules<R = unknown> = Readonly<Record<string, PolicyRule<R>>>;
24
+ export type Policy<R = unknown> = {
25
+ readonly node: 'policy';
26
+ readonly subject: string;
27
+ readonly rules: PolicyRules<R>;
28
+ };
29
+ /**
30
+ * ```ts
31
+ * export const ArticlePolicy = policy('articles', {
32
+ * read: () => true,
33
+ * update: ({ actor, record }) => actor?.id === record.authorId,
34
+ * delete: ({ can }) => can('articles.delete'),
35
+ * })
36
+ * ```
37
+ *
38
+ * SPEC.md §51 spells the record argument after the model — `{ actor, article }`.
39
+ * It is named `record` here because the key would have to be derived from a literal
40
+ * table name that `model()` does not preserve; naming it after the subject would be
41
+ * a runtime-only convenience with no type behind it.
42
+ */
43
+ export declare const policy: <R = Record<string, unknown>>(subject: string, rules: PolicyRules<R>) => Policy<R>;
44
+ /**
45
+ * What the Schema Registry is told about a policy (SPEC.md §51, ADR-0002).
46
+ *
47
+ * The rules themselves are functions and stay here: a function does not survive
48
+ * `JSON.stringify`, so a descriptor that carried one would arrive at Studio as an empty
49
+ * object and be worse than saying nothing (ADR-0027). What travels is the shape of the
50
+ * thing — which subject, which actions it answers for, and which module put it there.
51
+ *
52
+ * That last field is the reason this section exists at all. `registerPolicy` grants
53
+ * access, and until now it wrote nothing anywhere: an installed package could open
54
+ * `pages.create` to everybody in twelve lines and the application's single source of
55
+ * truth described the rest of the system perfectly while saying nothing about that.
56
+ */
57
+ export type PolicyDescriptor = {
58
+ /** The subject the policy is about, which is what it is addressed by. */
59
+ readonly name: string;
60
+ /** The actions it answers for, in declaration order. */
61
+ readonly actions: readonly string[];
62
+ /**
63
+ * The module that registered it, when a module did.
64
+ *
65
+ * Absent means `registerPolicy` was called outside module registration — which is
66
+ * legal, and is exactly the case worth being able to see.
67
+ */
68
+ readonly module?: string;
69
+ };
70
+ declare module '@assemora/core' {
71
+ interface RegistrySections {
72
+ policies: PolicyDescriptor;
73
+ }
74
+ }
75
+ export declare const describePolicy: (definition: Policy<never>, module?: string) => PolicyDescriptor;
76
+ /**
77
+ * What the application's own policies are attributed to.
78
+ *
79
+ * A policy passed as `auth({ policies: [...] })` is written at the composition root by
80
+ * the person who assembled the application, so it speaks for the whole of it and is
81
+ * held to no ownership rule — the application *is* the trust boundary the rule exists
82
+ * to protect. A symbol rather than a name so that no module can be called this, and so
83
+ * that a descriptor never carries it: the section shows such a policy with no `module`,
84
+ * which is what "the application, not a package" has looked like since #5.
85
+ */
86
+ export declare const APPLICATION: unique symbol;
87
+ export type PolicySource = string | typeof APPLICATION;
88
+ /**
89
+ * Puts a rule in the map, attributed to whoever is registering it.
90
+ *
91
+ * Deliberately *not* exported from this package. The source is what the ownership rule
92
+ * is checked against, so a caller able to supply one is a caller able to claim to be
93
+ * `pages` — and a package outside this file cannot reach this function to try
94
+ * (ADR-0027). The two callers are the module facet, which passes the module the
95
+ * builder is registering, and `auth({ policies })`, which passes `APPLICATION`.
96
+ */
97
+ export declare const registerModulePolicy: (definition: Policy<never>, module: PolicySource) => void;
98
+ /**
99
+ * Puts a rule in the map, attributed to nobody.
100
+ *
101
+ * Exported, because a test of the authorization port needs a policy in front of it and
102
+ * never boots an application. An application *does* boot, and refuses to start on a
103
+ * policy nothing declares — so this is a harness seam rather than a way in: a package
104
+ * that calls it at import time takes the application down at boot, naming the subject
105
+ * and itself (`ownership.ts`).
106
+ */
107
+ export declare const registerPolicy: (definition: Policy<never>) => void;
108
+ /**
109
+ * Who registered each policy, including the ones the application registered itself.
110
+ *
111
+ * `describedPolicies` cannot answer this: a descriptor carries a module *name*, and
112
+ * the application is deliberately not one. The check at boot needs the difference.
113
+ */
114
+ export declare const policySources: () => ReadonlyMap<string, PolicySource>;
115
+ export declare const policyFor: (subject: string) => Policy<never> | undefined;
116
+ export declare const registeredPolicies: () => readonly Policy<never>[];
117
+ /** Every policy as the registry describes it, whoever registered it and however. */
118
+ export declare const describedPolicies: () => readonly PolicyDescriptor[];
119
+ export declare const clearPolicies: () => void;
120
+ //# sourceMappingURL=policies.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policies.d.ts","sourceRoot":"","sources":["../src/policies.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,KAAK,KAAK,EAAE,KAAK,eAAe,EAAiB,MAAM,gBAAgB,CAAA;AAEhF,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,SAAS,CAAA;AAE3C,MAAM,MAAM,aAAa,CAAC,CAAC,GAAG,OAAO,IAAI;IACvC,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAA;IAC3B,8EAA8E;IAC9E,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAA;IAClB,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAA;IACjC,gEAAgE;IAChE,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAA;CACjC,CAAA;AAED,MAAM,MAAM,UAAU,CAAC,CAAC,GAAG,OAAO,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;AAE/F;;;GAGG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,GAAG,OAAO,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;AAE9E,MAAM,MAAM,MAAM,CAAC,CAAC,GAAG,OAAO,IAAI;IAChC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,CAAA;CAC/B,CAAA;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,MAAM,GAAI,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,WACvC,MAAM,SACR,WAAW,CAAC,CAAC,CAAC,KACpB,MAAM,CAAC,CAAC,CAAyC,CAAA;AAEpD;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,yEAAyE;IACzE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,wDAAwD;IACxD,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAA;IACnC;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CACzB,CAAA;AAED,OAAO,QAAQ,gBAAgB,CAAC;IAC9B,UAAU,gBAAgB;QACxB,QAAQ,EAAE,gBAAgB,CAAA;KAC3B;CACF;AAED,eAAO,MAAM,cAAc,eAAgB,MAAM,CAAC,KAAK,CAAC,WAAW,MAAM,KAAG,gBAI1E,CAAA;AAOF;;;;;;;;;GASG;AACH,eAAO,MAAM,WAAW,EAAE,OAAO,MAAuC,CAAA;AAExE,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,OAAO,WAAW,CAAA;AAEtD;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,eAAgB,MAAM,CAAC,KAAK,CAAC,UAAU,YAAY,KAAG,IAGtF,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,eAAgB,MAAM,CAAC,KAAK,CAAC,KAAG,IAU1D,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,aAAa,QAAO,WAAW,CAAC,MAAM,EAAE,YAAY,CAAY,CAAA;AAE7E,eAAO,MAAM,SAAS,YAAa,MAAM,KAAG,MAAM,CAAC,KAAK,CAAC,GAAG,SAAoC,CAAA;AAEhG,eAAO,MAAM,kBAAkB,QAAO,SAAS,MAAM,CAAC,KAAK,CAAC,EAA8B,CAAA;AAE1F,oFAAoF;AACpF,eAAO,MAAM,iBAAiB,QAAO,SAAS,gBAAgB,EAQ1D,CAAA;AAEJ,eAAO,MAAM,aAAa,QAAO,IAGhC,CAAA"}
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Policies (SPEC.md §51).
3
+ *
4
+ * A policy answers one question — may this actor do this to this thing — and the
5
+ * same answer is given to Studio, REST, the SDK, the CLI and MCP, because they all
6
+ * arrive through the same Command Bus and the same Query Bus.
7
+ */
8
+ import { AssemoraError } from '@assemora/core';
9
+ /**
10
+ * ```ts
11
+ * export const ArticlePolicy = policy('articles', {
12
+ * read: () => true,
13
+ * update: ({ actor, record }) => actor?.id === record.authorId,
14
+ * delete: ({ can }) => can('articles.delete'),
15
+ * })
16
+ * ```
17
+ *
18
+ * SPEC.md §51 spells the record argument after the model — `{ actor, article }`.
19
+ * It is named `record` here because the key would have to be derived from a literal
20
+ * table name that `model()` does not preserve; naming it after the subject would be
21
+ * a runtime-only convenience with no type behind it.
22
+ */
23
+ export const policy = (subject, rules) => ({ node: 'policy', subject, rules });
24
+ export const describePolicy = (definition, module) => ({
25
+ name: definition.subject,
26
+ actions: Object.keys(definition.rules),
27
+ ...(module === undefined ? {} : { module }),
28
+ });
29
+ const registered = new Map();
30
+ /** Which module registered which subject, for the check at boot to be able to say. */
31
+ const sources = new Map();
32
+ /**
33
+ * What the application's own policies are attributed to.
34
+ *
35
+ * A policy passed as `auth({ policies: [...] })` is written at the composition root by
36
+ * the person who assembled the application, so it speaks for the whole of it and is
37
+ * held to no ownership rule — the application *is* the trust boundary the rule exists
38
+ * to protect. A symbol rather than a name so that no module can be called this, and so
39
+ * that a descriptor never carries it: the section shows such a policy with no `module`,
40
+ * which is what "the application, not a package" has looked like since #5.
41
+ */
42
+ export const APPLICATION = Symbol('assemora.application');
43
+ /**
44
+ * Puts a rule in the map, attributed to whoever is registering it.
45
+ *
46
+ * Deliberately *not* exported from this package. The source is what the ownership rule
47
+ * is checked against, so a caller able to supply one is a caller able to claim to be
48
+ * `pages` — and a package outside this file cannot reach this function to try
49
+ * (ADR-0027). The two callers are the module facet, which passes the module the
50
+ * builder is registering, and `auth({ policies })`, which passes `APPLICATION`.
51
+ */
52
+ export const registerModulePolicy = (definition, module) => {
53
+ registerPolicy(definition);
54
+ sources.set(definition.subject, module);
55
+ };
56
+ /**
57
+ * Puts a rule in the map, attributed to nobody.
58
+ *
59
+ * Exported, because a test of the authorization port needs a policy in front of it and
60
+ * never boots an application. An application *does* boot, and refuses to start on a
61
+ * policy nothing declares — so this is a harness seam rather than a way in: a package
62
+ * that calls it at import time takes the application down at boot, naming the subject
63
+ * and itself (`ownership.ts`).
64
+ */
65
+ export const registerPolicy = (definition) => {
66
+ if (registered.has(definition.subject)) {
67
+ throw new AssemoraError('CONFIGURATION_ERROR', `A policy for "${definition.subject}" is already registered`, { status: 500 });
68
+ }
69
+ registered.set(definition.subject, definition);
70
+ };
71
+ /**
72
+ * Who registered each policy, including the ones the application registered itself.
73
+ *
74
+ * `describedPolicies` cannot answer this: a descriptor carries a module *name*, and
75
+ * the application is deliberately not one. The check at boot needs the difference.
76
+ */
77
+ export const policySources = () => sources;
78
+ export const policyFor = (subject) => registered.get(subject);
79
+ export const registeredPolicies = () => [...registered.values()];
80
+ /** Every policy as the registry describes it, whoever registered it and however. */
81
+ export const describedPolicies = () => [...registered.values()].map((definition) => {
82
+ const source = sources.get(definition.subject);
83
+ // The application's policies are described with no module, which is the shape a
84
+ // registration outside any module has always had — and the right one: naming a
85
+ // module there would claim a package wrote what the application wrote.
86
+ return describePolicy(definition, typeof source === 'string' ? source : undefined);
87
+ });
88
+ export const clearPolicies = () => {
89
+ registered.clear();
90
+ sources.clear();
91
+ };
92
+ //# sourceMappingURL=policies.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policies.js","sourceRoot":"","sources":["../src/policies.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAoC,aAAa,EAAE,MAAM,gBAAgB,CAAA;AA2BhF;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,CACpB,OAAe,EACf,KAAqB,EACV,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAA;AAmCpD,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,UAAyB,EAAE,MAAe,EAAoB,EAAE,CAAC,CAAC;IAC/F,IAAI,EAAE,UAAU,CAAC,OAAO;IACxB,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC;IACtC,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5C,CAAC,CAAA;AAEF,MAAM,UAAU,GAAG,IAAI,GAAG,EAAyB,CAAA;AAEnD,sFAAsF;AACtF,MAAM,OAAO,GAAG,IAAI,GAAG,EAAwB,CAAA;AAE/C;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,WAAW,GAAkB,MAAM,CAAC,sBAAsB,CAAC,CAAA;AAIxE;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,UAAyB,EAAE,MAAoB,EAAQ,EAAE;IAC5F,cAAc,CAAC,UAAU,CAAC,CAAA;IAC1B,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;AACzC,CAAC,CAAA;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,UAAyB,EAAQ,EAAE;IAChE,IAAI,UAAU,CAAC,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QACvC,MAAM,IAAI,aAAa,CACrB,qBAAqB,EACrB,iBAAiB,UAAU,CAAC,OAAO,yBAAyB,EAC5D,EAAE,MAAM,EAAE,GAAG,EAAE,CAChB,CAAA;IACH,CAAC;IAED,UAAU,CAAC,GAAG,CAAC,UAAU,CAAC,OAAO,EAAE,UAAU,CAAC,CAAA;AAChD,CAAC,CAAA;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,GAAsC,EAAE,CAAC,OAAO,CAAA;AAE7E,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,OAAe,EAA6B,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;AAEhG,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAA6B,EAAE,CAAC,CAAC,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,CAAA;AAE1F,oFAAoF;AACpF,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAgC,EAAE,CACjE,CAAC,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE;IAC1C,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,CAAA;IAE9C,gFAAgF;IAChF,+EAA+E;IAC/E,uEAAuE;IACvE,OAAO,cAAc,CAAC,UAAU,EAAE,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;AACpF,CAAC,CAAC,CAAA;AAEJ,MAAM,CAAC,MAAM,aAAa,GAAG,GAAS,EAAE;IACtC,UAAU,CAAC,KAAK,EAAE,CAAA;IAClB,OAAO,CAAC,KAAK,EAAE,CAAA;AACjB,CAAC,CAAA"}
@@ -0,0 +1,166 @@
1
+ export declare const ListUsers: import("@assemora/core").QueryDefinition<{
2
+ page: import("@assemora/schema").OptionalSchema<number>;
3
+ perPage: import("@assemora/schema").OptionalSchema<number>;
4
+ search: import("@assemora/schema").OptionalSchema<string>;
5
+ active: import("@assemora/schema").OptionalSchema<boolean>;
6
+ }, {
7
+ readonly data: readonly {
8
+ readonly active: boolean;
9
+ readonly createdAt: Date;
10
+ readonly email: string;
11
+ readonly id: string;
12
+ readonly name: string;
13
+ readonly roles: readonly string[];
14
+ readonly version: number;
15
+ }[];
16
+ readonly lastPage: number;
17
+ readonly page: number;
18
+ readonly perPage: number;
19
+ readonly total: number;
20
+ }>;
21
+ export declare const GetUser: import("@assemora/core").QueryDefinition<{
22
+ id: import("@assemora/schema").StringSchema;
23
+ }, {
24
+ readonly active: boolean;
25
+ readonly createdAt: Date;
26
+ readonly email: string;
27
+ readonly id: string;
28
+ readonly name: string;
29
+ readonly permissions: readonly string[];
30
+ readonly roles: readonly string[];
31
+ readonly updatedAt: Date;
32
+ readonly version: number;
33
+ }>;
34
+ export declare const ListRoles: import("@assemora/core").QueryDefinition<{}, {
35
+ readonly data: readonly {
36
+ readonly id: string;
37
+ readonly label: string;
38
+ readonly name: string;
39
+ readonly permissions: readonly string[];
40
+ readonly version: number;
41
+ }[];
42
+ }>;
43
+ export declare const ListPermissions: import("@assemora/core").QueryDefinition<{}, {
44
+ readonly data: readonly {
45
+ readonly description: string | null;
46
+ readonly id: string;
47
+ readonly name: string;
48
+ }[];
49
+ }>;
50
+ export declare const ListApiTokens: import("@assemora/core").QueryDefinition<{
51
+ page: import("@assemora/schema").OptionalSchema<number>;
52
+ perPage: import("@assemora/schema").OptionalSchema<number>;
53
+ userId: import("@assemora/schema").OptionalSchema<string>;
54
+ }, {
55
+ readonly data: readonly {
56
+ readonly createdAt: Date;
57
+ readonly expiresAt: Date | null;
58
+ readonly id: string;
59
+ readonly lastUsedAt: Date | null;
60
+ readonly name: string;
61
+ readonly permissions: readonly string[];
62
+ readonly userId: string | null;
63
+ }[];
64
+ readonly lastPage: number;
65
+ readonly page: number;
66
+ readonly perPage: number;
67
+ readonly total: number;
68
+ }>;
69
+ export declare const ListAgents: import("@assemora/core").QueryDefinition<{
70
+ page: import("@assemora/schema").OptionalSchema<number>;
71
+ perPage: import("@assemora/schema").OptionalSchema<number>;
72
+ }, {
73
+ readonly data: readonly {
74
+ readonly createdAt: Date;
75
+ readonly description: string | null;
76
+ readonly enabled: boolean;
77
+ readonly id: string;
78
+ readonly name: string;
79
+ readonly permissions: readonly string[];
80
+ }[];
81
+ readonly lastPage: number;
82
+ readonly page: number;
83
+ readonly perPage: number;
84
+ readonly total: number;
85
+ }>;
86
+ export declare const authQueries: readonly [import("@assemora/core").QueryDefinition<{
87
+ page: import("@assemora/schema").OptionalSchema<number>;
88
+ perPage: import("@assemora/schema").OptionalSchema<number>;
89
+ search: import("@assemora/schema").OptionalSchema<string>;
90
+ active: import("@assemora/schema").OptionalSchema<boolean>;
91
+ }, {
92
+ readonly data: readonly {
93
+ readonly active: boolean;
94
+ readonly createdAt: Date;
95
+ readonly email: string;
96
+ readonly id: string;
97
+ readonly name: string;
98
+ readonly roles: readonly string[];
99
+ readonly version: number;
100
+ }[];
101
+ readonly lastPage: number;
102
+ readonly page: number;
103
+ readonly perPage: number;
104
+ readonly total: number;
105
+ }>, import("@assemora/core").QueryDefinition<{
106
+ id: import("@assemora/schema").StringSchema;
107
+ }, {
108
+ readonly active: boolean;
109
+ readonly createdAt: Date;
110
+ readonly email: string;
111
+ readonly id: string;
112
+ readonly name: string;
113
+ readonly permissions: readonly string[];
114
+ readonly roles: readonly string[];
115
+ readonly updatedAt: Date;
116
+ readonly version: number;
117
+ }>, import("@assemora/core").QueryDefinition<{}, {
118
+ readonly data: readonly {
119
+ readonly id: string;
120
+ readonly label: string;
121
+ readonly name: string;
122
+ readonly permissions: readonly string[];
123
+ readonly version: number;
124
+ }[];
125
+ }>, import("@assemora/core").QueryDefinition<{}, {
126
+ readonly data: readonly {
127
+ readonly description: string | null;
128
+ readonly id: string;
129
+ readonly name: string;
130
+ }[];
131
+ }>, import("@assemora/core").QueryDefinition<{
132
+ page: import("@assemora/schema").OptionalSchema<number>;
133
+ perPage: import("@assemora/schema").OptionalSchema<number>;
134
+ userId: import("@assemora/schema").OptionalSchema<string>;
135
+ }, {
136
+ readonly data: readonly {
137
+ readonly createdAt: Date;
138
+ readonly expiresAt: Date | null;
139
+ readonly id: string;
140
+ readonly lastUsedAt: Date | null;
141
+ readonly name: string;
142
+ readonly permissions: readonly string[];
143
+ readonly userId: string | null;
144
+ }[];
145
+ readonly lastPage: number;
146
+ readonly page: number;
147
+ readonly perPage: number;
148
+ readonly total: number;
149
+ }>, import("@assemora/core").QueryDefinition<{
150
+ page: import("@assemora/schema").OptionalSchema<number>;
151
+ perPage: import("@assemora/schema").OptionalSchema<number>;
152
+ }, {
153
+ readonly data: readonly {
154
+ readonly createdAt: Date;
155
+ readonly description: string | null;
156
+ readonly enabled: boolean;
157
+ readonly id: string;
158
+ readonly name: string;
159
+ readonly permissions: readonly string[];
160
+ }[];
161
+ readonly lastPage: number;
162
+ readonly page: number;
163
+ readonly perPage: number;
164
+ readonly total: number;
165
+ }>];
166
+ //# sourceMappingURL=queries.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queries.d.ts","sourceRoot":"","sources":["../src/queries.ts"],"names":[],"mappings":"AAgEA,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;EA4CpB,CAAA;AAEF,eAAO,MAAM,OAAO;;;;;;;;;;;;EAiClB,CAAA;AAEF,eAAO,MAAM,SAAS;;;;;;;;EAuCpB,CAAA;AAEF,eAAO,MAAM,eAAe;;;;;;EAa1B,CAAA;AAEF,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;EAsCxB,CAAA;AAEF,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;EA+BrB,CAAA;AAEF,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAOd,CAAA"}