@kubun/graphql 0.17.1 → 0.18.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/lib/context.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { DocumentID } from '@kubun/id';
2
2
  import type { AggregateHaving, AggregateSpec, AggregateValues, DocumentData, DocumentFilter, DocumentNode, DocumentOrderBy, GroupedAggregateOrderTerm, GroupedAggregateValue } from '@kubun/protocol';
3
- import type { SearchDocumentResult, SearchDocumentsParams, StoredAccessDefaultRow, ViewerReadAccess } from '@kubun/store-graph';
3
+ import type { SignedRecord } from '@kubun/records';
4
+ import type { CatalogDefinition, CatalogLevel, CatalogRuleConflict, CatalogRuleResolutionInput, DocumentHold, DocumentScopeView, SearchDocumentResult, SearchDocumentsParams, StoredAccessDefaultRow, ViewerReadAccess } from '@kubun/store-graph';
4
5
  import type { Tracer } from '@sozai/otel';
5
6
  import type { GraphQLResolveInfo } from 'graphql';
6
7
  import type { Connection, Edge } from './relay.js';
@@ -34,7 +35,10 @@ export type ReadContextDB = {
34
35
  filter?: DocumentFilter;
35
36
  orderBy?: DocumentOrderBy;
36
37
  owner?: string;
37
- catalogID?: string;
38
+ catalogRef?: {
39
+ owner: string;
40
+ id: string;
41
+ };
38
42
  first?: number | null;
39
43
  last?: number | null;
40
44
  before?: string | null;
@@ -54,7 +58,10 @@ export type ReadContextDB = {
54
58
  modelIDs: Array<string>;
55
59
  filter?: DocumentFilter;
56
60
  owner?: string;
57
- catalogID?: string;
61
+ catalogRef?: {
62
+ owner: string;
63
+ id: string;
64
+ };
58
65
  count: boolean;
59
66
  aggregates: Array<AggregateSpec>;
60
67
  readAccess?: ViewerReadAccess;
@@ -63,7 +70,10 @@ export type ReadContextDB = {
63
70
  modelIDs: Array<string>;
64
71
  filter?: DocumentFilter;
65
72
  owner?: string;
66
- catalogID?: string;
73
+ catalogRef?: {
74
+ owner: string;
75
+ id: string;
76
+ };
67
77
  count: boolean;
68
78
  aggregates: Array<AggregateSpec>;
69
79
  groupBy: Array<{
@@ -86,7 +96,10 @@ export type AggregateScope = {
86
96
  modelIDs: Array<string>;
87
97
  filter?: DocumentFilter;
88
98
  owner?: string;
89
- catalogID?: string;
99
+ catalogRef?: {
100
+ owner: string;
101
+ id: string;
102
+ };
90
103
  };
91
104
  /**
92
105
  * Connection value returned by {@link QueryContext.resolveConnection}. Extends
@@ -103,7 +116,10 @@ export type ScopedConnection = Connection<DocumentNode> & {
103
116
  export type ResolveAggregateParams = {
104
117
  filter?: DocumentFilter;
105
118
  owner?: string;
106
- catalogID?: string;
119
+ catalogRef?: {
120
+ owner: string;
121
+ id: string;
122
+ };
107
123
  count: boolean;
108
124
  aggregates: Array<AggregateSpec>;
109
125
  };
@@ -116,7 +132,10 @@ export type ResolveAggregateParams = {
116
132
  export type ResolveGroupedAggregateParams = {
117
133
  filter?: DocumentFilter;
118
134
  owner?: string;
119
- catalogID?: string;
135
+ catalogRef?: {
136
+ owner: string;
137
+ id: string;
138
+ };
120
139
  count: boolean;
121
140
  aggregates: Array<AggregateSpec>;
122
141
  groupBy: Array<{
@@ -158,6 +177,176 @@ export type SubscriptionContext = {
158
177
  * Query and subscription functions requiring server implementation
159
178
  */
160
179
  export type ReadOnlyContext = QueryContext & SubscriptionContext;
180
+ export type CatalogRuleInput = Omit<CatalogRuleValue, 'id'> & {
181
+ id?: string;
182
+ };
183
+ export type CatalogRuleValue = {
184
+ id: string;
185
+ models?: Array<string>;
186
+ owners?: Array<string>;
187
+ maxAge?: string;
188
+ maxAhead?: string;
189
+ dateField?: string;
190
+ };
191
+ export type CatalogValue = {
192
+ id: string;
193
+ headVersionID: string;
194
+ upstream?: CatalogVersionRef;
195
+ name: string;
196
+ description?: string;
197
+ rules: Array<CatalogRuleValue>;
198
+ app?: string;
199
+ };
200
+ export type CatalogListing = {
201
+ id: string;
202
+ app: string | null;
203
+ name: string;
204
+ deletedAt: string | null;
205
+ catalog: CatalogValue | null;
206
+ };
207
+ export type CatalogVersionRef = {
208
+ catalogID: string;
209
+ versionID: string;
210
+ };
211
+ export type CatalogVersion = {
212
+ id: string;
213
+ catalogID: string;
214
+ parents: Array<CatalogVersionRef>;
215
+ catalog: CatalogValue;
216
+ };
217
+ export type CatalogVersionConnection = {
218
+ edges: Array<{
219
+ cursor: string;
220
+ node: CatalogVersion;
221
+ }>;
222
+ pageInfo: {
223
+ endCursor: string | null;
224
+ hasNextPage: boolean;
225
+ };
226
+ };
227
+ export type CatalogDraft = {
228
+ catalogID: string;
229
+ baseVersionID: string;
230
+ dirty: boolean;
231
+ updateAvailable: boolean;
232
+ catalog: CatalogValue;
233
+ };
234
+ export type CatalogUpdateInput = {
235
+ name?: string;
236
+ description?: string;
237
+ rules?: Array<CatalogRuleInput>;
238
+ };
239
+ export type FetchCatalogHeadVersionParams = {
240
+ catalogID: string;
241
+ versionID: string;
242
+ groupID: string;
243
+ peerDID: string;
244
+ /** Aborts once another member's copy decided the lookup. */
245
+ signal?: AbortSignal;
246
+ };
247
+ export type CatalogUpstreamUnavailableReason = 'NO_LIVE_OFFER' | 'UPSTREAM_DELETED' | 'BASE_MISSING' | 'VERSION_PENDING';
248
+ export type CatalogUpstreamStatus = {
249
+ upstream: CatalogVersionRef | null;
250
+ latestVersionID: string | null;
251
+ updateAvailable: boolean;
252
+ unavailableReason: CatalogUpstreamUnavailableReason | null;
253
+ };
254
+ export type CatalogUpstreamLookup = {
255
+ status(id: string): Promise<CatalogUpstreamStatus>;
256
+ held(ref: CatalogVersionRef): Promise<SignedRecord | null>;
257
+ fetch(params: FetchCatalogHeadVersionParams): Promise<SignedRecord | null>;
258
+ eligible(catalogID: string): Promise<boolean>;
259
+ };
260
+ export type { CatalogRuleResolutionInput } from '@kubun/store-graph';
261
+ export type CatalogPullPreview = {
262
+ baseVersionID: string;
263
+ localRevisionID: string;
264
+ upstreamVersionID: string;
265
+ merged: CatalogDefinition & {
266
+ id: string;
267
+ };
268
+ conflicts: Array<CatalogRuleConflict>;
269
+ };
270
+ export type CatalogPullResult = {
271
+ catalog: CatalogListing;
272
+ draft: CatalogDraft | null;
273
+ published: boolean;
274
+ };
275
+ export type ResolveCatalogPullParams = {
276
+ id: string;
277
+ baseVersionID: string;
278
+ localRevisionID: string;
279
+ upstreamVersionID: string;
280
+ resolutions: Array<CatalogRuleResolutionInput>;
281
+ publish: boolean;
282
+ };
283
+ export type CatalogDeviceStorage = {
284
+ catalogID: string;
285
+ level: CatalogLevel | null;
286
+ state: 'READY' | 'NEEDS_ROUTE';
287
+ matchingLocalDocuments: number;
288
+ };
289
+ export type CustodyStatus = 'NONE' | 'PENDING' | 'PARTIAL' | 'READY';
290
+ export type CatalogCustody = {
291
+ catalogID: string;
292
+ custodians: Array<string>;
293
+ accepted: Array<string>;
294
+ /** Named custodians whose last word is that they take no custody. */
295
+ declined: Array<string>;
296
+ status: CustodyStatus;
297
+ };
298
+ export type CatalogContext = {
299
+ previewCatalogPull(id: string): Promise<CatalogPullPreview>;
300
+ resolveCatalogPull(params: ResolveCatalogPullParams): Promise<CatalogPullResult>;
301
+ cloneCatalog(id: string): Promise<CatalogListing>;
302
+ catalogUpstreamStatus(id: string): Promise<CatalogUpstreamStatus>;
303
+ createCatalog(input: {
304
+ name: string;
305
+ description?: string;
306
+ rules: Array<CatalogRuleInput>;
307
+ app?: string;
308
+ }): Promise<CatalogListing>;
309
+ updateCatalog(id: string, input: {
310
+ name?: string;
311
+ description?: string;
312
+ rules?: Array<CatalogRuleInput>;
313
+ }): Promise<CatalogListing>;
314
+ deleteCatalog(id: string): Promise<boolean>;
315
+ deduplicateAppCatalogs(app: string, name: string): Promise<CatalogListing | null>;
316
+ setCatalogDeviceLevel(id: string, level: CatalogLevel): Promise<CatalogDeviceStorage>;
317
+ catalogDeviceStorage(id: string): Promise<CatalogDeviceStorage>;
318
+ catalogCustody(id: string): Promise<CatalogCustody>;
319
+ /**
320
+ * Assign the catalog's custodians, superseding the current assignment. A custodian no longer
321
+ * named keeps its pins until a named custodian has accepted and answered held, so an empty list
322
+ * releases nothing: the old custodians keep their copies.
323
+ */
324
+ setCatalogCustodians(id: string, deviceDIDs: Array<string>): Promise<CatalogCustody>;
325
+ /** Whether this device accepts custody assignments; kept locally, over the engine default. */
326
+ setDeviceCustodyEligible(eligible: boolean): Promise<boolean>;
327
+ /**
328
+ * Write the next assignment without this device. Refused (`CUSTODY_ONLY_COPY`) while no other
329
+ * custodian accepted the current one, unless `force`. Pins go after another custodian's receipt,
330
+ * or at once with `force`, which deletes nothing.
331
+ */
332
+ leaveCatalogCustody(id: string, force: boolean): Promise<CatalogCustody>;
333
+ catalogs(includeDeleted: boolean): Promise<Array<CatalogListing>>;
334
+ catalogVersions(params: {
335
+ id: string;
336
+ first: number;
337
+ after?: string;
338
+ }): Promise<CatalogVersionConnection>;
339
+ catalogDraft(id: string): Promise<CatalogDraft | null>;
340
+ editCatalogDraft(id: string, input: CatalogUpdateInput): Promise<CatalogDraft>;
341
+ discardCatalogDraft(id: string): Promise<boolean>;
342
+ publishCatalogDraft(id: string, expectedHeadVersionID: string): Promise<CatalogListing>;
343
+ documentScope(id: string): Promise<DocumentScopeView>;
344
+ retainDocument(documentID: string, options: {
345
+ reason?: string;
346
+ expiresAt?: string;
347
+ }): Promise<DocumentHold>;
348
+ releaseDocumentHold(holdID: string): Promise<boolean>;
349
+ };
161
350
  export type AccessChecker = (doc: DocumentNode, permissionType: 'read' | 'write') => Promise<boolean>;
162
351
  export type AccessWarning = {
163
352
  message: string;
@@ -174,7 +363,6 @@ export type ReadContextParams = {
174
363
  store: ReadContextDB;
175
364
  events?: DocumentEventEmitter;
176
365
  viewerDID: string;
177
- catalogID?: string;
178
366
  accessChecker?: AccessChecker;
179
367
  viewerReadAccess?: ViewerReadAccess;
180
368
  warnings?: Array<AccessWarning>;
@@ -216,6 +404,13 @@ export type SetMutationParams = {
216
404
  * viewer) owns. Omitted means owner = signer (the ops-level owner).
217
405
  */
218
406
  owner?: string;
407
+ /**
408
+ * Fetch-order token for a merge-policy-aware SET, forwarded to the engine's
409
+ * outcome-bearing seam. A model that declares a `providerMergePolicy` rejects
410
+ * a tokenless write; ordinary models ignore it. Optional, so non-connector
411
+ * callers stay source-compatible.
412
+ */
413
+ sourceFetchHLC?: string;
219
414
  };
220
415
  export type UpdateMutationParams = {
221
416
  input: UpdateMutationInput;
@@ -249,6 +444,19 @@ export type SetDocumentAccessOverrideParams = {
249
444
  export type MutationContext = {
250
445
  executeCreateMutation(params: CreateMutationParams): Promise<DocumentNode>;
251
446
  executeSetMutation(params: SetMutationParams): Promise<DocumentNode>;
447
+ /**
448
+ * Outcome-bearing SET, present only on the engine's signing context. The
449
+ * client and service graph contexts do not implement it, so a consumer that
450
+ * needs the merge outcome asserts presence with a runtime null check and
451
+ * fails closed (throws) when it is absent, rather than mis-committing through
452
+ * the plain SET seam. `document` is null when a gate dropped the projection.
453
+ */
454
+ executeSetMutationWithOutcome?(params: SetMutationParams): Promise<{
455
+ document: DocumentNode | null;
456
+ outcome?: 'applied' | 'dominated' | 'nonconforming';
457
+ candidateDigest?: string;
458
+ winnerDigest?: string;
459
+ }>;
252
460
  executeUpdateMutation(params: UpdateMutationParams): Promise<DocumentNode>;
253
461
  executeRemoveMutation(params: RemoveMutationParams): Promise<void>;
254
462
  executeSetModelAccessDefaults(params: SetModelAccessDefaultsParams): Promise<ModelAccessDefaults>;
@@ -283,4 +491,6 @@ export type MutationContext = {
283
491
  */
284
492
  export interface PluginContextMap {
285
493
  }
286
- export type Context = ReadOnlyContext & MutationContext & Partial<PluginContextMap>;
494
+ export type Context = ReadOnlyContext & MutationContext & {
495
+ catalog?: CatalogContext;
496
+ } & Partial<PluginContextMap>;
package/lib/context.js CHANGED
@@ -1,4 +1,4 @@
1
- import { DocumentID } from '@kubun/id';
1
+ import { CatalogID, DocumentID } from '@kubun/id';
2
2
  import { getKubunLogger } from '@kubun/logger';
3
3
  import { KubunAttributeKeys, KubunSpanNames } from '@kubun/protocol';
4
4
  import { defer } from '@sozai/async';
@@ -243,8 +243,9 @@ export function createReadContext(params) {
243
243
  modelIDs,
244
244
  filter: args.filter,
245
245
  owner: args.owner,
246
- catalogID: params.catalogID
246
+ catalogRef: args.catalogID == null ? undefined : CatalogID.fromString(args.catalogID).toRecordKey()
247
247
  };
248
+ const catalogRef = aggregateScope.catalogRef;
248
249
  const result = await traced(KubunSpanNames.DATA_QUERY, {
249
250
  [KubunAttributeKeys.MODEL_ID]: modelIDs.join(',')
250
251
  }, async ()=>{
@@ -253,7 +254,8 @@ export function createReadContext(params) {
253
254
  const searchResults = await store.searchDocuments({
254
255
  query: args.search,
255
256
  modelIDs,
256
- first: 200
257
+ first: 200,
258
+ catalogRef
257
259
  });
258
260
  if (searchResults.length === 0) {
259
261
  if (params.complexity != null) {
@@ -314,7 +316,7 @@ export function createReadContext(params) {
314
316
  const { entries, hasMore } = await store.queryDocuments({
315
317
  ...args,
316
318
  modelIDs,
317
- catalogID: params.catalogID
319
+ catalogRef
318
320
  });
319
321
  if (params.complexity != null) {
320
322
  params.complexity.resolverCount++;
@@ -357,7 +359,7 @@ export function createReadContext(params) {
357
359
  const { entries, hasMore } = await store.queryDocuments({
358
360
  ...args,
359
361
  modelIDs,
360
- catalogID: params.catalogID,
362
+ catalogRef,
361
363
  first: UNBOUNDED_SCAN_BATCH_SIZE,
362
364
  after: cursor
363
365
  });
@@ -399,7 +401,7 @@ export function createReadContext(params) {
399
401
  const { entries, hasMore } = await store.queryDocuments({
400
402
  ...args,
401
403
  modelIDs,
402
- catalogID: params.catalogID,
404
+ catalogRef,
403
405
  first: fetchSize,
404
406
  after: cursor
405
407
  });
@@ -430,7 +432,7 @@ export function createReadContext(params) {
430
432
  const { entries, hasMore } = await store.queryDocuments({
431
433
  ...args,
432
434
  modelIDs,
433
- catalogID: params.catalogID,
435
+ catalogRef,
434
436
  last: fetchSize,
435
437
  before: cursor
436
438
  });
@@ -539,7 +541,7 @@ export function createReadContext(params) {
539
541
  modelIDs,
540
542
  filter: aggregateParams.filter,
541
543
  owner: aggregateParams.owner,
542
- catalogID: aggregateParams.catalogID ?? params.catalogID,
544
+ catalogRef: aggregateParams.catalogRef,
543
545
  count: aggregateParams.count,
544
546
  aggregates: aggregateParams.aggregates,
545
547
  readAccess: viewerReadAccess
@@ -565,7 +567,7 @@ export function createReadContext(params) {
565
567
  modelIDs,
566
568
  filter: groupedParams.filter,
567
569
  owner: groupedParams.owner,
568
- catalogID: groupedParams.catalogID ?? params.catalogID,
570
+ catalogRef: groupedParams.catalogRef,
569
571
  count: groupedParams.count,
570
572
  aggregates: groupedParams.aggregates,
571
573
  groupBy: groupedParams.groupBy,
package/lib/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export type { AccessWarning, AggregateScope, ComplexityStats, Context, CreateMutationParams, DocumentEventEmitter, DocumentSavedEvent, MutationContext, PluginContextMap, QueryContext, ReadContextDB, ReadContextParams, ReadOnlyContext, RemoveMutationParams, ScopedConnection, SetDocumentAccessOverrideParams, SetModelAccessDefaultsParams, SetMutationParams, SubscriptionContext, UpdateMutationParams, } from './context.js';
1
+ export type { AccessWarning, AggregateScope, CatalogContext, CatalogCustody, CatalogDeviceStorage, CatalogDraft, CatalogListing, CatalogPullPreview, CatalogPullResult, CatalogRuleInput, CatalogRuleResolutionInput, CatalogRuleValue, CatalogUpdateInput, CatalogUpstreamLookup, CatalogUpstreamStatus, CatalogUpstreamUnavailableReason, CatalogValue, CatalogVersion, CatalogVersionConnection, CatalogVersionRef, ComplexityStats, Context, CreateMutationParams, CustodyStatus, DocumentEventEmitter, DocumentSavedEvent, FetchCatalogHeadVersionParams, MutationContext, PluginContextMap, QueryContext, ReadContextDB, ReadContextParams, ReadOnlyContext, RemoveMutationParams, ResolveCatalogPullParams, ScopedConnection, SetDocumentAccessOverrideParams, SetModelAccessDefaultsParams, SetMutationParams, SubscriptionContext, UpdateMutationParams, } from './context.js';
2
2
  export { createReadContext } from './context.js';
3
3
  export { GraphQLAttachmentID, GraphQLCredentialEntryID, GraphQLDocID, } from './identifiers.js';
4
4
  export type { ExtensionResolvers, SchemaBuilderParams, SharedDefinitions, } from './schema.js';
package/lib/schema.d.ts CHANGED
@@ -94,6 +94,7 @@ export declare class SchemaBuilder {
94
94
  _relationsTo: Record<string, RelationFields>;
95
95
  _extensionSDL: string | undefined;
96
96
  _extensionResolvers: ExtensionResolvers | undefined;
97
+ _skippedTypeExtensions: Set<string>;
97
98
  _stubExtensionResolvers: boolean;
98
99
  _pluginNodeResolvers: Record<string, {
99
100
  resolve: (localID: string, ctx: Context) => Promise<unknown> | unknown;