@grantjs/schema 1.4.2 → 1.5.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.
Files changed (49) hide show
  1. package/package.json +9 -9
  2. package/src/events/event-catalog.ts +214 -0
  3. package/src/events/event-envelope.ts +73 -0
  4. package/src/events/index.ts +2 -0
  5. package/src/generated/graphql.ts +1318 -111
  6. package/src/generated/resolvers.ts +628 -0
  7. package/src/generated/schema-types.ts +312 -0
  8. package/src/index.ts +3 -0
  9. package/src/notifications/notification.types.ts +21 -0
  10. package/src/operations/api-keys/rotate-api-key.graphql +11 -0
  11. package/src/operations/groups/getGroups.graphql +1 -8
  12. package/src/operations/groups/getGroupsList.graphql +6 -0
  13. package/src/operations/me/mark-all-my-notifications-read.graphql +5 -0
  14. package/src/operations/me/mark-my-notification-read.graphql +3 -0
  15. package/src/operations/me/my-notification-preferences.graphql +11 -0
  16. package/src/operations/me/my-notifications.graphql +26 -0
  17. package/src/operations/me/my-project-membership.graphql +14 -0
  18. package/src/operations/me/my-project-memberships.graphql +14 -0
  19. package/src/operations/me/my-unread-notification-count.graphql +5 -0
  20. package/src/operations/me/set-my-notification-preference.graphql +11 -0
  21. package/src/operations/me/update-my-project-membership.graphql +14 -0
  22. package/src/operations/me/upload-my-project-membership-picture.graphql +6 -0
  23. package/src/operations/roles/getRoles.graphql +1 -14
  24. package/src/operations/webhooks/create-webhook-subscription.graphql +13 -0
  25. package/src/operations/webhooks/delete-webhook-subscription.graphql +3 -0
  26. package/src/operations/webhooks/replay-webhook-delivery.graphql +11 -0
  27. package/src/operations/webhooks/rotate-webhook-subscription-secret.graphql +7 -0
  28. package/src/operations/webhooks/update-webhook-subscription.graphql +10 -0
  29. package/src/operations/webhooks/webhook-deliveries.graphql +31 -0
  30. package/src/operations/webhooks/webhook-subscription.graphql +13 -0
  31. package/src/operations/webhooks/webhook-subscriptions.graphql +13 -0
  32. package/src/schema/api-keys/inputs/rotate-api-key.graphql +4 -0
  33. package/src/schema/api-keys/mutations/rotate-api-key.graphql +7 -0
  34. package/src/schema/me/input/notification.graphql +16 -0
  35. package/src/schema/me/input/update-my-project-membership.graphql +5 -0
  36. package/src/schema/me/input/upload-my-project-membership-picture.graphql +6 -0
  37. package/src/schema/me/mutations/my-notifications.graphql +5 -0
  38. package/src/schema/me/mutations/update-my-project-membership.graphql +6 -0
  39. package/src/schema/me/mutations/upload-my-project-membership-picture.graphql +5 -0
  40. package/src/schema/me/queries/my-notifications.graphql +5 -0
  41. package/src/schema/me/queries/my-project-membership.graphql +6 -0
  42. package/src/schema/me/queries/my-project-memberships.graphql +6 -0
  43. package/src/schema/me/types/my-project-membership.graphql +12 -0
  44. package/src/schema/me/types/notification.graphql +63 -0
  45. package/src/schema/webhooks/inputs/webhook-subscription.graphql +33 -0
  46. package/src/schema/webhooks/mutations/webhook-subscriptions.graphql +33 -0
  47. package/src/schema/webhooks/queries/webhook-subscriptions.graphql +22 -0
  48. package/src/schema/webhooks/types/webhook-subscription.graphql +58 -0
  49. package/src/webhooks/webhook.types.ts +25 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grantjs/schema",
3
- "version": "1.4.2",
3
+ "version": "1.5.1",
4
4
  "description": "GraphQL schema and generated types for Grant",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
@@ -13,14 +13,14 @@
13
13
  "@graphql-typed-document-node/core": "^3.2.0"
14
14
  },
15
15
  "devDependencies": {
16
- "@graphql-codegen/cli": "^7.1.2",
17
- "@graphql-codegen/client-preset": "^6.0.1",
18
- "@graphql-codegen/typescript": "^6.0.2",
19
- "@graphql-codegen/typescript-resolvers": "^6.0.2",
20
- "@parcel/watcher": "^2.5.4",
21
- "@types/node": "^25.9.3",
22
- "eslint": "^10.5.0",
23
- "typescript": "^6"
16
+ "@graphql-codegen/cli": "^7.2.0",
17
+ "@graphql-codegen/client-preset": "^6.1.0",
18
+ "@graphql-codegen/typescript": "^6.1.0",
19
+ "@graphql-codegen/typescript-resolvers": "^6.1.0",
20
+ "@parcel/watcher": "^2.6.0",
21
+ "@types/node": "^26.1.1",
22
+ "eslint": "^10.7.0",
23
+ "typescript": "^6.0.3"
24
24
  },
