emdash 0.5.0 → 0.6.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/dist/{adapters-C2BzVy0p.d.mts → adapters-Di31kZ28.d.mts} +16 -1
- package/dist/adapters-Di31kZ28.d.mts.map +1 -0
- package/dist/{apply-Cma_PiF6.mjs → apply-B4MsLM-w.mjs} +27 -12
- package/dist/apply-B4MsLM-w.mjs.map +1 -0
- package/dist/astro/index.d.mts +6 -6
- package/dist/astro/index.d.mts.map +1 -1
- package/dist/astro/index.mjs +199 -33
- package/dist/astro/index.mjs.map +1 -1
- package/dist/astro/middleware/auth.d.mts +5 -5
- package/dist/astro/middleware/auth.d.mts.map +1 -1
- package/dist/astro/middleware/auth.mjs +30 -4
- package/dist/astro/middleware/auth.mjs.map +1 -1
- package/dist/astro/middleware/redirect.mjs +1 -1
- package/dist/astro/middleware/request-context.d.mts.map +1 -1
- package/dist/astro/middleware/request-context.mjs +5 -3
- package/dist/astro/middleware/request-context.mjs.map +1 -1
- package/dist/astro/middleware/setup.mjs +1 -1
- package/dist/astro/middleware.d.mts.map +1 -1
- package/dist/astro/middleware.mjs +460 -180
- package/dist/astro/middleware.mjs.map +1 -1
- package/dist/astro/types.d.mts +8 -9
- package/dist/astro/types.d.mts.map +1 -1
- package/dist/{byline-WuOq9MFJ.mjs → byline-C4OVd8b3.mjs} +3 -19
- package/dist/byline-C4OVd8b3.mjs.map +1 -0
- package/dist/{bylines-C_Wsnz4L.mjs → bylines-hPTW79hw.mjs} +20 -33
- package/dist/bylines-hPTW79hw.mjs.map +1 -0
- package/dist/{cache-E3Dts-yT.mjs → cache-BkKBuIvS.mjs} +1 -1
- package/dist/{cache-E3Dts-yT.mjs.map → cache-BkKBuIvS.mjs.map} +1 -1
- package/dist/chunks-HGz06Soa.mjs +19 -0
- package/dist/chunks-HGz06Soa.mjs.map +1 -0
- package/dist/cli/index.mjs +9 -8
- package/dist/cli/index.mjs.map +1 -1
- package/dist/client/cf-access.d.mts +1 -1
- package/dist/client/index.d.mts +1 -1
- package/dist/client/index.mjs +1 -1
- package/dist/{config-DkxPrM9l.mjs → config-BXwuX8Bx.mjs} +1 -1
- package/dist/{config-DkxPrM9l.mjs.map → config-BXwuX8Bx.mjs.map} +1 -1
- package/dist/{connection-B4zVnQIa.mjs → connection-2igzM-AT.mjs} +19 -2
- package/dist/connection-2igzM-AT.mjs.map +1 -0
- package/dist/database/instrumentation.d.mts +45 -0
- package/dist/database/instrumentation.d.mts.map +1 -0
- package/dist/database/instrumentation.mjs +61 -0
- package/dist/database/instrumentation.mjs.map +1 -0
- package/dist/db/index.d.mts +3 -3
- package/dist/db/index.mjs.map +1 -1
- package/dist/db/libsql.d.mts +1 -1
- package/dist/db/postgres.d.mts +1 -1
- package/dist/db/sqlite.d.mts +1 -1
- package/dist/db-errors-D0UT85nC.mjs +41 -0
- package/dist/db-errors-D0UT85nC.mjs.map +1 -0
- package/dist/{default-PUx9RK6u.mjs → default-CME5YdZ3.mjs} +1 -1
- package/dist/{default-PUx9RK6u.mjs.map → default-CME5YdZ3.mjs.map} +1 -1
- package/dist/{error-HBeQbVhV.mjs → error-CiYn9yDu.mjs} +1 -1
- package/dist/{error-HBeQbVhV.mjs.map → error-CiYn9yDu.mjs.map} +1 -1
- package/dist/{index-CCWzlriB.d.mts → index-BYv0mB9g.d.mts} +135 -19
- package/dist/index-BYv0mB9g.d.mts.map +1 -0
- package/dist/index.d.mts +11 -11
- package/dist/index.mjs +20 -18
- package/dist/{load-BhSSm-TS.mjs → load-CBcmDIot.mjs} +1 -1
- package/dist/{load-BhSSm-TS.mjs.map → load-CBcmDIot.mjs.map} +1 -1
- package/dist/{loader-BYzwzORf.mjs → loader-DeiBJEMe.mjs} +18 -12
- package/dist/loader-DeiBJEMe.mjs.map +1 -0
- package/dist/{manifest-schema-BsXINkQD.mjs → manifest-schema-V30qsMft.mjs} +1 -1
- package/dist/{manifest-schema-BsXINkQD.mjs.map → manifest-schema-V30qsMft.mjs.map} +1 -1
- package/dist/media/index.d.mts +1 -1
- package/dist/media/index.mjs +1 -1
- package/dist/media/local-runtime.d.mts +7 -7
- package/dist/{mode-CyPLdO3C.mjs → mode-CpNnGkPz.mjs} +1 -1
- package/dist/{mode-CyPLdO3C.mjs.map → mode-CpNnGkPz.mjs.map} +1 -1
- package/dist/page/index.d.mts +11 -2
- package/dist/page/index.d.mts.map +1 -1
- package/dist/page/index.mjs +23 -1
- package/dist/page/index.mjs.map +1 -1
- package/dist/{placeholder-DntBEQo7.mjs → placeholder-C-fk5hYI.mjs} +1 -1
- package/dist/{placeholder-DntBEQo7.mjs.map → placeholder-C-fk5hYI.mjs.map} +1 -1
- package/dist/{placeholder-BBCtpTES.d.mts → placeholder-tzpqGWII.d.mts} +1 -1
- package/dist/{placeholder-BBCtpTES.d.mts.map → placeholder-tzpqGWII.d.mts.map} +1 -1
- package/dist/plugins/adapt-sandbox-entry.d.mts +5 -5
- package/dist/plugins/adapt-sandbox-entry.mjs +1 -1
- package/dist/{query-B6Vu0d2i.mjs → query-Bk_3vKvU.mjs} +78 -11
- package/dist/query-Bk_3vKvU.mjs.map +1 -0
- package/dist/{registry-BgnP3ysR.mjs → registry-Ci3WxVAr.mjs} +133 -97
- package/dist/registry-Ci3WxVAr.mjs.map +1 -0
- package/dist/request-cache-DiR961CV.mjs +79 -0
- package/dist/request-cache-DiR961CV.mjs.map +1 -0
- package/dist/request-context.d.mts +19 -16
- package/dist/request-context.d.mts.map +1 -1
- package/dist/request-context.mjs.map +1 -1
- package/dist/{runner-DYv3rX8P.d.mts → runner-Fl2NcUUz.d.mts} +2 -2
- package/dist/{runner-DYv3rX8P.d.mts.map → runner-Fl2NcUUz.d.mts.map} +1 -1
- package/dist/runtime.d.mts +6 -6
- package/dist/runtime.mjs +1 -1
- package/dist/{search-Cn1SYvYF.mjs → search-DI4bM2w9.mjs} +96 -206
- package/dist/search-DI4bM2w9.mjs.map +1 -0
- package/dist/seed/index.d.mts +2 -2
- package/dist/seed/index.mjs +8 -7
- package/dist/seo/index.d.mts +1 -1
- package/dist/storage/local.d.mts +1 -1
- package/dist/storage/local.mjs +1 -1
- package/dist/storage/s3.d.mts +1 -1
- package/dist/storage/s3.mjs +1 -1
- package/dist/taxonomies-DbrKzDju.mjs +308 -0
- package/dist/taxonomies-DbrKzDju.mjs.map +1 -0
- package/dist/{tokens-DKHiCYCB.mjs → tokens-BFPFx3CA.mjs} +1 -1
- package/dist/{tokens-DKHiCYCB.mjs.map → tokens-BFPFx3CA.mjs.map} +1 -1
- package/dist/{transport-BtcQ-Z7T.mjs → transport-BykRfpyy.mjs} +1 -1
- package/dist/{transport-BtcQ-Z7T.mjs.map → transport-BykRfpyy.mjs.map} +1 -1
- package/dist/{transport-CKQA_G44.d.mts → transport-H4Iwx7tC.d.mts} +1 -1
- package/dist/{transport-CKQA_G44.d.mts.map → transport-H4Iwx7tC.d.mts.map} +1 -1
- package/dist/{types-BmkQR1En.d.mts → types-6CUZRrZP.d.mts} +1 -1
- package/dist/{types-BmkQR1En.d.mts.map → types-6CUZRrZP.d.mts.map} +1 -1
- package/dist/{types-B6BzlZxx.d.mts → types-8xrvl_68.d.mts} +1 -1
- package/dist/{types-B6BzlZxx.d.mts.map → types-8xrvl_68.d.mts.map} +1 -1
- package/dist/{types-Dz9_WMS6.mjs → types-BH2L167P.mjs} +1 -1
- package/dist/{types-Dz9_WMS6.mjs.map → types-BH2L167P.mjs.map} +1 -1
- package/dist/{types-DNZpaCBk.d.mts → types-CFWjXmus.d.mts} +1 -1
- package/dist/{types-DNZpaCBk.d.mts.map → types-CFWjXmus.d.mts.map} +1 -1
- package/dist/{types-DeG21anB.d.mts → types-CnZYHyLW.d.mts} +55 -5
- package/dist/types-CnZYHyLW.d.mts.map +1 -0
- package/dist/{types-xxCWI3j0.mjs → types-DDS4MxsT.mjs} +11 -3
- package/dist/types-DDS4MxsT.mjs.map +1 -0
- package/dist/{types-C3ronwXb.d.mts → types-DgrIP0tF.d.mts} +102 -4
- package/dist/types-DgrIP0tF.d.mts.map +1 -0
- package/dist/{validate-Db1yNL3i.d.mts → validate-CaLH1Ia2.d.mts} +5 -52
- package/dist/validate-CaLH1Ia2.d.mts.map +1 -0
- package/dist/{validate-DuZDIxfy.mjs → validate-CqsNItbt.mjs} +2 -2
- package/dist/{validate-DuZDIxfy.mjs.map → validate-CqsNItbt.mjs.map} +1 -1
- package/dist/version-Uaf2ynPX.mjs +7 -0
- package/dist/{version-CMMjTuqu.mjs.map → version-Uaf2ynPX.mjs.map} +1 -1
- package/package.json +10 -5
- package/src/after.ts +62 -0
- package/src/api/handlers/oauth-authorization.ts +2 -32
- package/src/api/handlers/oauth-clients.ts +40 -4
- package/src/api/handlers/taxonomies.ts +13 -0
- package/src/api/oauth/redirect-uri.ts +34 -0
- package/src/api/openapi/document.ts +126 -118
- package/src/api/schemas/media.ts +26 -15
- package/src/api/schemas/schema.ts +1 -0
- package/src/astro/integration/font-provider.ts +176 -0
- package/src/astro/integration/index.ts +42 -0
- package/src/astro/integration/routes.ts +6 -0
- package/src/astro/integration/runtime.ts +63 -0
- package/src/astro/integration/virtual-modules.ts +41 -39
- package/src/astro/integration/vite-config.ts +16 -5
- package/src/astro/middleware/auth.ts +33 -1
- package/src/astro/middleware/request-context.ts +15 -3
- package/src/astro/middleware.ts +340 -263
- package/src/astro/routes/admin.astro +7 -3
- package/src/astro/routes/api/auth/passkey/verify.ts +5 -1
- package/src/astro/routes/api/content/[collection]/[id]/terms/[taxonomy].ts +5 -0
- package/src/astro/routes/api/import/wordpress/execute.ts +1 -1
- package/src/astro/routes/api/import/wordpress-plugin/execute.ts +1 -1
- package/src/astro/routes/api/media/upload-url.ts +10 -2
- package/src/astro/routes/api/media.ts +10 -7
- package/src/astro/routes/api/oauth/register.ts +178 -0
- package/src/astro/routes/api/oauth/token.ts +15 -0
- package/src/astro/routes/api/openapi.json.ts +15 -5
- package/src/astro/routes/api/schema/collections/[slug]/fields/[fieldSlug].ts +2 -0
- package/src/astro/routes/api/schema/collections/[slug]/fields/index.ts +1 -0
- package/src/astro/routes/api/schema/collections/[slug]/fields/reorder.ts +1 -0
- package/src/astro/routes/api/search/index.ts +5 -0
- package/src/astro/routes/api/search/suggest.ts +3 -0
- package/src/astro/routes/api/taxonomies/index.ts +1 -0
- package/src/astro/routes/api/well-known/oauth-authorization-server.ts +1 -1
- package/src/bylines/index.ts +22 -45
- package/src/components/EmDashHead.astro +23 -7
- package/src/database/connection.ts +23 -1
- package/src/database/instrumentation.ts +98 -0
- package/src/db/adapters.ts +15 -0
- package/src/emdash-runtime.ts +309 -91
- package/src/index.ts +6 -0
- package/src/loader.ts +19 -24
- package/src/menus/index.ts +6 -3
- package/src/page/index.ts +1 -1
- package/src/page/seo-contributions.ts +36 -0
- package/src/query.ts +104 -7
- package/src/request-cache.ts +106 -0
- package/src/request-context.ts +19 -0
- package/src/schema/query.ts +5 -2
- package/src/schema/registry.ts +243 -166
- package/src/schema/types.ts +13 -2
- package/src/schema/zod-generator.ts +4 -0
- package/src/search/fts-manager.ts +19 -5
- package/src/search/query.ts +4 -3
- package/src/seed/apply.ts +15 -1
- package/src/settings/index.ts +24 -5
- package/src/taxonomies/index.ts +324 -124
- package/src/utils/db-errors.ts +46 -0
- package/src/virtual-modules.d.ts +31 -10
- package/src/widgets/index.ts +54 -25
- package/dist/adapters-C2BzVy0p.d.mts.map +0 -1
- package/dist/apply-Cma_PiF6.mjs.map +0 -1
- package/dist/byline-WuOq9MFJ.mjs.map +0 -1
- package/dist/bylines-C_Wsnz4L.mjs.map +0 -1
- package/dist/connection-B4zVnQIa.mjs.map +0 -1
- package/dist/index-CCWzlriB.d.mts.map +0 -1
- package/dist/loader-BYzwzORf.mjs.map +0 -1
- package/dist/query-B6Vu0d2i.mjs.map +0 -1
- package/dist/registry-BgnP3ysR.mjs.map +0 -1
- package/dist/search-Cn1SYvYF.mjs.map +0 -1
- package/dist/types-C3ronwXb.d.mts.map +0 -1
- package/dist/types-DeG21anB.d.mts.map +0 -1
- package/dist/types-xxCWI3j0.mjs.map +0 -1
- package/dist/validate-Db1yNL3i.d.mts.map +0 -1
- package/dist/version-CMMjTuqu.mjs +0 -7
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import BetterSqlite3 from "better-sqlite3";
|
|
2
2
|
import { Kysely, SqliteDialect } from "kysely";
|
|
3
3
|
|
|
4
|
+
import { kyselyLogOption } from "./instrumentation.js";
|
|
4
5
|
import type { Database } from "./types.js";
|
|
5
6
|
|
|
6
7
|
export interface DatabaseConfig {
|
|
@@ -18,6 +19,23 @@ export class EmDashDatabaseError extends Error {
|
|
|
18
19
|
}
|
|
19
20
|
}
|
|
20
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Returns a helpful, actionable message when better-sqlite3's native binary
|
|
24
|
+
* was compiled against a different Node.js version than the one running. This
|
|
25
|
+
* happens after upgrading Node without rebuilding native deps.
|
|
26
|
+
*
|
|
27
|
+
* Returns null if the error is not a NODE_MODULE_VERSION mismatch.
|
|
28
|
+
*/
|
|
29
|
+
export function formatNativeModuleVersionError(error: unknown): string | null {
|
|
30
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
31
|
+
if (!message.includes("NODE_MODULE_VERSION")) return null;
|
|
32
|
+
return (
|
|
33
|
+
"better-sqlite3's native binary was compiled against a different Node.js version. " +
|
|
34
|
+
"Rebuild it with `pnpm rebuild better-sqlite3` (or `npm rebuild better-sqlite3`), " +
|
|
35
|
+
"or reinstall dependencies with your current Node.js version."
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
21
39
|
/**
|
|
22
40
|
* Creates a Kysely database instance
|
|
23
41
|
* Supports:
|
|
@@ -45,7 +63,7 @@ export function createDatabase(config: DatabaseConfig): Kysely<Database> {
|
|
|
45
63
|
database: sqlite,
|
|
46
64
|
});
|
|
47
65
|
|
|
48
|
-
return new Kysely<Database>({ dialect });
|
|
66
|
+
return new Kysely<Database>({ dialect, log: kyselyLogOption() });
|
|
49
67
|
}
|
|
50
68
|
|
|
51
69
|
// Handle libSQL (Turso)
|
|
@@ -62,6 +80,10 @@ export function createDatabase(config: DatabaseConfig): Kysely<Database> {
|
|
|
62
80
|
if (error instanceof EmDashDatabaseError) {
|
|
63
81
|
throw error;
|
|
64
82
|
}
|
|
83
|
+
const nativeVersionHint = formatNativeModuleVersionError(error);
|
|
84
|
+
if (nativeVersionHint) {
|
|
85
|
+
throw new EmDashDatabaseError(nativeVersionHint, error);
|
|
86
|
+
}
|
|
65
87
|
throw new EmDashDatabaseError("Failed to create database", error);
|
|
66
88
|
}
|
|
67
89
|
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Query instrumentation
|
|
3
|
+
*
|
|
4
|
+
* Dev/test-only: captures every Kysely query executed inside a request,
|
|
5
|
+
* tagged with the route, method, and a caller-supplied phase (e.g. "cold"
|
|
6
|
+
* or "warm"). Events are emitted as prefixed NDJSON on stdout so the
|
|
7
|
+
* harness can capture them from both Node and workerd — workerd has no
|
|
8
|
+
* filesystem access, but `console.log` is portable.
|
|
9
|
+
*
|
|
10
|
+
* The recorder lives on the request context (AsyncLocalStorage). The
|
|
11
|
+
* Kysely `log` hook reads the recorder at query time and appends an
|
|
12
|
+
* event. When no recorder is attached, the hook is a null check.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import type { LogEvent, Logger } from "kysely";
|
|
16
|
+
|
|
17
|
+
import { getRequestContext } from "../request-context.js";
|
|
18
|
+
|
|
19
|
+
export const QUERY_LOG_ENV = "EMDASH_QUERY_LOG";
|
|
20
|
+
export const QUERY_LOG_PREFIX = "[emdash-query-log]";
|
|
21
|
+
|
|
22
|
+
export interface QueryEvent {
|
|
23
|
+
sql: string;
|
|
24
|
+
params: readonly unknown[];
|
|
25
|
+
durationMs: number;
|
|
26
|
+
route: string;
|
|
27
|
+
method: string;
|
|
28
|
+
phase: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface QueryRecorder {
|
|
32
|
+
events: QueryEvent[];
|
|
33
|
+
route: string;
|
|
34
|
+
method: string;
|
|
35
|
+
phase: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function createRecorder(route: string, method: string, phase: string): QueryRecorder {
|
|
39
|
+
return { events: [], route, method, phase };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function recordEvent(
|
|
43
|
+
rec: QueryRecorder,
|
|
44
|
+
sql: string,
|
|
45
|
+
params: readonly unknown[],
|
|
46
|
+
durationMs: number,
|
|
47
|
+
): void {
|
|
48
|
+
rec.events.push({
|
|
49
|
+
sql,
|
|
50
|
+
params,
|
|
51
|
+
durationMs,
|
|
52
|
+
route: rec.route,
|
|
53
|
+
method: rec.method,
|
|
54
|
+
phase: rec.phase,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Emit all events from a recorder as prefixed NDJSON on stdout. The
|
|
60
|
+
* harness pipes the child's stdout, filters lines beginning with
|
|
61
|
+
* QUERY_LOG_PREFIX, and writes them to its own file. Using stdout means
|
|
62
|
+
* the sink works uniformly in Node and in workerd (which has no fs).
|
|
63
|
+
*/
|
|
64
|
+
export function flushRecorder(rec: QueryRecorder): void {
|
|
65
|
+
if (rec.events.length === 0) return;
|
|
66
|
+
for (const e of rec.events) {
|
|
67
|
+
console.log(`${QUERY_LOG_PREFIX} ${JSON.stringify(e)}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Whether query instrumentation is enabled. Read at Kysely construction
|
|
73
|
+
* time and middleware entry — the env var is a process-lifetime flag, not
|
|
74
|
+
* per-request. Gated via `process.env` so adapters that ship env through
|
|
75
|
+
* to the worker (e.g. Miniflare via wrangler.jsonc `vars` or host env
|
|
76
|
+
* pass-through) can enable it at runtime.
|
|
77
|
+
*/
|
|
78
|
+
export function isInstrumentationEnabled(): boolean {
|
|
79
|
+
return Boolean(
|
|
80
|
+
typeof process !== "undefined" && process.env && process.env[QUERY_LOG_ENV] === "1",
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function kyselyLog(event: LogEvent): void {
|
|
85
|
+
if (event.level !== "query") return;
|
|
86
|
+
const rec = getRequestContext()?.queryRecorder;
|
|
87
|
+
if (!rec) return;
|
|
88
|
+
recordEvent(rec, event.query.sql, event.query.parameters, event.queryDurationMillis);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Returns a Kysely `log` option when instrumentation is enabled, or undefined.
|
|
93
|
+
* Pass as `new Kysely({ dialect, log: kyselyLogOption() })` so disabled mode
|
|
94
|
+
* has zero overhead — Kysely skips query timing entirely when `log` is absent.
|
|
95
|
+
*/
|
|
96
|
+
export function kyselyLogOption(): Logger | undefined {
|
|
97
|
+
return isInstrumentationEnabled() ? kyselyLog : undefined;
|
|
98
|
+
}
|
package/src/db/adapters.ts
CHANGED
|
@@ -33,6 +33,21 @@ export interface DatabaseDescriptor {
|
|
|
33
33
|
entrypoint: string;
|
|
34
34
|
config: unknown;
|
|
35
35
|
type: DatabaseDialectType;
|
|
36
|
+
/**
|
|
37
|
+
* When true, the adapter's runtime entrypoint MUST export a named
|
|
38
|
+
* `createRequestScopedDb` function matching the signature declared in
|
|
39
|
+
* `virtual:emdash/dialect`. The virtual-module generator re-exports it
|
|
40
|
+
* by name, so a missing export becomes a build-time bundler error.
|
|
41
|
+
*
|
|
42
|
+
* The function is called once per request and decides — based on its own
|
|
43
|
+
* runtime config (e.g. whether the user opted into D1 sessions) — whether
|
|
44
|
+
* to return a per-request Kysely or null. Use this for features like D1
|
|
45
|
+
* read-replica sessions, bookmark cookies, or any per-request DB handle.
|
|
46
|
+
*
|
|
47
|
+
* When false or absent, the generator emits a stub that returns null and
|
|
48
|
+
* the middleware takes its default (singleton) path.
|
|
49
|
+
*/
|
|
50
|
+
supportsRequestScope?: boolean;
|
|
36
51
|
}
|
|
37
52
|
|
|
38
53
|
export interface SqliteConfig {
|