@krantho/persistence 0.0.0-stage → 1.0.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.
- package/LICENSE +21 -0
- package/dist/auth.d.ts +79 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +2 -0
- package/dist/auth.js.map +1 -0
- package/dist/content-contract.d.ts +36 -0
- package/dist/content-contract.d.ts.map +1 -0
- package/dist/content-contract.js +70 -0
- package/dist/content-contract.js.map +1 -0
- package/dist/content-query.d.ts +48 -0
- package/dist/content-query.d.ts.map +1 -0
- package/dist/content-query.js +5 -0
- package/dist/content-query.js.map +1 -0
- package/dist/content-row.d.ts +59 -0
- package/dist/content-row.d.ts.map +1 -0
- package/dist/content-row.js +60 -0
- package/dist/content-row.js.map +1 -0
- package/dist/index.d.ts +503 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +36 -0
- package/dist/index.js.map +1 -0
- package/dist/live-version.d.ts +44 -0
- package/dist/live-version.d.ts.map +1 -0
- package/dist/live-version.js +74 -0
- package/dist/live-version.js.map +1 -0
- package/dist/migration-utils.d.ts +35 -0
- package/dist/migration-utils.d.ts.map +1 -0
- package/dist/migration-utils.js +50 -0
- package/dist/migration-utils.js.map +1 -0
- package/dist/migrations.d.ts +119 -0
- package/dist/migrations.d.ts.map +1 -0
- package/dist/migrations.js +2 -0
- package/dist/migrations.js.map +1 -0
- package/dist/page-cache.d.ts +77 -0
- package/dist/page-cache.d.ts.map +1 -0
- package/dist/page-cache.js +65 -0
- package/dist/page-cache.js.map +1 -0
- package/dist/repository-content.d.ts +45 -0
- package/dist/repository-content.d.ts.map +1 -0
- package/dist/repository-content.js +94 -0
- package/dist/repository-content.js.map +1 -0
- package/dist/sort-columns.d.ts +19 -0
- package/dist/sort-columns.d.ts.map +1 -0
- package/dist/sort-columns.js +21 -0
- package/dist/sort-columns.js.map +1 -0
- package/package.json +35 -3
- package/README.md +0 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Descoped
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/auth.d.ts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/** Role in the krantho permission system */
|
|
2
|
+
export type Role = 'admin' | 'editor' | 'viewer';
|
|
3
|
+
/** A user record in the database */
|
|
4
|
+
export interface UserRecord {
|
|
5
|
+
id: string;
|
|
6
|
+
email: string | null;
|
|
7
|
+
name: string | null;
|
|
8
|
+
roles: Role[];
|
|
9
|
+
oidcSubject: string | null;
|
|
10
|
+
oidcIssuer: string | null;
|
|
11
|
+
isActive: boolean;
|
|
12
|
+
createdAt: string;
|
|
13
|
+
updatedAt: string;
|
|
14
|
+
}
|
|
15
|
+
/** Data for creating a new user */
|
|
16
|
+
export interface CreateUserData {
|
|
17
|
+
email: string | null;
|
|
18
|
+
name: string | null;
|
|
19
|
+
roles: Role[];
|
|
20
|
+
oidcSubject?: string | undefined;
|
|
21
|
+
oidcIssuer?: string | undefined;
|
|
22
|
+
}
|
|
23
|
+
/** Data for updating a user */
|
|
24
|
+
export interface UpdateUserData {
|
|
25
|
+
email?: string | undefined;
|
|
26
|
+
name?: string | undefined;
|
|
27
|
+
roles?: Role[] | undefined;
|
|
28
|
+
isActive?: boolean | undefined;
|
|
29
|
+
oidcSubject?: string | undefined;
|
|
30
|
+
oidcIssuer?: string | undefined;
|
|
31
|
+
}
|
|
32
|
+
/** A refresh token record */
|
|
33
|
+
export interface RefreshTokenRecord {
|
|
34
|
+
id: string;
|
|
35
|
+
userId: string;
|
|
36
|
+
tokenHash: string;
|
|
37
|
+
expiresAt: string;
|
|
38
|
+
createdAt: string;
|
|
39
|
+
}
|
|
40
|
+
/** Per-collection permission override */
|
|
41
|
+
export interface UserPermission {
|
|
42
|
+
userId: string;
|
|
43
|
+
collection: string;
|
|
44
|
+
role: Role;
|
|
45
|
+
}
|
|
46
|
+
/** Auth adapter interface — users, refresh tokens, permissions */
|
|
47
|
+
export interface IAuthAdapter {
|
|
48
|
+
/** Create a user and return the created record */
|
|
49
|
+
createUser(data: CreateUserData): Promise<UserRecord>;
|
|
50
|
+
/** Get a user by ID */
|
|
51
|
+
getUser(id: string): Promise<UserRecord | null>;
|
|
52
|
+
/** Get a user by email */
|
|
53
|
+
getUserByEmail(email: string): Promise<UserRecord | null>;
|
|
54
|
+
/** Get a user by OIDC identity (issuer + subject) */
|
|
55
|
+
getUserByOidc(issuer: string, subject: string): Promise<UserRecord | null>;
|
|
56
|
+
/** List all users */
|
|
57
|
+
listUsers(): Promise<UserRecord[]>;
|
|
58
|
+
/** Update a user */
|
|
59
|
+
updateUser(id: string, data: UpdateUserData): Promise<UserRecord | null>;
|
|
60
|
+
/** Delete a user (cascades refresh tokens and permissions) */
|
|
61
|
+
deleteUser(id: string): Promise<boolean>;
|
|
62
|
+
/** Store a refresh token hash for a user */
|
|
63
|
+
createRefreshToken(userId: string, tokenHash: string, expiresAt: string): Promise<string>;
|
|
64
|
+
/** Find a refresh token by hash */
|
|
65
|
+
getRefreshTokenByHash(tokenHash: string): Promise<RefreshTokenRecord | null>;
|
|
66
|
+
/** Delete a refresh token by ID (used during rotation) */
|
|
67
|
+
deleteRefreshToken(id: string): Promise<void>;
|
|
68
|
+
/** Delete all refresh tokens for a user (used on logout/deactivation) */
|
|
69
|
+
deleteUserRefreshTokens(userId: string): Promise<void>;
|
|
70
|
+
/** Delete expired refresh tokens (cleanup on startup) */
|
|
71
|
+
deleteExpiredRefreshTokens(): Promise<number>;
|
|
72
|
+
/** Get all permission overrides for a user */
|
|
73
|
+
getUserPermissions(userId: string): Promise<UserPermission[]>;
|
|
74
|
+
/** Set a per-collection permission override (upsert) */
|
|
75
|
+
setUserPermission(userId: string, collection: string, role: Role): Promise<void>;
|
|
76
|
+
/** Delete a per-collection permission override */
|
|
77
|
+
deleteUserPermission(userId: string, collection: string): Promise<boolean>;
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAC5C,MAAM,MAAM,IAAI,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAA;AAEhD,oCAAoC;AACpC,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,KAAK,EAAE,IAAI,EAAE,CAAA;IACb,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,QAAQ,EAAE,OAAO,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,mCAAmC;AACnC,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,KAAK,EAAE,IAAI,EAAE,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAChC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAChC;AAED,+BAA+B;AAC/B,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACzB,KAAK,CAAC,EAAE,IAAI,EAAE,GAAG,SAAS,CAAA;IAC1B,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC9B,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAChC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAChC;AAED,6BAA6B;AAC7B,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,yCAAyC;AACzC,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,MAAM,CAAA;IAClB,IAAI,EAAE,IAAI,CAAA;CACX;AAED,kEAAkE;AAClE,MAAM,WAAW,YAAY;IAE3B,kDAAkD;IAClD,UAAU,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC,CAAA;IACrD,uBAAuB;IACvB,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAA;IAC/C,0BAA0B;IAC1B,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAA;IACzD,qDAAqD;IACrD,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAA;IAC1E,qBAAqB;IACrB,SAAS,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC,CAAA;IAClC,oBAAoB;IACpB,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAA;IACxE,8DAA8D;IAC9D,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAGxC,4CAA4C;IAC5C,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IACzF,mCAAmC;IACnC,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAAA;IAC5E,0DAA0D;IAC1D,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC7C,yEAAyE;IACzE,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACtD,yDAAyD;IACzD,0BAA0B,IAAI,OAAO,CAAC,MAAM,CAAC,CAAA;IAG7C,8CAA8C;IAC9C,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAAA;IAC7D,wDAAwD;IACxD,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAChF,kDAAkD;IAClD,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;CAC3E"}
|
package/dist/auth.js
ADDED
package/dist/auth.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type AuthoredMetadata } from '@krantho/core';
|
|
2
|
+
export type { AuthoredMetadata, ContentContract, ContractReference, DefaultProvenance, MetadataGroupContract, } from '@krantho/core';
|
|
3
|
+
export { authoredMetadata } from '@krantho/core';
|
|
4
|
+
/** An atomic document write refused a stale revision or a duplicate create. */
|
|
5
|
+
export declare class ContentWriteConflict extends Error {
|
|
6
|
+
readonly currentVersion: number;
|
|
7
|
+
readonly expectedVersion: number;
|
|
8
|
+
constructor(currentVersion: number, expectedVersion: number);
|
|
9
|
+
}
|
|
10
|
+
/** The authored values a write names; one left undefined keeps the previous version's */
|
|
11
|
+
export type AuthoredWrite = {
|
|
12
|
+
readonly [K in keyof AuthoredMetadata]?: AuthoredMetadata[K] | undefined;
|
|
13
|
+
};
|
|
14
|
+
/** The authored values a write names; the ones it leaves out stay undefined */
|
|
15
|
+
export declare function authoredOf(write: AuthoredWrite): AuthoredWrite;
|
|
16
|
+
/**
|
|
17
|
+
* The authored values a new version stores: what the write names and, for
|
|
18
|
+
* whatever it leaves out, the previous version's. A write that touches only
|
|
19
|
+
* fields or the body never clears a document's metadata, contract or
|
|
20
|
+
* provenance. A repository basis agreed at version zero takes the new version.
|
|
21
|
+
*/
|
|
22
|
+
export declare function nextAuthored(previous: AuthoredWrite | null, write: AuthoredWrite, version: number): AuthoredMetadata;
|
|
23
|
+
/** Repository agreement is bookkeeping, not an author-owned value. */
|
|
24
|
+
export declare function sameAuthoredValues(left: Partial<AuthoredMetadata>, right: Partial<AuthoredMetadata>): boolean;
|
|
25
|
+
/**
|
|
26
|
+
* A `doc:<collection>/<slug>` SEO config row as the document it belongs to and
|
|
27
|
+
* its config object, unwrapping a config stored as JSON text. Null when the id
|
|
28
|
+
* names no document or the config is not an object, so a migration leaves the
|
|
29
|
+
* row where it is.
|
|
30
|
+
*/
|
|
31
|
+
export declare function documentSeoRow(id: string, config: unknown): {
|
|
32
|
+
collection: string;
|
|
33
|
+
slug: string;
|
|
34
|
+
config: Record<string, unknown>;
|
|
35
|
+
} | null;
|
|
36
|
+
//# sourceMappingURL=content-contract.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-contract.d.ts","sourceRoot":"","sources":["../src/content-contract.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,gBAAgB,EAAmC,MAAM,eAAe,CAAA;AAEtF,YAAY,EACV,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAEhD,+EAA+E;AAC/E,qBAAa,oBAAqB,SAAQ,KAAK;IAE3C,QAAQ,CAAC,cAAc,EAAE,MAAM;IAC/B,QAAQ,CAAC,eAAe,EAAE,MAAM;gBADvB,cAAc,EAAE,MAAM,EACtB,eAAe,EAAE,MAAM;CAKnC;AAED,yFAAyF;AACzF,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,EAAE,CAAC,IAAI,MAAM,gBAAgB,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,SAAS;CACzE,CAAA;AAED,+EAA+E;AAC/E,wBAAgB,UAAU,CAAC,KAAK,EAAE,aAAa,GAAG,aAAa,CAO9D;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,aAAa,GAAG,IAAI,EAC9B,KAAK,EAAE,aAAa,EACpB,OAAO,EAAE,MAAM,GACd,gBAAgB,CASlB;AAED,sEAAsE;AACtE,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,OAAO,CAAC,gBAAgB,CAAC,EAC/B,KAAK,EAAE,OAAO,CAAC,gBAAgB,CAAC,GAC/B,OAAO,CAMT;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,OAAO,GACd;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAAG,IAAI,CAa9E"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { authoredMetadata, canonicalJson } from '@krantho/core';
|
|
2
|
+
export { authoredMetadata } from '@krantho/core';
|
|
3
|
+
/** An atomic document write refused a stale revision or a duplicate create. */
|
|
4
|
+
export class ContentWriteConflict extends Error {
|
|
5
|
+
currentVersion;
|
|
6
|
+
expectedVersion;
|
|
7
|
+
constructor(currentVersion, expectedVersion) {
|
|
8
|
+
super('Content has been modified by another user');
|
|
9
|
+
this.currentVersion = currentVersion;
|
|
10
|
+
this.expectedVersion = expectedVersion;
|
|
11
|
+
this.name = 'ContentWriteConflict';
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/** The authored values a write names; the ones it leaves out stay undefined */
|
|
15
|
+
export function authoredOf(write) {
|
|
16
|
+
return {
|
|
17
|
+
metadata: write.metadata,
|
|
18
|
+
contract: write.contract,
|
|
19
|
+
defaults: write.defaults,
|
|
20
|
+
repositoryBasis: write.repositoryBasis,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The authored values a new version stores: what the write names and, for
|
|
25
|
+
* whatever it leaves out, the previous version's. A write that touches only
|
|
26
|
+
* fields or the body never clears a document's metadata, contract or
|
|
27
|
+
* provenance. A repository basis agreed at version zero takes the new version.
|
|
28
|
+
*/
|
|
29
|
+
export function nextAuthored(previous, write, version) {
|
|
30
|
+
const kept = authoredMetadata(previous ?? {});
|
|
31
|
+
const basis = write.repositoryBasis !== undefined ? write.repositoryBasis : kept.repositoryBasis;
|
|
32
|
+
return {
|
|
33
|
+
metadata: write.metadata ?? kept.metadata,
|
|
34
|
+
contract: write.contract !== undefined ? write.contract : kept.contract,
|
|
35
|
+
defaults: write.defaults ?? kept.defaults,
|
|
36
|
+
repositoryBasis: basis?.version === 0 ? { ...basis, version } : basis,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/** Repository agreement is bookkeeping, not an author-owned value. */
|
|
40
|
+
export function sameAuthoredValues(left, right) {
|
|
41
|
+
const semantic = (input) => {
|
|
42
|
+
const { repositoryBasis: _basis, ...values } = authoredMetadata(input);
|
|
43
|
+
return canonicalJson(values);
|
|
44
|
+
};
|
|
45
|
+
return semantic(left) === semantic(right);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* A `doc:<collection>/<slug>` SEO config row as the document it belongs to and
|
|
49
|
+
* its config object, unwrapping a config stored as JSON text. Null when the id
|
|
50
|
+
* names no document or the config is not an object, so a migration leaves the
|
|
51
|
+
* row where it is.
|
|
52
|
+
*/
|
|
53
|
+
export function documentSeoRow(id, config) {
|
|
54
|
+
const match = /^doc:([^/]+)\/(.+)$/.exec(id);
|
|
55
|
+
if (!match?.[1] || !match[2])
|
|
56
|
+
return null;
|
|
57
|
+
let value = config;
|
|
58
|
+
for (let depth = 0; typeof value === 'string' && depth < 2; depth++) {
|
|
59
|
+
try {
|
|
60
|
+
value = JSON.parse(value);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value))
|
|
67
|
+
return null;
|
|
68
|
+
return { collection: match[1], slug: match[2], config: value };
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=content-contract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-contract.js","sourceRoot":"","sources":["../src/content-contract.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyB,gBAAgB,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAStF,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAEhD,+EAA+E;AAC/E,MAAM,OAAO,oBAAqB,SAAQ,KAAK;IAElC;IACA;IAFX,YACW,cAAsB,EACtB,eAAuB;QAEhC,KAAK,CAAC,2CAA2C,CAAC,CAAA;QAHzC,mBAAc,GAAd,cAAc,CAAQ;QACtB,oBAAe,GAAf,eAAe,CAAQ;QAGhC,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAA;IACpC,CAAC;CACF;AAOD,+EAA+E;AAC/E,MAAM,UAAU,UAAU,CAAC,KAAoB;IAC7C,OAAO;QACL,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,eAAe,EAAE,KAAK,CAAC,eAAe;KACvC,CAAA;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAC1B,QAA8B,EAC9B,KAAoB,EACpB,OAAe;IAEf,MAAM,IAAI,GAAG,gBAAgB,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAA;IAC7C,MAAM,KAAK,GAAG,KAAK,CAAC,eAAe,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,eAAe,CAAA;IAChG,OAAO;QACL,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ;QACzC,QAAQ,EAAE,KAAK,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ;QACvE,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ;QACzC,eAAe,EAAE,KAAK,EAAE,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,KAAK;KACtE,CAAA;AACH,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,kBAAkB,CAChC,IAA+B,EAC/B,KAAgC;IAEhC,MAAM,QAAQ,GAAG,CAAC,KAAgC,EAAU,EAAE;QAC5D,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAA;QACtE,OAAO,aAAa,CAAC,MAAM,CAAC,CAAA;IAC9B,CAAC,CAAA;IACD,OAAO,QAAQ,CAAC,IAAI,CAAC,KAAK,QAAQ,CAAC,KAAK,CAAC,CAAA;AAC3C,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAC5B,EAAU,EACV,MAAe;IAEf,MAAM,KAAK,GAAG,qBAAqB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IAC5C,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAA;IACzC,IAAI,KAAK,GAAG,MAAM,CAAA;IAClB,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC;QACpE,IAAI,CAAC;YACH,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;QAC3B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAA;QACb,CAAC;IACH,CAAC;IACD,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IACpF,OAAO,EAAE,UAAU,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAgC,EAAE,CAAA;AAC3F,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { ContentDocument, ContentSummary, DocumentMeta, IPersistenceAdapter } from './index.js';
|
|
2
|
+
/** What a query returns of each document */
|
|
3
|
+
export type QueryProjection = 'meta' | 'summary' | 'full';
|
|
4
|
+
/** Where a text match looks */
|
|
5
|
+
export type MatchTarget = 'slug' | 'fields' | 'body';
|
|
6
|
+
interface QueryBase<P extends QueryProjection> {
|
|
7
|
+
/** The collections to read; every collection when omitted */
|
|
8
|
+
collections?: readonly string[] | undefined;
|
|
9
|
+
/** One locale; every locale when omitted */
|
|
10
|
+
locale?: string | undefined;
|
|
11
|
+
projection: P;
|
|
12
|
+
/** Case-insensitive substring match; a document matches when any target contains the text */
|
|
13
|
+
match?: {
|
|
14
|
+
text: string;
|
|
15
|
+
in: readonly MatchTarget[];
|
|
16
|
+
} | undefined;
|
|
17
|
+
/** `field` or `field:asc|desc`, the sort whitelist of sort-columns.ts */
|
|
18
|
+
sort?: string | undefined;
|
|
19
|
+
/** At most this many items; every match when omitted */
|
|
20
|
+
limit?: number | undefined;
|
|
21
|
+
offset?: number | undefined;
|
|
22
|
+
}
|
|
23
|
+
/** The current version of each document (drafts included) */
|
|
24
|
+
export interface CurrentQuery<P extends QueryProjection = QueryProjection> extends QueryBase<P> {
|
|
25
|
+
state: 'current';
|
|
26
|
+
status?: 'draft' | 'published' | 'archived' | undefined;
|
|
27
|
+
}
|
|
28
|
+
/** The live version of each document that has one */
|
|
29
|
+
export interface PublishedQuery<P extends QueryProjection = QueryProjection> extends QueryBase<P> {
|
|
30
|
+
state: 'published';
|
|
31
|
+
}
|
|
32
|
+
export type ContentQuery<P extends QueryProjection = QueryProjection> = CurrentQuery<P> | PublishedQuery<P>;
|
|
33
|
+
export type QueryItem<P extends QueryProjection> = P extends 'meta' ? DocumentMeta : P extends 'summary' ? ContentSummary : ContentDocument;
|
|
34
|
+
/** One page of a query */
|
|
35
|
+
export interface QueryPage<P extends QueryProjection> {
|
|
36
|
+
/** Each item; `snippet` is set when the query matched `body` or `fields` */
|
|
37
|
+
items: Array<QueryItem<P> & {
|
|
38
|
+
snippet?: string | undefined;
|
|
39
|
+
}>;
|
|
40
|
+
/** Whether another page follows (read as LIMIT n + 1, never a count) */
|
|
41
|
+
hasMore: boolean;
|
|
42
|
+
}
|
|
43
|
+
/** Adapter that can answer a content query. */
|
|
44
|
+
export type QueryReader = IPersistenceAdapter & Required<Pick<IPersistenceAdapter, 'query'>>;
|
|
45
|
+
/** Check if adapter can answer a content query. */
|
|
46
|
+
export declare function canQuery(adapter: IPersistenceAdapter): adapter is QueryReader;
|
|
47
|
+
export {};
|
|
48
|
+
//# sourceMappingURL=content-query.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-query.d.ts","sourceRoot":"","sources":["../src/content-query.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAEpG,4CAA4C;AAC5C,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,CAAA;AAEzD,+BAA+B;AAC/B,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,CAAA;AAEpD,UAAU,SAAS,CAAC,CAAC,SAAS,eAAe;IAC3C,6DAA6D;IAC7D,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAA;IAC3C,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC3B,UAAU,EAAE,CAAC,CAAA;IACb,6FAA6F;IAC7F,KAAK,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,SAAS,WAAW,EAAE,CAAA;KAAE,GAAG,SAAS,CAAA;IAChE,yEAAyE;IACzE,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACzB,wDAAwD;IACxD,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAC5B;AAED,6DAA6D;AAC7D,MAAM,WAAW,YAAY,CAAC,CAAC,SAAS,eAAe,GAAG,eAAe,CAAE,SAAQ,SAAS,CAAC,CAAC,CAAC;IAC7F,KAAK,EAAE,SAAS,CAAA;IAChB,MAAM,CAAC,EAAE,OAAO,GAAG,WAAW,GAAG,UAAU,GAAG,SAAS,CAAA;CACxD;AAED,qDAAqD;AACrD,MAAM,WAAW,cAAc,CAAC,CAAC,SAAS,eAAe,GAAG,eAAe,CAAE,SAAQ,SAAS,CAAC,CAAC,CAAC;IAC/F,KAAK,EAAE,WAAW,CAAA;CACnB;AAED,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS,eAAe,GAAG,eAAe,IAChE,YAAY,CAAC,CAAC,CAAC,GACf,cAAc,CAAC,CAAC,CAAC,CAAA;AAErB,MAAM,MAAM,SAAS,CAAC,CAAC,SAAS,eAAe,IAAI,CAAC,SAAS,MAAM,GAC/D,YAAY,GACZ,CAAC,SAAS,SAAS,GACjB,cAAc,GACd,eAAe,CAAA;AAErB,0BAA0B;AAC1B,MAAM,WAAW,SAAS,CAAC,CAAC,SAAS,eAAe;IAClD,4EAA4E;IAC5E,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG;QAAE,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC,CAAA;IAC7D,wEAAwE;IACxE,OAAO,EAAE,OAAO,CAAA;CACjB;AAED,+CAA+C;AAC/C,MAAM,MAAM,WAAW,GAAG,mBAAmB,GAAG,QAAQ,CAAC,IAAI,CAAC,mBAAmB,EAAE,OAAO,CAAC,CAAC,CAAA;AAE5F,mDAAmD;AACnD,wBAAgB,QAAQ,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,IAAI,WAAW,CAE7E"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-query.js","sourceRoot":"","sources":["../src/content-query.ts"],"names":[],"mappings":"AAuDA,mDAAmD;AACnD,MAAM,UAAU,QAAQ,CAAC,OAA4B;IACnD,OAAO,CAAC,CAAC,OAAO,CAAC,KAAK,CAAA;AACxB,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { type AuthoredMetadata } from './content-contract.js';
|
|
2
|
+
import type { ContentDocument, ContentSummary, DocumentMeta } from './index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Canonical shape of a content row's meta — what a list that reads no fields, body, HTML or
|
|
5
|
+
* sections returns — as understood by the shared mapper.
|
|
6
|
+
*
|
|
7
|
+
* Each adapter is responsible for normalizing its raw storage row into this shape
|
|
8
|
+
* (or one that extends it) before passing it to `contentDocumentFromRow`,
|
|
9
|
+
* `contentSummaryFromRow` or `documentMetaFromRow`. This
|
|
10
|
+
* keeps the shared mapping logic in one place (this file) while letting each
|
|
11
|
+
* adapter own its raw-row-to-normalized translation (JSON parsing for SQLite,
|
|
12
|
+
* Date-to-ISO for Postgres, null-coalescing for html/sections, etc.).
|
|
13
|
+
*/
|
|
14
|
+
export interface NormalizedMetaRow {
|
|
15
|
+
readonly collection: string;
|
|
16
|
+
readonly slug: string;
|
|
17
|
+
readonly locale: string;
|
|
18
|
+
readonly version: number;
|
|
19
|
+
/** The document's live version as of this row; null when nothing is live */
|
|
20
|
+
readonly liveVersion: number | null;
|
|
21
|
+
readonly status: string;
|
|
22
|
+
readonly source: string;
|
|
23
|
+
readonly changedBy: string | null;
|
|
24
|
+
/** ISO 8601 timestamp — every adapter must normalize to this format */
|
|
25
|
+
readonly createdAt: string;
|
|
26
|
+
}
|
|
27
|
+
/** A content row with its fields, read without its body, rendered HTML and sections */
|
|
28
|
+
export interface NormalizedSummaryRow extends NormalizedMetaRow {
|
|
29
|
+
readonly fields: Record<string, unknown>;
|
|
30
|
+
/** The authored metadata groups; a summary leaves out the contract and provenance */
|
|
31
|
+
readonly metadata: AuthoredMetadata['metadata'];
|
|
32
|
+
}
|
|
33
|
+
/** A content row with its body, rendered HTML and sections */
|
|
34
|
+
export interface NormalizedContentRow extends NormalizedSummaryRow {
|
|
35
|
+
readonly authored: AuthoredMetadata;
|
|
36
|
+
readonly body: string;
|
|
37
|
+
readonly html: string;
|
|
38
|
+
readonly sections: Record<string, string>;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Produce a full `ContentDocument` from a normalized row.
|
|
42
|
+
*
|
|
43
|
+
* Called by every persistence adapter after its own `normalizeRow` step.
|
|
44
|
+
* Having this in the shared package means a change to the `ContentDocument`
|
|
45
|
+
* shape only needs to be propagated here, not in every adapter.
|
|
46
|
+
*/
|
|
47
|
+
export declare function contentDocumentFromRow(row: NormalizedContentRow): ContentDocument;
|
|
48
|
+
/**
|
|
49
|
+
* Produce a `ContentSummary` from a row read without its content: the meta and
|
|
50
|
+
* the fields, never the body, the rendered HTML or the sections.
|
|
51
|
+
*/
|
|
52
|
+
export declare function contentSummaryFromRow(row: NormalizedSummaryRow): ContentSummary;
|
|
53
|
+
/**
|
|
54
|
+
* Produce a `DocumentMeta` (no body, no fields, no html, no sections) from a
|
|
55
|
+
* normalized row. Used when listing version history where the caller only
|
|
56
|
+
* needs metadata.
|
|
57
|
+
*/
|
|
58
|
+
export declare function documentMetaFromRow(row: NormalizedMetaRow): DocumentMeta;
|
|
59
|
+
//# sourceMappingURL=content-row.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-row.d.ts","sourceRoot":"","sources":["../src/content-row.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,gBAAgB,EAAoB,MAAM,uBAAuB,CAAA;AAC/E,OAAO,KAAK,EACV,eAAe,EAGf,cAAc,EACd,YAAY,EACb,MAAM,YAAY,CAAA;AAWnB;;;;;;;;;;GAUG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,4EAA4E;IAC5E,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IACnC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACjC,uEAAuE;IACvE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAC3B;AAED,uFAAuF;AACvF,MAAM,WAAW,oBAAqB,SAAQ,iBAAiB;IAC7D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACxC,qFAAqF;IACrF,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,CAAC,UAAU,CAAC,CAAA;CAChD;AAED,8DAA8D;AAC9D,MAAM,WAAW,oBAAqB,SAAQ,oBAAoB;IAChE,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,CAAA;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC1C;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,oBAAoB,GAAG,eAAe,CAUjF;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,oBAAoB,GAAG,cAAc,CAG/E;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,iBAAiB,GAAG,YAAY,CAsBxE"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { authoredMetadata } from './content-contract.js';
|
|
2
|
+
import { statusOf } from './live-version.js';
|
|
3
|
+
const VALID_STATUSES = new Set([
|
|
4
|
+
'draft',
|
|
5
|
+
'published',
|
|
6
|
+
'archived',
|
|
7
|
+
'deleted',
|
|
8
|
+
]);
|
|
9
|
+
const VALID_SOURCES = new Set(['filesystem', 'api', 'import']);
|
|
10
|
+
/**
|
|
11
|
+
* Produce a full `ContentDocument` from a normalized row.
|
|
12
|
+
*
|
|
13
|
+
* Called by every persistence adapter after its own `normalizeRow` step.
|
|
14
|
+
* Having this in the shared package means a change to the `ContentDocument`
|
|
15
|
+
* shape only needs to be propagated here, not in every adapter.
|
|
16
|
+
*/
|
|
17
|
+
export function contentDocumentFromRow(row) {
|
|
18
|
+
const meta = documentMetaFromRow(row);
|
|
19
|
+
return {
|
|
20
|
+
meta,
|
|
21
|
+
fields: row.fields,
|
|
22
|
+
...authoredMetadata(row.authored),
|
|
23
|
+
body: row.body,
|
|
24
|
+
html: row.html,
|
|
25
|
+
sections: row.sections,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Produce a `ContentSummary` from a row read without its content: the meta and
|
|
30
|
+
* the fields, never the body, the rendered HTML or the sections.
|
|
31
|
+
*/
|
|
32
|
+
export function contentSummaryFromRow(row) {
|
|
33
|
+
const meta = documentMetaFromRow(row);
|
|
34
|
+
return { meta, fields: row.fields, metadata: row.metadata };
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Produce a `DocumentMeta` (no body, no fields, no html, no sections) from a
|
|
38
|
+
* normalized row. Used when listing version history where the caller only
|
|
39
|
+
* needs metadata.
|
|
40
|
+
*/
|
|
41
|
+
export function documentMetaFromRow(row) {
|
|
42
|
+
return {
|
|
43
|
+
collection: row.collection,
|
|
44
|
+
slug: row.slug,
|
|
45
|
+
locale: row.locale,
|
|
46
|
+
version: row.version,
|
|
47
|
+
liveVersion: row.liveVersion,
|
|
48
|
+
status: VALID_STATUSES.has(row.status)
|
|
49
|
+
? statusOf(row.status, row.liveVersion)
|
|
50
|
+
: (console.warn(`[persistence] Unknown status "${row.status}" for ${row.collection}/${row.slug}, defaulting to "draft"`),
|
|
51
|
+
statusOf('draft', row.liveVersion)),
|
|
52
|
+
source: VALID_SOURCES.has(row.source)
|
|
53
|
+
? row.source
|
|
54
|
+
: (console.warn(`[persistence] Unknown source "${row.source}" for ${row.collection}/${row.slug}, defaulting to "filesystem"`),
|
|
55
|
+
'filesystem'),
|
|
56
|
+
changedBy: row.changedBy,
|
|
57
|
+
createdAt: row.createdAt,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=content-row.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-row.js","sourceRoot":"","sources":["../src/content-row.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyB,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAQ/E,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAE5C,MAAM,cAAc,GAAwB,IAAI,GAAG,CAAgB;IACjE,OAAO;IACP,WAAW;IACX,UAAU;IACV,SAAS;CACV,CAAC,CAAA;AACF,MAAM,aAAa,GAAwB,IAAI,GAAG,CAAgB,CAAC,YAAY,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAA;AA0ClG;;;;;;GAMG;AACH,MAAM,UAAU,sBAAsB,CAAC,GAAyB;IAC9D,MAAM,IAAI,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAA;IACrC,OAAO;QACL,IAAI;QACJ,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,GAAG,gBAAgB,CAAC,GAAG,CAAC,QAAQ,CAAC;QACjC,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,QAAQ,EAAE,GAAG,CAAC,QAAQ;KACvB,CAAA;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,GAAyB;IAC7D,MAAM,IAAI,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAA;IACrC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,CAAA;AAC7D,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAsB;IACxD,OAAO;QACL,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,WAAW,EAAE,GAAG,CAAC,WAAW;QAC5B,MAAM,EAAE,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC;YACpC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,WAAW,CAAC;YACvC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CACX,iCAAiC,GAAG,CAAC,MAAM,SAAS,GAAG,CAAC,UAAU,IAAI,GAAG,CAAC,IAAI,yBAAyB,CACxG;gBACD,QAAQ,CAAC,OAAO,EAAE,GAAG,CAAC,WAAW,CAAC,CAAC;QACvC,MAAM,EAAE,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC;YACnC,CAAC,CAAE,GAAG,CAAC,MAAwB;YAC/B,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CACX,iCAAiC,GAAG,CAAC,MAAM,SAAS,GAAG,CAAC,UAAU,IAAI,GAAG,CAAC,IAAI,8BAA8B,CAC7G;gBACD,YAA6B,CAAC;QAClC,SAAS,EAAE,GAAG,CAAC,SAAS;QACxB,SAAS,EAAE,GAAG,CAAC,SAAS;KACzB,CAAA;AACH,CAAC"}
|