@sequenceholdings/orm 2.8.3 → 2.8.5
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/dist/authoring.cjs +154 -45
- package/dist/authoring.cjs.map +1 -1
- package/dist/authoring.d.cts +12 -2
- package/dist/authoring.d.ts +12 -2
- package/dist/authoring.js +154 -45
- package/dist/authoring.js.map +1 -1
- package/dist/canonical.cjs +64 -14
- package/dist/canonical.cjs.map +1 -1
- package/dist/canonical.d.cts +12 -7
- package/dist/canonical.d.ts +12 -7
- package/dist/canonical.js +64 -14
- package/dist/canonical.js.map +1 -1
- package/dist/cli.cjs +78 -20
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +78 -20
- package/dist/cli.js.map +1 -1
- package/dist/compiler.cjs +77 -20
- package/dist/compiler.cjs.map +1 -1
- package/dist/compiler.d.cts +4 -4
- package/dist/compiler.d.ts +4 -4
- package/dist/compiler.js +77 -20
- package/dist/compiler.js.map +1 -1
- package/dist/index.cjs +161 -47
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -11
- package/dist/index.d.ts +11 -11
- package/dist/index.js +161 -47
- package/dist/index.js.map +1 -1
- package/dist/ir.cjs +71 -16
- package/dist/ir.cjs.map +1 -1
- package/dist/ir.d.cts +9 -9
- package/dist/ir.d.ts +9 -9
- package/dist/ir.js +71 -16
- package/dist/ir.js.map +1 -1
- package/dist/{naming-CU9ThP_E.d.cts → naming-BMZKFO0W.d.cts} +1 -2
- package/dist/{naming-CPfx7EfF.d.ts → naming-DUk7AcXs.d.ts} +1 -2
- package/dist/predicate-v2.cjs +71 -16
- package/dist/predicate-v2.cjs.map +1 -1
- package/dist/predicate-v2.d.cts +3 -3
- package/dist/predicate-v2.d.ts +3 -3
- package/dist/predicate-v2.js +71 -16
- package/dist/predicate-v2.js.map +1 -1
- package/dist/sdl.cjs +71 -16
- package/dist/sdl.cjs.map +1 -1
- package/dist/sdl.d.cts +2 -2
- package/dist/sdl.d.ts +2 -2
- package/dist/sdl.js +71 -16
- package/dist/sdl.js.map +1 -1
- package/package.json +1 -1
- package/src/codegen-gql/apollo-hooks.cjs +1 -1
- package/src/codegen-gql/server-fns.cjs +1 -1
- package/templates/orm-namespace/graphql/example/queries.ts +1 -1
package/dist/compiler.d.cts
CHANGED
|
@@ -4,7 +4,7 @@ import { DocumentNode, OperationDefinitionNode, FragmentDefinitionNode, GraphQLS
|
|
|
4
4
|
export { DynamicInputIssue, DynamicInputResult, NormalizedBoolExp, NormalizedOnConflict, NormalizedOrderBy, NormalizedRow, ORDER_BY_DIRECTIONS, ScalarKind, allowedBoolExpOperators, allowedOps, validateAppendInput, validateBoolExpInput, validateInsertInput, validateOnConflictInput, validateOrderByInput, validateSelectColumns, validateSetInput } from './dynamic-input.cjs';
|
|
5
5
|
export { CompilePolicyInput, CompilePredicateInput, POLICY_COMPARE_OPS, POLICY_MAX_LINK_DEPTH, ParsedPolicy, PolicyAccess, PolicyColumnInfo, PolicyCompareOp, PolicyCompileError, PolicyCompileErrorCode, PolicyLinkInfo, PolicyNode, PolicyOperand, PolicyParseError, PolicyResolver, PolicyTableInfo, assertClaimsOnlyWhen, compilePolicy, compilePredicateV2, parsePolicy, walkPolicy } from './predicate-v2.cjs';
|
|
6
6
|
import { z } from 'zod';
|
|
7
|
-
export { P as PLATFORM_USER_TYPENAME, c as camelCase, g as enumTypeName, p as pascalCase } from './naming-
|
|
7
|
+
export { P as PLATFORM_USER_TYPENAME, c as camelCase, g as enumTypeName, p as pascalCase } from './naming-BMZKFO0W.cjs';
|
|
8
8
|
export { S as SqlOperationDefinition, d as defineOperation, s as sqlOperationDefinitionSchema } from './definition-DjfC7Lll.cjs';
|
|
9
9
|
import './constants-Dou1RkTo.cjs';
|
|
10
10
|
import './policy-1iZbGnZZ.cjs';
|
|
@@ -223,7 +223,7 @@ interface ParsedDocument {
|
|
|
223
223
|
path: string;
|
|
224
224
|
document: DocumentNode;
|
|
225
225
|
operations: OperationDefinitionNode[];
|
|
226
|
-
/** Fragments are document-scoped —
|
|
226
|
+
/** Fragments are document-scoped — consumers colocate them via TS interpolation. */
|
|
227
227
|
fragments: Map<string, FragmentDefinitionNode>;
|
|
228
228
|
}
|
|
229
229
|
declare function parseSdl(sdl: string): GraphQLSchema;
|
|
@@ -277,8 +277,8 @@ declare function computePinnedColumnsByTable(canonical: CanonicalDefinition): Re
|
|
|
277
277
|
|
|
278
278
|
/**
|
|
279
279
|
* Flatten pass — inline every fragment spread (and inline fragment) so the
|
|
280
|
-
* lowering pass sees one plain selection tree.
|
|
281
|
-
* TS-interpolated fragments
|
|
280
|
+
* lowering pass sees one plain selection tree. Consumers typically use flat,
|
|
281
|
+
* TS-interpolated fragments; nested spreads still work here, but cycles
|
|
282
282
|
* and directives-on-spreads are rejected.
|
|
283
283
|
*/
|
|
284
284
|
|
package/dist/compiler.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { DocumentNode, OperationDefinitionNode, FragmentDefinitionNode, GraphQLS
|
|
|
4
4
|
export { DynamicInputIssue, DynamicInputResult, NormalizedBoolExp, NormalizedOnConflict, NormalizedOrderBy, NormalizedRow, ORDER_BY_DIRECTIONS, ScalarKind, allowedBoolExpOperators, allowedOps, validateAppendInput, validateBoolExpInput, validateInsertInput, validateOnConflictInput, validateOrderByInput, validateSelectColumns, validateSetInput } from './dynamic-input.js';
|
|
5
5
|
export { CompilePolicyInput, CompilePredicateInput, POLICY_COMPARE_OPS, POLICY_MAX_LINK_DEPTH, ParsedPolicy, PolicyAccess, PolicyColumnInfo, PolicyCompareOp, PolicyCompileError, PolicyCompileErrorCode, PolicyLinkInfo, PolicyNode, PolicyOperand, PolicyParseError, PolicyResolver, PolicyTableInfo, assertClaimsOnlyWhen, compilePolicy, compilePredicateV2, parsePolicy, walkPolicy } from './predicate-v2.js';
|
|
6
6
|
import { z } from 'zod';
|
|
7
|
-
export { P as PLATFORM_USER_TYPENAME, c as camelCase, g as enumTypeName, p as pascalCase } from './naming-
|
|
7
|
+
export { P as PLATFORM_USER_TYPENAME, c as camelCase, g as enumTypeName, p as pascalCase } from './naming-DUk7AcXs.js';
|
|
8
8
|
export { S as SqlOperationDefinition, d as defineOperation, s as sqlOperationDefinitionSchema } from './definition-DjfC7Lll.js';
|
|
9
9
|
import './constants-Dou1RkTo.js';
|
|
10
10
|
import './policy-1iZbGnZZ.js';
|
|
@@ -223,7 +223,7 @@ interface ParsedDocument {
|
|
|
223
223
|
path: string;
|
|
224
224
|
document: DocumentNode;
|
|
225
225
|
operations: OperationDefinitionNode[];
|
|
226
|
-
/** Fragments are document-scoped —
|
|
226
|
+
/** Fragments are document-scoped — consumers colocate them via TS interpolation. */
|
|
227
227
|
fragments: Map<string, FragmentDefinitionNode>;
|
|
228
228
|
}
|
|
229
229
|
declare function parseSdl(sdl: string): GraphQLSchema;
|
|
@@ -277,8 +277,8 @@ declare function computePinnedColumnsByTable(canonical: CanonicalDefinition): Re
|
|
|
277
277
|
|
|
278
278
|
/**
|
|
279
279
|
* Flatten pass — inline every fragment spread (and inline fragment) so the
|
|
280
|
-
* lowering pass sees one plain selection tree.
|
|
281
|
-
* TS-interpolated fragments
|
|
280
|
+
* lowering pass sees one plain selection tree. Consumers typically use flat,
|
|
281
|
+
* TS-interpolated fragments; nested spreads still work here, but cycles
|
|
282
282
|
* and directives-on-spreads are rejected.
|
|
283
283
|
*/
|
|
284
284
|
|
package/dist/compiler.js
CHANGED
|
@@ -264,6 +264,48 @@ var policyExpressionSchema = z.union([
|
|
|
264
264
|
variants: z.array(policyVariantSchema).max(10).default([])
|
|
265
265
|
})
|
|
266
266
|
]);
|
|
267
|
+
|
|
268
|
+
// src/canonical/resolved-rows.ts
|
|
269
|
+
function logicalKey(table) {
|
|
270
|
+
return (table.primaryKey ?? []).map((columnName) => table.columns.find((c) => c.columnName === columnName)?.name ?? columnName);
|
|
271
|
+
}
|
|
272
|
+
function sameKey(key, expected) {
|
|
273
|
+
return key.length === expected.length && key.every((name2) => expected.includes(name2));
|
|
274
|
+
}
|
|
275
|
+
function validateRowIdentity(input) {
|
|
276
|
+
validateExistsIn(input);
|
|
277
|
+
if (input.keys.length > 1) validateCompositeKey(input);
|
|
278
|
+
}
|
|
279
|
+
function validateExistsIn({ resolved, sourceByName, fail: fail4 }) {
|
|
280
|
+
const name2 = resolved.existsIn?.[0];
|
|
281
|
+
if (name2 === void 0) return;
|
|
282
|
+
if (sourceByName.get(name2)?.kind !== "etl") fail4(`existsIn '${name2}' must be an etl source`);
|
|
283
|
+
if (resolved.policies.insert !== void 0 || resolved.policies.write !== void 0) {
|
|
284
|
+
fail4("existsIn allows only an update policy: rows come from the etl source");
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
function validateCompositeKey({ resolved, keys, tableByName, fail: fail4 }) {
|
|
288
|
+
const keyList = `(${keys.join(", ")})`;
|
|
289
|
+
if (resolved.existsIn === void 0) {
|
|
290
|
+
fail4(`requires exactly one primary-key column: composite key ${keyList} requires existsIn`);
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
const writes = ["insert", "update", "write"].filter((kind) => resolved.policies[kind] !== void 0);
|
|
294
|
+
if (writes.length > 0) fail4(`composite key ${keyList} requires a read-only table: remove the ${writes.join("/")} policy`);
|
|
295
|
+
if (resolved.materialized) fail4(`composite key ${keyList} cannot be materialized`);
|
|
296
|
+
for (const source of resolved.sources) {
|
|
297
|
+
if (source.kind === "prefetch") {
|
|
298
|
+
fail4(`composite key ${keyList} cannot have prefetch source '${source.name}'`);
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
const table = tableByName.get(source.table);
|
|
302
|
+
if (table && !sameKey(logicalKey(table), keys)) {
|
|
303
|
+
fail4(`composite key ${keyList} requires ${source.kind} source '${source.name}' to be keyed by it; table '${source.table}' is keyed by (${logicalKey(table).join(", ")})`);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// src/canonical/resolved.ts
|
|
267
309
|
function parseDurationMs(input) {
|
|
268
310
|
if (typeof input === "number") return Number.isInteger(input) && input > 0 ? input : void 0;
|
|
269
311
|
const match = /^(\d+(?:\.\d+)?)(ms|s|m)$/.exec(input);
|
|
@@ -340,6 +382,13 @@ var canonicalResolvedSchema = z.object({
|
|
|
340
382
|
resolve: z.array(canonicalResolveRuleSchema).max(500),
|
|
341
383
|
/** Sources whose tombstone hides the resolved row (v1: the manual source only). */
|
|
342
384
|
deletedBy: z.array(identifier).max(1).default([]),
|
|
385
|
+
/**
|
|
386
|
+
* `rows.existsIn`: the one etl source whose rows are the table's rows (read
|
|
387
|
+
* directly as the row source). Only an update policy may write. It is also
|
|
388
|
+
* what admits a composite primary key: a read-only table keyed exactly like
|
|
389
|
+
* that source.
|
|
390
|
+
*/
|
|
391
|
+
existsIn: z.array(identifier).length(1).optional(),
|
|
343
392
|
/**
|
|
344
393
|
* Materialized: the view reads an indexed internal table (`table`) that the
|
|
345
394
|
* data plane keeps in step with `compute`, the internal view holding the
|
|
@@ -358,8 +407,8 @@ function validateResolvedView({ view, tables, views = [] }) {
|
|
|
358
407
|
if (!resolved) return [];
|
|
359
408
|
const errors = [];
|
|
360
409
|
const fail4 = (message) => errors.push(`resolved table '${view.view}': ${message}`);
|
|
361
|
-
const
|
|
362
|
-
if (
|
|
410
|
+
const keyColumns = (view.primaryKey ?? []).map((columnName) => view.columns.find((c) => c.columnName === columnName)).filter((column) => column !== void 0);
|
|
411
|
+
if (keyColumns.length === 0 || keyColumns.length !== view.primaryKey?.length) {
|
|
363
412
|
fail4("requires exactly one primary-key column");
|
|
364
413
|
return errors;
|
|
365
414
|
}
|
|
@@ -367,7 +416,7 @@ function validateResolvedView({ view, tables, views = [] }) {
|
|
|
367
416
|
const scope = {
|
|
368
417
|
view,
|
|
369
418
|
resolved,
|
|
370
|
-
|
|
419
|
+
keys: new Map(keyColumns.map((column) => [column.name, column])),
|
|
371
420
|
dataColumns: new Map(view.columns.filter((c) => !systemNames.has(c.name) && c.cell === void 0).map((c) => [c.name, c])),
|
|
372
421
|
sourceByName: /* @__PURE__ */ new Map(),
|
|
373
422
|
fail: fail4
|
|
@@ -381,6 +430,7 @@ function validateResolvedView({ view, tables, views = [] }) {
|
|
|
381
430
|
if (!table) fail4(`source '${source.name}' references unknown table '${source.table}'`);
|
|
382
431
|
else validateSource({ scope, source, table });
|
|
383
432
|
}
|
|
433
|
+
validateRowIdentity({ resolved, keys: [...scope.keys.keys()], sourceByName: scope.sourceByName, tableByName, fail: fail4 });
|
|
384
434
|
validateRules(scope);
|
|
385
435
|
validateViewCells(scope);
|
|
386
436
|
if (resolved.materialized) validateMaterialized({ scope, tableByName, views });
|
|
@@ -410,7 +460,7 @@ function validateSource({ scope, source, table }) {
|
|
|
410
460
|
validatePrefetchFields({ scope, source });
|
|
411
461
|
}
|
|
412
462
|
function validatePrefetchFields({ scope, source }) {
|
|
413
|
-
const { fail: fail4,
|
|
463
|
+
const { fail: fail4, keys } = scope;
|
|
414
464
|
if (source.kind !== "prefetch") {
|
|
415
465
|
if (source.key || source.fetch || source.function || source.prefetchDefaults || source.writeback) {
|
|
416
466
|
fail4(`only prefetch sources may declare key/fetch/function/prefetchDefaults/writeback ('${source.name}')`);
|
|
@@ -419,7 +469,7 @@ function validatePrefetchFields({ scope, source }) {
|
|
|
419
469
|
}
|
|
420
470
|
if (!source.key || !source.fetch || !source.function || !source.prefetchDefaults) {
|
|
421
471
|
fail4(`prefetch source '${source.name}' requires key, fetch, function and prefetchDefaults`);
|
|
422
|
-
} else if (source.key
|
|
472
|
+
} else if (!keys.has(source.key) && !source.columns.includes(source.key)) {
|
|
423
473
|
fail4(`prefetch source '${source.name}' key '${source.key}' must be the primary key or one of its columns`);
|
|
424
474
|
}
|
|
425
475
|
for (const column of source.writeback?.columns ?? []) {
|
|
@@ -427,19 +477,19 @@ function validatePrefetchFields({ scope, source }) {
|
|
|
427
477
|
}
|
|
428
478
|
}
|
|
429
479
|
function validateSourceColumns({ scope, source, byName }) {
|
|
430
|
-
const { fail: fail4,
|
|
431
|
-
for (const column of [
|
|
432
|
-
const own = column
|
|
480
|
+
const { fail: fail4, keys, dataColumns } = scope;
|
|
481
|
+
for (const column of [...keys.keys(), ...source.columns]) {
|
|
482
|
+
const own = keys.get(column) ?? dataColumns.get(column);
|
|
433
483
|
const theirs = byName.get(column);
|
|
434
484
|
if (!own) fail4(`source '${source.name}' supplies unknown column '${column}'`);
|
|
435
485
|
else if (!theirs) fail4(`source '${source.name}' table '${source.table}' has no column '${column}'`);
|
|
436
486
|
else if (JSON.stringify(theirs.type) !== JSON.stringify(own.type)) fail4(`column '${column}' has a different type on source '${source.name}'`);
|
|
437
487
|
}
|
|
438
488
|
}
|
|
439
|
-
function validateRules({ resolved,
|
|
489
|
+
function validateRules({ resolved, keys, dataColumns, sourceByName, fail: fail4 }) {
|
|
440
490
|
const ruled = /* @__PURE__ */ new Set();
|
|
441
491
|
for (const rule of resolved.resolve) {
|
|
442
|
-
if (!dataColumns.has(rule.column) || rule.column
|
|
492
|
+
if (!dataColumns.has(rule.column) || keys.has(rule.column)) {
|
|
443
493
|
fail4(`resolve rule targets unknown column '${rule.column}'`);
|
|
444
494
|
continue;
|
|
445
495
|
}
|
|
@@ -456,7 +506,7 @@ function validateRules({ resolved, pkColumn, dataColumns, sourceByName, fail: fa
|
|
|
456
506
|
}
|
|
457
507
|
}
|
|
458
508
|
for (const column of dataColumns.keys()) {
|
|
459
|
-
if (column
|
|
509
|
+
if (!keys.has(column) && !ruled.has(column)) fail4(`column '${column}' has no resolve rule`);
|
|
460
510
|
}
|
|
461
511
|
}
|
|
462
512
|
var VIEW_CELL_TYPES = {
|
|
@@ -465,10 +515,10 @@ var VIEW_CELL_TYPES = {
|
|
|
465
515
|
by: "text",
|
|
466
516
|
conflicts: "jsonb"
|
|
467
517
|
};
|
|
468
|
-
function validateViewCells({ view, dataColumns,
|
|
518
|
+
function validateViewCells({ view, dataColumns, keys, fail: fail4 }) {
|
|
469
519
|
const expected = /* @__PURE__ */ new Map();
|
|
470
520
|
for (const column of dataColumns.values()) {
|
|
471
|
-
if (column.name
|
|
521
|
+
if (keys.has(column.name)) continue;
|
|
472
522
|
for (const part of RESOLVED_VIEW_CELL_PARTS) {
|
|
473
523
|
expected.set(cellColumnName(column.name, part), { part, columnName: cellColumnName(column.columnName, part) });
|
|
474
524
|
}
|
|
@@ -836,7 +886,7 @@ var canonicalTableSchema = z.object({
|
|
|
836
886
|
table: identifier2,
|
|
837
887
|
displayName: z.string().trim().min(1).max(120).optional(),
|
|
838
888
|
description: z.string().max(2e3).optional(),
|
|
839
|
-
/** Table-level rename hint
|
|
889
|
+
/** Table-level rename hint. */
|
|
840
890
|
renamedFrom: identifier2.optional(),
|
|
841
891
|
/** Synced/reflected read-only tables (`source: schema.table`) — v1 semantics. */
|
|
842
892
|
source: z.string().regex(QUALIFIED_TABLE_PATTERN).optional(),
|
|
@@ -1694,8 +1744,13 @@ var resolvedSourceIrSchema = z.object({
|
|
|
1694
1744
|
}).optional()
|
|
1695
1745
|
});
|
|
1696
1746
|
var resolvedMetaIrSchema = z.object({
|
|
1697
|
-
/**
|
|
1698
|
-
|
|
1747
|
+
/**
|
|
1748
|
+
* Logical pk column of the resolved view. Absent for a composite key, which
|
|
1749
|
+
* only a read-only `existsIn` table may declare: it has no writes, prefetch
|
|
1750
|
+
* or materialization (the paths that address one key column), and its reads
|
|
1751
|
+
* use the view's `primaryKey` like any keyed view.
|
|
1752
|
+
*/
|
|
1753
|
+
pk: z.string().optional(),
|
|
1699
1754
|
/** Source name of the manual source. */
|
|
1700
1755
|
manual: z.string(),
|
|
1701
1756
|
sources: z.array(resolvedSourceIrSchema),
|
|
@@ -3151,7 +3206,7 @@ function buildCompileContext(rawCanonical) {
|
|
|
3151
3206
|
appendable: readOnly ? [] : active.filter((c) => c.type === "jsonb").map((c) => c.name),
|
|
3152
3207
|
...touchOnUpdate.length > 0 ? { touchOnUpdate } : {},
|
|
3153
3208
|
...cells ? { cells } : {},
|
|
3154
|
-
...resolved ? { resolved: resolvedMeta({ resolved,
|
|
3209
|
+
...resolved ? { resolved: resolvedMeta({ resolved, primaryKey }) } : {},
|
|
3155
3210
|
constraints: isView ? [] : constraints,
|
|
3156
3211
|
dynamicFilterMaxDepth: 6,
|
|
3157
3212
|
dynamicFilterMaxNodes: 64
|
|
@@ -3433,10 +3488,10 @@ function resolvedWritable(entries) {
|
|
|
3433
3488
|
const system = new Set(Object.values(RESOLVED_VIEW_SYSTEM_COLUMNS).map((column) => column.name));
|
|
3434
3489
|
return entries.filter((entry) => !entry.deprecated && !system.has(entry.name)).map((entry) => entry.name);
|
|
3435
3490
|
}
|
|
3436
|
-
function resolvedMeta({ resolved,
|
|
3491
|
+
function resolvedMeta({ resolved, primaryKey }) {
|
|
3437
3492
|
const manual = resolved.sources.find((source) => source.kind === "manual");
|
|
3438
3493
|
return {
|
|
3439
|
-
pk,
|
|
3494
|
+
...primaryKey.length === 1 ? { pk: primaryKey[0] } : {},
|
|
3440
3495
|
manual: manual?.name ?? "",
|
|
3441
3496
|
deletedBy: [...resolved.deletedBy],
|
|
3442
3497
|
...resolved.materialized ? { materialized: { ...resolved.materialized } } : {},
|
|
@@ -3562,7 +3617,9 @@ function applyPrefetchDirectives({ node, root, meta }) {
|
|
|
3562
3617
|
if (directive.maxStaleness !== void 0 && maxStalenessMs === void 0) {
|
|
3563
3618
|
fail(`@prefetch(maxStaleness: "${directive.maxStaleness}") must look like '500ms', '30s' or '5m'`, "invalid-source-directive");
|
|
3564
3619
|
}
|
|
3565
|
-
const
|
|
3620
|
+
const pk = meta.resolved?.pk;
|
|
3621
|
+
if (pk === void 0) return fail(`@prefetch on '${root.alias}' needs a single-column primary key`, "invalid-source-directive");
|
|
3622
|
+
const key = root.kind === "byPk" ? { one: root.pk[0].value } : selectKey({ where: root.where, pk, source, alias: root.alias });
|
|
3566
3623
|
return { source: source.name, ...maxStalenessMs !== void 0 ? { maxStalenessMs } : {}, key };
|
|
3567
3624
|
});
|
|
3568
3625
|
return { ...root, prefetch: plans };
|