@simtlix/simfinity-core 3.13.0 → 3.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -3
- package/package.json +12 -2
- package/src/after-commit.js +83 -0
- package/src/const/QLQuantifier.js +21 -0
- package/src/index.js +1 -0
- package/src/introspection-config.js +17 -0
- package/src/introspection-state.js +10 -0
- package/src/introspection.js +5 -3
- package/src/query-emptiness.js +2 -2
- package/src/query-frames.js +510 -0
- package/src/query-limits.js +9 -2
- package/src/query-operators.js +23 -9
- package/src/query-plan.js +236 -155
- package/src/runtime.js +291 -137
- package/src/schema-registrations.js +18 -0
- package/src/transaction-callbacks.js +34 -0
- package/types/index.d.ts +132 -19
- package/types/introspection.d.ts +4 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Module-local ownership shared by consumers of this exact core installation. The snapshot is
|
|
2
|
+
// registration data only; descriptor formats and HTTP belong to opt-in consumer packages.
|
|
3
|
+
const registrationsBySchema = new WeakMap();
|
|
4
|
+
|
|
5
|
+
export const captureSchemaRegistrations = (schema, registrations, queryable) => {
|
|
6
|
+
const snapshot = registrations.map((entry) => Object.freeze({
|
|
7
|
+
...entry,
|
|
8
|
+
stateMachine: entry.stateMachine ? Object.freeze({
|
|
9
|
+
initialState: entry.stateMachine.initialState
|
|
10
|
+
? Object.freeze({ ...entry.stateMachine.initialState }) : null,
|
|
11
|
+
}) : null,
|
|
12
|
+
queryableFields: Object.freeze(Object.fromEntries(Object.values(entry.gqltype.getFields())
|
|
13
|
+
.map((field) => [field.name, queryable(entry.gqltype, field)]))),
|
|
14
|
+
}));
|
|
15
|
+
registrationsBySchema.set(schema, Object.freeze(snapshot));
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const schemaRegistrations = (schema) => registrationsBySchema.get(schema);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// The transaction owner creates a new queue for every attempt, even when the driver reuses the
|
|
2
|
+
// session. No session is adopted merely because it reports an active transaction.
|
|
3
|
+
export const createTransactionCallbacks = () => {
|
|
4
|
+
const active = new WeakMap();
|
|
5
|
+
const enqueue = (session, run) => {
|
|
6
|
+
const jobs = active.get(session);
|
|
7
|
+
if (!jobs) return false;
|
|
8
|
+
jobs.push(run);
|
|
9
|
+
return true;
|
|
10
|
+
};
|
|
11
|
+
const begin = (session) => {
|
|
12
|
+
const jobs = [];
|
|
13
|
+
let state = 'active';
|
|
14
|
+
const detach = () => { if (active.get(session) === jobs) active.delete(session); };
|
|
15
|
+
active.set(session, jobs);
|
|
16
|
+
return {
|
|
17
|
+
seal() { if (state === 'active') { state = 'sealed'; detach(); } },
|
|
18
|
+
confirm() { if (state === 'sealed') state = 'confirmed'; },
|
|
19
|
+
discard() { if (state !== 'confirmed' && state !== 'drained') { state = 'discarded'; detach(); jobs.length = 0; } },
|
|
20
|
+
async drain() {
|
|
21
|
+
if (state !== 'confirmed') return;
|
|
22
|
+
state = 'drained';
|
|
23
|
+
for (const run of jobs.splice(0)) {
|
|
24
|
+
try { await run(); } catch {
|
|
25
|
+
// Runtime jobs report their own failures. A faulty adapter consumer cannot change an
|
|
26
|
+
// already committed result, stop later jobs, or enter the driver's retry classifier.
|
|
27
|
+
try { console.error('After-commit callback failed'); } catch { /* Logging is best effort. */ }
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
return { begin, enqueue };
|
|
34
|
+
};
|
package/types/index.d.ts
CHANGED
|
@@ -12,6 +12,8 @@ import type {
|
|
|
12
12
|
|
|
13
13
|
/** The filter operator enum; its values are the {@link QueryOperator} names. */
|
|
14
14
|
export const QLOperator: GraphQLEnumType;
|
|
15
|
+
/** The collection and reference quantifier enum. */
|
|
16
|
+
export const QLQuantifier: GraphQLEnumType;
|
|
15
17
|
export const QLSort: GraphQLInputObjectType;
|
|
16
18
|
export const QLValue: GraphQLScalarType<unknown, unknown>;
|
|
17
19
|
|
|
@@ -45,19 +47,57 @@ export const MAX_RECORD_VERSION: number;
|
|
|
45
47
|
* embedded object or one whose stored members are all empty by these rules; the record identity is
|
|
46
48
|
* never empty. The target's `id` right after a reference is empty exactly when the reference is,
|
|
47
49
|
* including when the target is missing or hidden by its `find` scope. A path that continues inside an
|
|
48
|
-
* embedded list
|
|
50
|
+
* embedded list fails with `INVALID_FILTER_PATH`; shorthand through a collection excludes emptiness
|
|
51
|
+
* on a leaf, so use an explicit group. With an adapter that declares
|
|
49
52
|
* `queryFeatures.referenceEmptiness: true`, a path may also cross single references and end at a
|
|
50
53
|
* reference or a collection, and an embedded object may have reference and collection members: a
|
|
51
54
|
* reference is empty when no record it points to exists that the target's `find` scope admits for
|
|
52
55
|
* the caller, a collection when no such child exists, and a hidden record on the way reads as
|
|
53
56
|
* missing; constructs that middleware and scopes add read stored data. With any other adapter they
|
|
54
|
-
* fail with `UNSUPPORTED_QUERY`. A term or group condition without a path tests the field itself
|
|
57
|
+
* fail with `UNSUPPORTED_QUERY`. A term or group condition without a path tests the field itself, except that every term
|
|
58
|
+
* of a quantified relation filter needs a non-empty element-relative path.
|
|
55
59
|
*/
|
|
56
60
|
export type QueryOperator =
|
|
57
61
|
| 'EQ' | 'NE' | 'LT' | 'LTE' | 'GT' | 'GTE' | 'BTW' | 'IN' | 'NIN' | 'LIKE'
|
|
58
62
|
| 'ILIKE' | 'STARTS_WITH' | 'ISTARTS_WITH' | 'ENDS_WITH' | 'IENDS_WITH' | 'IEQ'
|
|
59
63
|
| 'IS_EMPTY' | 'IS_NOT_EMPTY';
|
|
60
64
|
|
|
65
|
+
/** The quantifier applied to the elements visible in a collection or reference frame. */
|
|
66
|
+
export type QueryQuantifier = 'ANY' | 'ALL' | 'NONE';
|
|
67
|
+
|
|
68
|
+
/** A condition inside a filter group, with paths relative to the current frame. */
|
|
69
|
+
export interface QLFilterCondition {
|
|
70
|
+
field: string;
|
|
71
|
+
operator?: QueryOperator | null;
|
|
72
|
+
value?: any;
|
|
73
|
+
path?: string | null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Recursive Boolean groups and quantified frames. */
|
|
77
|
+
export interface QLFilterGroup {
|
|
78
|
+
conditions?: Array<QLFilterCondition | null> | null;
|
|
79
|
+
AND?: Array<QLFilterGroup | null> | null;
|
|
80
|
+
OR?: Array<QLFilterGroup | null> | null;
|
|
81
|
+
ANY?: Array<QLQuantifiedGroup | null> | null;
|
|
82
|
+
ALL?: Array<QLQuantifiedGroup | null> | null;
|
|
83
|
+
NONE?: Array<QLQuantifiedGroup | null> | null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** A frame path ends at a collection or single reference without crossing a list; inner paths are element-relative. */
|
|
87
|
+
export interface QLQuantifiedGroup extends QLFilterGroup {
|
|
88
|
+
path: string;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** A quantified field filter may omit terms; every supplied term needs a non-empty element-relative path. */
|
|
92
|
+
export interface QLTypeFilterExpression {
|
|
93
|
+
quantifier?: QueryQuantifier | null;
|
|
94
|
+
terms?: Array<{
|
|
95
|
+
operator?: QueryOperator | null;
|
|
96
|
+
value?: any;
|
|
97
|
+
path?: string | null;
|
|
98
|
+
} | null> | null;
|
|
99
|
+
}
|
|
100
|
+
|
|
61
101
|
/**
|
|
62
102
|
* A condition of a {@link FilterGroup}, as the `QLFilterCondition` input takes it: the field it
|
|
63
103
|
* tests, its {@link QueryOperator} (`EQ` when omitted) and its value. On an object field, `path`
|
|
@@ -71,8 +111,11 @@ export interface FilterCondition {
|
|
|
71
111
|
path?: string;
|
|
72
112
|
}
|
|
73
113
|
|
|
74
|
-
/** A `QLFilterGroup` value: flat conditions
|
|
114
|
+
/** A `QLFilterGroup` value: flat conditions, Boolean groups and element-relative quantified groups. */
|
|
75
115
|
export interface FilterGroup {
|
|
116
|
+
ANY?: QLQuantifiedGroup[];
|
|
117
|
+
ALL?: QLQuantifiedGroup[];
|
|
118
|
+
NONE?: QLQuantifiedGroup[];
|
|
76
119
|
AND?: FilterGroup[];
|
|
77
120
|
OR?: FilterGroup[];
|
|
78
121
|
conditions?: FilterCondition[];
|
|
@@ -97,6 +140,15 @@ export interface QueryFeatures {
|
|
|
97
140
|
* must apply each target scope in `scopes`; constructs without a marker read stored data.
|
|
98
141
|
*/
|
|
99
142
|
referenceEmptiness?: boolean | null | undefined;
|
|
143
|
+
/**
|
|
144
|
+
* Whether the adapter compiles quantified frames and their scoped joins. Requires
|
|
145
|
+
* `referenceEmptiness: true`; undefined or null means false. Invalid declarations fail with
|
|
146
|
+
* `INVALID_QUERY_FEATURES` (500). Client markers have frame-relative scope keys; compilers must
|
|
147
|
+
* apply frame scopes before quantification and honour scoped joins/aggregate anchors. Without
|
|
148
|
+
* this flag explicit quantified forms fail with `UNSUPPORTED_QUERY`; collection filters keep
|
|
149
|
+
* legacy joins. All adapters reject list sorts through collections, including count mode.
|
|
150
|
+
*/
|
|
151
|
+
quantifiers?: boolean | null | undefined;
|
|
100
152
|
}
|
|
101
153
|
|
|
102
154
|
export interface ModelRegistration {
|
|
@@ -281,7 +333,27 @@ export class InternalServerError extends SimfinityError {
|
|
|
281
333
|
*/
|
|
282
334
|
export function buildErrorFormatter(callback?: (error: SimfinityError) => Error | void): (error: unknown) => GraphQLError;
|
|
283
335
|
|
|
284
|
-
export interface
|
|
336
|
+
export interface AfterCommitErrorDetails {
|
|
337
|
+
committed: true;
|
|
338
|
+
typeName: string;
|
|
339
|
+
operation: 'save' | 'update' | 'delete' | 'state_changed';
|
|
340
|
+
callbackName: 'onSaveCommitted' | 'onUpdateCommitted' | 'onDeleteCommitted' | 'onTransitionCommitted';
|
|
341
|
+
context: any;
|
|
342
|
+
}
|
|
343
|
+
export interface AfterCommitOptions {
|
|
344
|
+
/** Operational reporting only. Rejections are caught and never fail the committed write. */
|
|
345
|
+
onError?: (error: unknown, details: AfterCommitErrorDetails) => void | Promise<void>;
|
|
346
|
+
}
|
|
347
|
+
export interface CommittedControllerCallbacks {
|
|
348
|
+
/** After the owning transaction commits; a detached result and the successful attempt's input. */
|
|
349
|
+
onSaveCommitted?(record: any, args: any, context: any): void | Promise<void>;
|
|
350
|
+
onUpdateCommitted?(record: any, context: any): void | Promise<void>;
|
|
351
|
+
/** Called only when a record was actually deleted. */
|
|
352
|
+
onDeleteCommitted?(record: any, context: any): void | Promise<void>;
|
|
353
|
+
/** Exclusive notification for a state action; action is its registered name. */
|
|
354
|
+
onTransitionCommitted?(record: any, action: string, context: any): void | Promise<void>;
|
|
355
|
+
}
|
|
356
|
+
export interface EntityController<Session = any> extends CommittedControllerCallbacks {
|
|
285
357
|
onSaving?(record: any, args: any, session: Session | undefined, context: any): void | Promise<void>;
|
|
286
358
|
onSaved?(record: any, args: any, session: Session | undefined, context: any): void | Promise<void>;
|
|
287
359
|
onUpdating?(id: any, update: any, session: Session | undefined, context: any): void | Promise<void>;
|
|
@@ -383,11 +455,17 @@ export interface QueryLimitsOptions {
|
|
|
383
455
|
/** Positive safe integer; defaults to 1000. Unpaged lists use min(100, maxPageSize). */
|
|
384
456
|
maxPageSize?: number;
|
|
385
457
|
/**
|
|
386
|
-
* Positive safe integer; defaults to 10. The most
|
|
387
|
-
* client's
|
|
388
|
-
*
|
|
458
|
+
* Positive safe integer; defaults to 10. The most compiled frames and scoped joins that the
|
|
459
|
+
* client's conditions may need in one list, aggregate or collection-field call; more fail with
|
|
460
|
+
* `FILTER_DEPTH_EXCEEDED` (400), after every client 400 check and before middleware. Units count
|
|
461
|
+
* independently of markers: shorthand per group/prefix, explicit frames separately, scoped joins
|
|
462
|
+
* per containing frame/path/scope chain and emptiness frames per path/shape. Trusted units do
|
|
463
|
+
* not count toward this total. Named views check client arguments and their `where` groups
|
|
464
|
+
* against separate budgets of the same size; the `where` budget runs after middleware.
|
|
389
465
|
*/
|
|
390
466
|
maxReferenceFrames?: number;
|
|
467
|
+
/** Positive safe integer; defaults to 3. Maximum client/trusted collection or reference frames on one chain. */
|
|
468
|
+
maxFrameNesting?: number;
|
|
391
469
|
}
|
|
392
470
|
|
|
393
471
|
/**
|
|
@@ -427,6 +505,8 @@ export interface ViewOptions {
|
|
|
427
505
|
}
|
|
428
506
|
|
|
429
507
|
export interface Runtime<Model = any, Session = any> {
|
|
508
|
+
/** Per runtime. No arguments resets to safe default logging; invalid options leave it unchanged. */
|
|
509
|
+
configureAfterCommit(options?: AfterCommitOptions): void;
|
|
430
510
|
/** The same process-wide setting as the root `configureQueryLimits`. */
|
|
431
511
|
configureQueryLimits(options?: QueryLimitsOptions): void;
|
|
432
512
|
/**
|
|
@@ -580,6 +660,15 @@ export interface DatabaseAdapter<Model = any, Session = any> {
|
|
|
580
660
|
castId(value: any): any;
|
|
581
661
|
stateValue?(state: { name: string; value: any }): any;
|
|
582
662
|
withTransaction<T>(session: Session | null | undefined, callback: (session: Session) => Promise<T> | T, model?: Model): Promise<T>;
|
|
663
|
+
/**
|
|
664
|
+
* Optional capability for controller callbacks. Accept only into an active attempt owned by this
|
|
665
|
+
* adapter; false means the supplied session has no owned attempt. A new queue is required per
|
|
666
|
+
* retry, including retries on the same native session. Seal before commit, discard on abort or
|
|
667
|
+
* unresolved uncertainty, and drain once, serially, after confirmed commit and cleanup attempt.
|
|
668
|
+
* Callback failures must never enter transaction retry/rollback paths or fail a committed write.
|
|
669
|
+
* Borrowed operations join the owning attempt; this method does not observe external commits.
|
|
670
|
+
*/
|
|
671
|
+
enqueueAfterCommit?(session: Session, run: () => void | Promise<void>): boolean;
|
|
583
672
|
newRecord(model: Model, data: any, session?: Session): any;
|
|
584
673
|
saveRecord(model: Model, record: any, session?: Session): any;
|
|
585
674
|
toObject(record: any): any;
|
|
@@ -697,6 +786,9 @@ export interface DatabaseAdapter<Model = any, Session = any> {
|
|
|
697
786
|
}
|
|
698
787
|
export function createRuntime<Model = any, Session = any>(adapter: DatabaseAdapter<Model, Session>): Runtime<Model, Session>;
|
|
699
788
|
|
|
789
|
+
/** Scoped single-reference joins, keyed by a dotted prefix relative to the containing frame. */
|
|
790
|
+
export type QueryJoins = Record<string, QueryPredicate | null>;
|
|
791
|
+
|
|
700
792
|
/**
|
|
701
793
|
* `operator` is a {@link QueryOperator}; the value of a text operator is a validated string. For
|
|
702
794
|
* `IS_EMPTY` and `IS_NOT_EMPTY` the value is null, and `path` may end at an embedded object, an
|
|
@@ -705,17 +797,31 @@ export function createRuntime<Model = any, Session = any>(adapter: DatabaseAdapt
|
|
|
705
797
|
* references and end at a reference or a collection.
|
|
706
798
|
*
|
|
707
799
|
* `scopes` exists only on such an emptiness predicate from a client, and only for a compiler that
|
|
708
|
-
* declares `referenceEmptiness: true`. It has one key per dotted path
|
|
800
|
+
* declares `referenceEmptiness: true`. It has one key per dotted path relative to its frame: each
|
|
709
801
|
* single reference the path crosses (a scoped join, which admits the target only where the scope
|
|
710
802
|
* admits it), the end when it is a reference or a collection, and each reference and collection
|
|
711
803
|
* member of an embedded object (frames, which keep only the records the scope admits). Each value is
|
|
712
804
|
* the target's `find` scope as a predicate in the target's own frame, or null when the scope added
|
|
713
805
|
* nothing; after a scoped crossing, a null hop still correlates through that scoped join. Without
|
|
714
|
-
* `scopes` the predicate reads stored data, through plain joins.
|
|
806
|
+
* `scopes` the predicate reads stored data, through plain joins. `joins` carries the scopes of
|
|
807
|
+
* single references crossed inside a client frame, with keys relative to the containing frame.
|
|
808
|
+
* A `quantified` node selects elements with `ANY`, `ALL` or `NONE`, applying `scope` before `where`;
|
|
809
|
+
* both predicates use paths relative to the element, and null means no restriction. Empty elements
|
|
810
|
+
* give ANY false, ALL/NONE true; scope is never negated, and frames never multiply roots. `anchor` is
|
|
811
|
+
* an aggregate's legacy join chain; when present, `path` is relative to it and `joins` is absent.
|
|
715
812
|
*/
|
|
716
813
|
export type QueryPredicate =
|
|
717
|
-
| { kind: 'predicate'; path: string[]; operator: string; value: any; scopes?: Record<string, QueryPredicate | null
|
|
718
|
-
| { kind: 'and' | 'or'; terms: QueryPredicate[] }
|
|
814
|
+
| { kind: 'predicate'; path: string[]; operator: string; value: any; scopes?: Record<string, QueryPredicate | null>; joins?: QueryJoins }
|
|
815
|
+
| { kind: 'and' | 'or'; terms: QueryPredicate[] }
|
|
816
|
+
| {
|
|
817
|
+
kind: 'quantified';
|
|
818
|
+
quantifier: QueryQuantifier;
|
|
819
|
+
path: string[];
|
|
820
|
+
where: QueryPredicate | null;
|
|
821
|
+
scope: QueryPredicate | null;
|
|
822
|
+
joins?: QueryJoins;
|
|
823
|
+
anchor?: string[];
|
|
824
|
+
};
|
|
719
825
|
export interface QueryPlan {
|
|
720
826
|
entity: string;
|
|
721
827
|
where: QueryPredicate | null;
|
|
@@ -738,8 +844,8 @@ export interface QueryPlan {
|
|
|
738
844
|
* value inside an embedded list or a collection, `INVALID_FILTER_OPERATOR` for an embedded type that
|
|
739
845
|
* reaches itself through single embedded members, `INVALID_FILTER_VALUE` for a value that is not
|
|
740
846
|
* omitted or null, and `UNSUPPORTED_QUERY` for a reference or collection on the path or among the
|
|
741
|
-
* members. A term without a path (omitted, null or `""`) and a group
|
|
742
|
-
* the field itself. For other operators, a filter path of more than one segment that ends at an
|
|
847
|
+
* members. A nonquantified emptiness term without a path (omitted, null or `""`) and a group
|
|
848
|
+
* condition without a path test the field itself; quantified filter terms always need a path. For other operators, a filter path of more than one segment that ends at an
|
|
743
849
|
* object fails with `INVALID_FILTER_PATH`, as on MongoDB; a bare object field keeps
|
|
744
850
|
* `MISSING_FILTER_PATH`.
|
|
745
851
|
*
|
|
@@ -748,18 +854,22 @@ export interface QueryPlan {
|
|
|
748
854
|
* reads a reference or collection member, fails with `UNSUPPORTED_QUERY` where the order above has
|
|
749
855
|
* `UNSUPPORTED_QUERY` for references; when it is true, such a condition plans with stored semantics,
|
|
750
856
|
* unless its term or group condition carries a client scope marker that the runtime filled, which
|
|
751
|
-
* becomes the predicate's `scopes`.
|
|
857
|
+
* becomes the predicate's `scopes`. `quantifiers` has the same default rule and requires reference
|
|
858
|
+
* emptiness; enabled collection filters emit quantified nodes, with per-group/prefix correlation,
|
|
859
|
+
* element-relative scopes/joins and aggregate anchors beyond legacy J joins. `__scope`, `__implicit`
|
|
860
|
+
* and `__anchor` are reserved internal input metadata. A marker not filled for every path fails
|
|
752
861
|
* with `DETACHED_CLIENT_FILTER` (500).
|
|
753
862
|
*/
|
|
754
|
-
export function createQueryPlan(models: ModelDescription, entityName: string, input?: Record<string, any>, options?: { mode?: QueryPlan['mode']; operators?: Iterable<string>; referenceEmptiness?: boolean }): QueryPlan;
|
|
863
|
+
export function createQueryPlan(models: ModelDescription, entityName: string, input?: Record<string, any>, options?: { mode?: QueryPlan['mode']; operators?: Iterable<string>; referenceEmptiness?: boolean; quantifiers?: boolean }): QueryPlan;
|
|
755
864
|
export function resolveModelPath(models: ModelDescription, entityName: string, path: string | string[]): FieldDescription[];
|
|
756
865
|
|
|
757
866
|
/**
|
|
758
867
|
* Process-wide. `maxPageSize` (default 1000) caps page sizes. `maxReferenceFrames` (default 10) caps,
|
|
759
|
-
* per list, aggregate or collection-field call, the
|
|
760
|
-
*
|
|
761
|
-
*
|
|
762
|
-
*
|
|
868
|
+
* per list, aggregate or collection-field call, the compiled frames and scoped joins a client's
|
|
869
|
+
* conditions need. Named views check client arguments and `where` groups separately. `maxFrameNesting` (default 3) caps nested frames along one chain in client and
|
|
870
|
+
* trusted constructs. Exceeding either frame limit fails with `FILTER_DEPTH_EXCEEDED` (400). Each
|
|
871
|
+
* option must be a positive safe integer, or the call throws `INVALID_QUERY_LIMITS` (400) and keeps
|
|
872
|
+
* every current limit; an omitted option takes its default again.
|
|
763
873
|
*/
|
|
764
874
|
export function configureQueryLimits(options?: QueryLimitsOptions): void;
|
|
765
875
|
/**
|
|
@@ -1014,3 +1124,6 @@ export const plugins: {
|
|
|
1014
1124
|
};
|
|
1015
1125
|
};
|
|
1016
1126
|
};
|
|
1127
|
+
|
|
1128
|
+
/** Early opt-out. Import its function from /introspection before loading the runtime. */
|
|
1129
|
+
export interface IntrospectionOptions { fieldExtensions: false; }
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { IntrospectionOptions } from './index.js';
|
|
2
|
+
export type { IntrospectionOptions } from './index.js';
|
|
3
|
+
/** Must run before importing the runtime/facades. Never removes installed metadata. */
|
|
4
|
+
export function configureIntrospection(options: IntrospectionOptions): void;
|