25
25
  "scripts": {
26
26
  "build": "tsc",
@@ -0,0 +1,214 @@
1
+ /**
2
+ * Canonical domain-event catalog.
3
+ *
4
+ * Single source of truth for every event `type` the platform emits, its
5
+ * notification `category`, its `deliveryClass`, and the declarative
6
+ * `audienceRule` consumed by the notification audience resolver. Services
7
+ * reference these constants instead of string literals; a coverage test asserts
8
+ * each mutating action that should emit has an entry here.
9
+ */
10
+
11
+ export const EVENT_CATEGORIES = ['security', 'iam', 'membership', 'integrations'] as const;
12
+ export type EventCategory = (typeof EVENT_CATEGORIES)[number];
13
+
14
+ export const EVENT_DELIVERY_CLASSES = ['transactional', 'notification'] as const;
15
+ export type EventDeliveryClass = (typeof EVENT_DELIVERY_CLASSES)[number];
16
+
17
+ /** Declarative audience primitives resolved by the notification audience resolver. */
18
+ export const AUDIENCE_PRIMITIVES = [
19
+ 'actor',
20
+ 'subject',
21
+ 'scopeMembers',
22
+ 'roleHolders',
23
+ 'owners',
24
+ 'watchers',
25
+ ] as const;
26
+ export type AudiencePrimitive = (typeof AUDIENCE_PRIMITIVES)[number];
27
+
28
+ export interface AudienceRule {
29
+ /** Primitives whose resolved users are unioned to form the candidate set. */
30
+ primitives: readonly AudiencePrimitive[];
31
+ /** Exclude the acting user from the resolved audience (default true). */
32
+ excludeActor?: boolean;
33
+ }
34
+
35
+ export interface EventCatalogEntry {
36
+ category: EventCategory;
37
+ deliveryClass: EventDeliveryClass;
38
+ audienceRule: AudienceRule;
39
+ }
40
+
41
+ const IAM_OWNERS: EventCatalogEntry = {
42
+ category: 'iam',
43
+ deliveryClass: 'notification',
44
+ audienceRule: { primitives: ['owners'], excludeActor: true },
45
+ };
46
+
47
+ const IAM_SUBJECT_OWNERS: EventCatalogEntry = {
48
+ category: 'iam',
49
+ deliveryClass: 'notification',
50
+ audienceRule: { primitives: ['subject', 'owners'], excludeActor: true },
51
+ };
52
+
53
+ const IAM_ROLE_HOLDERS_OWNERS: EventCatalogEntry = {
54
+ category: 'iam',
55
+ deliveryClass: 'notification',
56
+ audienceRule: { primitives: ['roleHolders', 'owners'], excludeActor: true },
57
+ };
58
+
59
+ const MEMBERSHIP_SUBJECT_OWNERS: EventCatalogEntry = {
60
+ category: 'membership',
61
+ deliveryClass: 'notification',
62
+ audienceRule: { primitives: ['subject', 'owners'], excludeActor: true },
63
+ };
64
+
65
+ const SECURITY_OWNERS: EventCatalogEntry = {
66
+ category: 'security',
67
+ deliveryClass: 'notification',
68
+ audienceRule: { primitives: ['owners'], excludeActor: true },
69
+ };
70
+
71
+ const SECURITY_SUBJECT: EventCatalogEntry = {
72
+ category: 'security',
73
+ deliveryClass: 'notification',
74
+ audienceRule: { primitives: ['subject'], excludeActor: false },
75
+ };
76
+
77
+ const SECURITY_SUBJECT_EXCLUDE_ACTOR: EventCatalogEntry = {
78
+ category: 'security',
79
+ deliveryClass: 'notification',
80
+ audienceRule: { primitives: ['subject'], excludeActor: true },
81
+ };
82
+
83
+ const INTEGRATIONS_OWNERS: EventCatalogEntry = {
84
+ category: 'integrations',
85
+ deliveryClass: 'notification',
86
+ audienceRule: { primitives: ['owners'], excludeActor: true },
87
+ };
88
+
89
+ export const EVENT_TYPES = [
90
+ // IAM mutations (Phase 1 slice)
91
+ 'role.created',
92
+ 'role.updated',
93
+ 'role.deleted',
94
+ 'permission.created',
95
+ 'permission.updated',
96
+ 'permission.deleted',
97
+ 'group.created',
98
+ 'group.updated',
99
+ 'group.deleted',
100
+ 'resource.created',
101
+ 'resource.updated',
102
+ 'resource.deleted',
103
+ 'role.permission_assigned',
104
+ 'role.permission_revoked',
105
+ 'user.permission_assigned',
106
+ 'user.permission_revoked',
107
+ 'group.permission_assigned',
108
+ 'group.permission_revoked',
109
+ 'role.group_assigned',
110
+ 'role.group_revoked',
111
+ 'user.group_assigned',
112
+ 'user.group_revoked',
113
+ 'api_key.created',
114
+ 'api_key.rotated',
115
+ 'api_key.revoked',
116
+ 'user.role_assigned',
117
+ 'user.role_revoked',
118
+ // Lifecycle / membership / security (Phase 3 seed)
119
+ 'organization.invitation_sent',
120
+ 'organization.invitation_accepted',
121
+ 'organization.invitation_revoked',
122
+ 'organization.member_added',
123
+ 'organization.member_role_changed',
124
+ 'organization.member_removed',
125
+ 'project.user_added',
126
+ 'project.user_removed',
127
+ 'project.user_profile_updated',
128
+ 'user.email_verification_requested',
129
+ 'organization.mfa_enforcement_changed',
130
+ 'signing_key.created',
131
+ 'signing_key.rotated',
132
+ 'user.mfa_enabled',
133
+ 'user.mfa_disabled',
134
+ 'user.mfa_recovery_codes_regenerated',
135
+ 'user.session_revoked',
136
+ 'user.sessions_revoked',
137
+ 'user.password_changed',
138
+ // CDM / project sync job summaries (entity events suppressed during import)
139
+ 'project_sync.completed',
140
+ 'project_sync.failed',
141
+ ] as const;
142
+
143
+ export type EventType = (typeof EVENT_TYPES)[number];
144
+
145
+ export const EVENT_CATALOG: Readonly<Record<EventType, EventCatalogEntry>> = {
146
+ 'role.created': IAM_ROLE_HOLDERS_OWNERS,
147
+ 'role.updated': IAM_OWNERS,
148
+ 'role.deleted': IAM_OWNERS,
149
+ 'permission.created': IAM_OWNERS,
150
+ 'permission.updated': IAM_ROLE_HOLDERS_OWNERS,
151
+ 'permission.deleted': IAM_OWNERS,
152
+ 'group.created': IAM_OWNERS,
153
+ 'group.updated': IAM_OWNERS,
154
+ 'group.deleted': IAM_OWNERS,
155
+ 'resource.created': IAM_OWNERS,
156
+ 'resource.updated': IAM_OWNERS,
157
+ 'resource.deleted': IAM_OWNERS,
158
+ 'role.permission_assigned': IAM_OWNERS,
159
+ 'role.permission_revoked': IAM_OWNERS,
160
+ 'user.permission_assigned': IAM_SUBJECT_OWNERS,
161
+ 'user.permission_revoked': IAM_SUBJECT_OWNERS,
162
+ 'group.permission_assigned': IAM_OWNERS,
163
+ 'group.permission_revoked': IAM_OWNERS,
164
+ 'role.group_assigned': IAM_OWNERS,
165
+ 'role.group_revoked': IAM_OWNERS,
166
+ 'user.group_assigned': IAM_SUBJECT_OWNERS,
167
+ 'user.group_revoked': IAM_SUBJECT_OWNERS,
168
+ 'api_key.created': SECURITY_OWNERS,
169
+ 'api_key.rotated': SECURITY_OWNERS,
170
+ 'api_key.revoked': SECURITY_OWNERS,
171
+ 'user.role_assigned': IAM_SUBJECT_OWNERS,
172
+ 'user.role_revoked': IAM_SUBJECT_OWNERS,
173
+ 'organization.invitation_sent': {
174
+ category: 'membership',
175
+ deliveryClass: 'notification',
176
+ audienceRule: { primitives: ['subject'], excludeActor: true },
177
+ },
178
+ 'organization.invitation_accepted': MEMBERSHIP_SUBJECT_OWNERS,
179
+ 'organization.invitation_revoked': MEMBERSHIP_SUBJECT_OWNERS,
180
+ 'organization.member_added': MEMBERSHIP_SUBJECT_OWNERS,
181
+ 'organization.member_role_changed': MEMBERSHIP_SUBJECT_OWNERS,
182
+ 'organization.member_removed': MEMBERSHIP_SUBJECT_OWNERS,
183
+ 'project.user_added': MEMBERSHIP_SUBJECT_OWNERS,
184
+ 'project.user_removed': MEMBERSHIP_SUBJECT_OWNERS,
185
+ 'project.user_profile_updated': MEMBERSHIP_SUBJECT_OWNERS,
186
+ 'user.email_verification_requested': {
187
+ category: 'security',
188
+ deliveryClass: 'transactional',
189
+ audienceRule: { primitives: ['subject'], excludeActor: false },
190
+ },
191
+ 'organization.mfa_enforcement_changed': {
192
+ category: 'security',
193
+ deliveryClass: 'notification',
194
+ audienceRule: { primitives: ['scopeMembers'], excludeActor: false },
195
+ },
196
+ 'signing_key.created': SECURITY_OWNERS,
197
+ 'signing_key.rotated': SECURITY_OWNERS,
198
+ 'user.mfa_enabled': SECURITY_SUBJECT,
199
+ 'user.mfa_disabled': SECURITY_SUBJECT,
200
+ 'user.mfa_recovery_codes_regenerated': SECURITY_SUBJECT,
201
+ 'user.session_revoked': SECURITY_SUBJECT_EXCLUDE_ACTOR,
202
+ 'user.sessions_revoked': SECURITY_SUBJECT,
203
+ 'user.password_changed': SECURITY_SUBJECT,
204
+ 'project_sync.completed': INTEGRATIONS_OWNERS,
205
+ 'project_sync.failed': INTEGRATIONS_OWNERS,
206
+ };
207
+
208
+ export function getEventCatalogEntry(type: EventType): EventCatalogEntry {
209
+ return EVENT_CATALOG[type];
210
+ }
211
+
212
+ export function isKnownEventType(type: string): type is EventType {
213
+ return Object.prototype.hasOwnProperty.call(EVENT_CATALOG, type);
214
+ }
@@ -0,0 +1,73 @@
1
+ import type { Scope } from '../generated/schema-types';
2
+ import type { EventCategory, EventType } from './event-catalog';
3
+
4
+ /**
5
+ * Payload carried inside an event. The internal model can be rich; the external
6
+ * webhook envelope projects `data` through a redaction allowlist.
7
+ */
8
+ export interface EventData {
9
+ /** Snapshot of the entity after the change (redacted on external delivery). */
10
+ after?: Record<string, unknown> | null;
11
+ /** Snapshot before the change (optional). */
12
+ before?: Record<string, unknown> | null;
13
+ /** Changed-fields delta: field -> { from, to }. */
14
+ delta?: Record<string, { from: unknown; to: unknown }> | null;
15
+ }
16
+
17
+ /** Reference to the aggregate (entity) the event is about. */
18
+ export interface EventAggregateRef {
19
+ kind: string;
20
+ id: string;
21
+ }
22
+
23
+ /**
24
+ * Input accepted by `IEventPublisher.publish`. The publisher fills in id,
25
+ * sequence, and persistence concerns.
26
+ */
27
+ export interface DomainEventInput {
28
+ type: EventType;
29
+ /** Optional explicit scope; when omitted the publisher derives it from the request auth context. */
30
+ scope?: Scope;
31
+ actor?: { userId: string } | null;
32
+ subjectUserId?: string | null;
33
+ aggregate?: EventAggregateRef;
34
+ data: EventData;
35
+ occurredAt?: Date;
36
+ }
37
+
38
+ /**
39
+ * The internal event as persisted to `event_log` and read by in-process
40
+ * consumers (notification generator). Carries the full (unredacted) payload.
41
+ */
42
+ export interface DomainEvent {
43
+ id: string;
44
+ sequence: number;
45
+ type: EventType;
46
+ category: EventCategory;
47
+ deliveryClass: 'transactional' | 'notification';
48
+ scope: Scope;
49
+ actorUserId: string | null;
50
+ subjectUserId: string | null;
51
+ aggregate?: EventAggregateRef;
52
+ data: EventData;
53
+ occurredAt: Date;
54
+ }
55
+
56
+ /**
57
+ * External event envelope delivered to webhook consumers. CloudEvents 1.0 core
58
+ * fields plus Grant extension attributes (prefixed `grant`). `data` is the
59
+ * redacted projection of the internal payload.
60
+ */
61
+ export interface CloudEventEnvelope {
62
+ specversion: '1.0';
63
+ id: string;
64
+ source: string;
65
+ type: EventType;
66
+ time: string;
67
+ subject?: string;
68
+ grantsequence: string;
69
+ grantscope: { tenant: string; id: string };
70
+ grantactor?: { userId: string } | null;
71
+ grantcategory: EventCategory;
72
+ data: EventData;
73
+ }
@@ -0,0 +1,2 @@
1
+ export * from './event-catalog';
2
+ export * from './event-envelope';