@beechcms/core 0.4.0 → 0.4.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/dist/auth/password-reset-token.repository.d.ts +0 -1
- package/dist/auth/password-reset-token.repository.d.ts.map +1 -1
- package/dist/clock.d.ts +25 -0
- package/dist/clock.d.ts.map +1 -0
- package/dist/clock.js +9 -0
- package/dist/content-scan.repository.d.ts +9 -0
- package/dist/content-scan.repository.d.ts.map +1 -0
- package/dist/content-scan.repository.js +1 -0
- package/dist/engine.d.ts +24 -24
- package/dist/engine.js +22 -22
- package/dist/id-generator.d.ts +19 -0
- package/dist/id-generator.d.ts.map +1 -0
- package/dist/id-generator.js +7 -0
- package/dist/index.d.ts +20 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -3
- package/dist/notifications/notification-service.d.ts +29 -0
- package/dist/notifications/notification-service.d.ts.map +1 -0
- package/dist/notifications/notification-service.js +1 -0
- package/dist/notifications/notification.repository.d.ts +51 -0
- package/dist/notifications/notification.repository.d.ts.map +1 -0
- package/dist/notifications/notification.repository.js +10 -0
- package/dist/observability/activity-log.repository.d.ts +58 -0
- package/dist/observability/activity-log.repository.d.ts.map +1 -0
- package/dist/observability/activity-log.repository.js +1 -0
- package/dist/observability/activity-logger.d.ts +49 -0
- package/dist/observability/activity-logger.d.ts.map +1 -0
- package/dist/observability/activity-logger.js +12 -0
- package/dist/observability/analytics.repository.d.ts +37 -0
- package/dist/observability/analytics.repository.d.ts.map +1 -0
- package/dist/observability/analytics.repository.js +1 -0
- package/dist/search/search.repository.d.ts +45 -0
- package/dist/search/search.repository.d.ts.map +1 -0
- package/dist/search/search.repository.js +1 -0
- package/dist/seed-registry.d.ts +49 -0
- package/dist/seed-registry.d.ts.map +1 -0
- package/dist/seed-registry.js +29 -0
- package/dist/validation.d.ts +7 -7
- package/dist/validation.js +7 -7
- package/dist/widget/widget.repository.d.ts +113 -0
- package/dist/widget/widget.repository.d.ts.map +1 -0
- package/dist/widget/widget.repository.js +1 -0
- package/package.json +9 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"password-reset-token.repository.d.ts","sourceRoot":"","sources":["../../src/auth/password-reset-token.repository.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,qBAAqB;IACpC,
|
|
1
|
+
{"version":3,"file":"password-reset-token.repository.d.ts","sourceRoot":"","sources":["../../src/auth/password-reset-token.repository.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,6BAA6B;IAC5C;;;OAGG;IACH,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAEtE,0FAA0F;IAC1F,MAAM,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAEpD;;;;OAIG;IACH,wBAAwB,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAAA;IAEtG,4DAA4D;IAC5D,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAC/D"}
|
package/dist/clock.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Time abstraction. Hides direct calls to {@link Date.now} so callers can
|
|
3
|
+
* swap in deterministic clocks during tests without resorting to global
|
|
4
|
+
* timer mocks (e.g. vi.useFakeTimers, sinon, monkey-patching Date).
|
|
5
|
+
*/
|
|
6
|
+
export interface IClock {
|
|
7
|
+
/**
|
|
8
|
+
* Returns the current Unix timestamp in milliseconds.
|
|
9
|
+
* Equivalent to Date.now() in production.
|
|
10
|
+
* Overridable in tests for deterministic time-sensitive assertions.
|
|
11
|
+
*/
|
|
12
|
+
now(): number;
|
|
13
|
+
/**
|
|
14
|
+
* Returns the current Unix timestamp in whole seconds.
|
|
15
|
+
* Equivalent to Math.floor(Date.now() / 1000) in production.
|
|
16
|
+
* Used by JWT issuance, session expiry, and analytics day-bucket computations.
|
|
17
|
+
*/
|
|
18
|
+
nowSeconds(): number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Production singleton. Delegates to the host runtime clock. Stateless and
|
|
22
|
+
* therefore safe to share across requests.
|
|
23
|
+
*/
|
|
24
|
+
export declare const SystemClock: IClock;
|
|
25
|
+
//# sourceMappingURL=clock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clock.d.ts","sourceRoot":"","sources":["../src/clock.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,WAAW,MAAM;IACrB;;;;OAIG;IACH,GAAG,IAAI,MAAM,CAAA;IAEb;;;;OAIG;IACH,UAAU,IAAI,MAAM,CAAA;CACrB;AAID;;;GAGG;AACH,eAAO,MAAM,WAAW,EAAE,MAGzB,CAAA"}
|
package/dist/clock.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const MILLISECONDS_PER_SECOND = 1000;
|
|
2
|
+
/**
|
|
3
|
+
* Production singleton. Delegates to the host runtime clock. Stateless and
|
|
4
|
+
* therefore safe to share across requests.
|
|
5
|
+
*/
|
|
6
|
+
export const SystemClock = {
|
|
7
|
+
now: () => Date.now(),
|
|
8
|
+
nowSeconds: () => Math.floor(Date.now() / MILLISECONDS_PER_SECOND),
|
|
9
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Seed } from './types.js';
|
|
2
|
+
export interface IContentScanRepository {
|
|
3
|
+
/**
|
|
4
|
+
* Scans across all registered seeds to identify media keys that are currently referenced
|
|
5
|
+
* by any content entry. Used for orphaned media detection and storage analytics.
|
|
6
|
+
*/
|
|
7
|
+
getReferencedMediaKeys(seeds: Seed[]): Promise<Set<string>>;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=content-scan.repository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-scan.repository.d.ts","sourceRoot":"","sources":["../src/content-scan.repository.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AAEtC,MAAM,WAAW,sBAAsB;IACrC;;;OAGG;IACH,sBAAsB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAA;CAC5D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/engine.d.ts
CHANGED
|
@@ -1,47 +1,47 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Botanical Engine: Schema Compiler + Query Builder.
|
|
3
3
|
*
|
|
4
|
-
* In v0.4.0
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* In v0.4.0 each Seed has a dedicated SQL table (`content_{slug}`) with
|
|
5
|
+
* real typed columns. This module generates the DDL and builds parameterized queries.
|
|
6
|
+
* It does not know about HTTP, auth, or UI — it's a pure TypeScript library.
|
|
7
7
|
*/
|
|
8
8
|
import type { Seed, Branch, SelectOptions, ParameterizedQuery } from './types.js';
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
11
|
-
* +
|
|
10
|
+
* Generates `CREATE TABLE IF NOT EXISTS content_{slug}` with system columns
|
|
11
|
+
* + one column per Branch. Pure function: same Seed → same SQL.
|
|
12
12
|
*/
|
|
13
13
|
export declare function generateCreateTable(seed: Seed): string;
|
|
14
14
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
15
|
+
* Generates the drafts table `content_{slug}_drafts` for Seeds with `allowDrafts: true`.
|
|
16
|
+
* All branch columns are nullable (drafts are partial).
|
|
17
|
+
* Returns null if the Seed does not have `allowDrafts: true`.
|
|
18
18
|
*/
|
|
19
19
|
export declare function generateDraftTable(seed: Seed): string | null;
|
|
20
20
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
21
|
+
* Generates `ALTER TABLE content_{slug} ADD COLUMN {alias} {type}`.
|
|
22
|
+
* New columns are always nullable (SQLite limit on ALTER TABLE).
|
|
23
23
|
*/
|
|
24
24
|
export declare function generateAddColumn(seed: Seed, branch: Branch): string;
|
|
25
25
|
/**
|
|
26
|
-
*
|
|
27
|
-
*
|
|
26
|
+
* Generates B-tree indexes for status, created_at and every filterable Branch
|
|
27
|
+
* with an indexable type (text, number, date, boolean).
|
|
28
28
|
*/
|
|
29
29
|
export declare function generateIndexes(seed: Seed): string[];
|
|
30
30
|
/**
|
|
31
|
-
*
|
|
32
|
-
*
|
|
31
|
+
* Generates the FTS5 virtual table for indexable text/richtext Branches.
|
|
32
|
+
* Returns null if the Seed has no branches with search enabled.
|
|
33
33
|
*/
|
|
34
34
|
export declare function generateFtsTable(seed: Seed): string | null;
|
|
35
35
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
36
|
+
* Generates the 3 SQLite triggers (insert/update/delete) that keep the FTS
|
|
37
|
+
* automatically synchronized — eliminates the need for manual syncFts.
|
|
38
|
+
* Returns an empty array if the Seed has no indexable branches.
|
|
39
39
|
*/
|
|
40
40
|
export declare function generateFtsTriggers(seed: Seed): string[];
|
|
41
41
|
/**
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
42
|
+
* Builds a parameterized SELECT on `content_{slug}`.
|
|
43
|
+
* Never uses json_extract — every column is a real column.
|
|
44
|
+
* Unknown columns in filters/orderBy are ignored (fail-closed).
|
|
45
45
|
*/
|
|
46
46
|
export declare function buildSelectQuery(seed: Seed, options?: SelectOptions): ParameterizedQuery;
|
|
47
47
|
export interface SchemaColumn {
|
|
@@ -51,17 +51,17 @@ export interface SchemaColumn {
|
|
|
51
51
|
isPk: boolean;
|
|
52
52
|
}
|
|
53
53
|
/**
|
|
54
|
-
*
|
|
55
|
-
*
|
|
54
|
+
* Returns the list of expected columns for a Seed's table.
|
|
55
|
+
* Used by `beech seed:load --diff` to compare current vs expected schema.
|
|
56
56
|
*/
|
|
57
57
|
export declare function getExpectedColumns(seed: Seed): SchemaColumn[];
|
|
58
58
|
/**
|
|
59
|
-
*
|
|
59
|
+
* Serializes a value for writing to the DB.
|
|
60
60
|
* boolean → 0/1 | date → Unix timestamp | json/asset-list → JSON string
|
|
61
61
|
*/
|
|
62
62
|
export declare function serializeForDb(branch: Branch, value: unknown): string | number | null;
|
|
63
63
|
/**
|
|
64
|
-
*
|
|
64
|
+
* Deserializes a value read from the DB for the API response.
|
|
65
65
|
* 0/1 → boolean | Unix timestamp → ISO 8601 | JSON string → object
|
|
66
66
|
*/
|
|
67
67
|
export declare function deserializeFromDb(branch: Branch, value: unknown): unknown;
|
package/dist/engine.js
CHANGED
|
@@ -71,8 +71,8 @@ function normalizeAssetListValue(rawValue) {
|
|
|
71
71
|
}
|
|
72
72
|
// ---- DDL Generators ----
|
|
73
73
|
/**
|
|
74
|
-
*
|
|
75
|
-
* +
|
|
74
|
+
* Generates `CREATE TABLE IF NOT EXISTS content_{slug}` with system columns
|
|
75
|
+
* + one column per Branch. Pure function: same Seed → same SQL.
|
|
76
76
|
*/
|
|
77
77
|
export function generateCreateTable(seed) {
|
|
78
78
|
const table = tableName(seed);
|
|
@@ -97,9 +97,9 @@ export function generateCreateTable(seed) {
|
|
|
97
97
|
return lines.join('\n');
|
|
98
98
|
}
|
|
99
99
|
/**
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
100
|
+
* Generates the drafts table `content_{slug}_drafts` for Seeds with `allowDrafts: true`.
|
|
101
|
+
* All branch columns are nullable (drafts are partial).
|
|
102
|
+
* Returns null if the Seed does not have `allowDrafts: true`.
|
|
103
103
|
*/
|
|
104
104
|
export function generateDraftTable(seed) {
|
|
105
105
|
if (!seed.allowDrafts)
|
|
@@ -114,7 +114,7 @@ export function generateDraftTable(seed) {
|
|
|
114
114
|
for (const branch of seed.branches) {
|
|
115
115
|
const { sqlType } = BRANCH_TYPE_SQL[branch.type];
|
|
116
116
|
let col = ` ${branch.alias} ${sqlType}`;
|
|
117
|
-
// boolean CHECK: in SQLite, NULL IN (0,1) → NULL,
|
|
117
|
+
// boolean CHECK: in SQLite, NULL IN (0,1) → NULL, which passes the CHECK (only FALSE fails it)
|
|
118
118
|
if (branch.type === 'boolean')
|
|
119
119
|
col += ` CHECK (${branch.alias} IN (0, 1))`;
|
|
120
120
|
lines.push(col + ',');
|
|
@@ -124,16 +124,16 @@ export function generateDraftTable(seed) {
|
|
|
124
124
|
return lines.join('\n');
|
|
125
125
|
}
|
|
126
126
|
/**
|
|
127
|
-
*
|
|
128
|
-
*
|
|
127
|
+
* Generates `ALTER TABLE content_{slug} ADD COLUMN {alias} {type}`.
|
|
128
|
+
* New columns are always nullable (SQLite limit on ALTER TABLE).
|
|
129
129
|
*/
|
|
130
130
|
export function generateAddColumn(seed, branch) {
|
|
131
131
|
const { sqlType } = BRANCH_TYPE_SQL[branch.type];
|
|
132
132
|
return `ALTER TABLE ${tableName(seed)} ADD COLUMN ${branch.alias} ${sqlType};`;
|
|
133
133
|
}
|
|
134
134
|
/**
|
|
135
|
-
*
|
|
136
|
-
*
|
|
135
|
+
* Generates B-tree indexes for status, created_at and every filterable Branch
|
|
136
|
+
* with an indexable type (text, number, date, boolean).
|
|
137
137
|
*/
|
|
138
138
|
export function generateIndexes(seed) {
|
|
139
139
|
const table = tableName(seed);
|
|
@@ -152,8 +152,8 @@ export function generateIndexes(seed) {
|
|
|
152
152
|
return indexes;
|
|
153
153
|
}
|
|
154
154
|
/**
|
|
155
|
-
*
|
|
156
|
-
*
|
|
155
|
+
* Generates the FTS5 virtual table for indexable text/richtext Branches.
|
|
156
|
+
* Returns null if the Seed has no branches with search enabled.
|
|
157
157
|
*/
|
|
158
158
|
export function generateFtsTable(seed) {
|
|
159
159
|
const rtBranches = indexableSearchBranches(seed);
|
|
@@ -170,9 +170,9 @@ export function generateFtsTable(seed) {
|
|
|
170
170
|
].join('\n');
|
|
171
171
|
}
|
|
172
172
|
/**
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
*
|
|
173
|
+
* Generates the 3 SQLite triggers (insert/update/delete) that keep the FTS
|
|
174
|
+
* automatically synchronized — eliminates the need for manual syncFts.
|
|
175
|
+
* Returns an empty array if the Seed has no indexable branches.
|
|
176
176
|
*/
|
|
177
177
|
export function generateFtsTriggers(seed) {
|
|
178
178
|
const rtBranches = indexableSearchBranches(seed);
|
|
@@ -208,9 +208,9 @@ export function generateFtsTriggers(seed) {
|
|
|
208
208
|
}
|
|
209
209
|
// ---- Query Builder ----
|
|
210
210
|
/**
|
|
211
|
-
*
|
|
212
|
-
*
|
|
213
|
-
*
|
|
211
|
+
* Builds a parameterized SELECT on `content_{slug}`.
|
|
212
|
+
* Never uses json_extract — every column is a real column.
|
|
213
|
+
* Unknown columns in filters/orderBy are ignored (fail-closed).
|
|
214
214
|
*/
|
|
215
215
|
export function buildSelectQuery(seed, options = {}) {
|
|
216
216
|
const table = tableName(seed);
|
|
@@ -369,8 +369,8 @@ function buildFilterCondition(col, type, cond, bindings) {
|
|
|
369
369
|
return null;
|
|
370
370
|
}
|
|
371
371
|
/**
|
|
372
|
-
*
|
|
373
|
-
*
|
|
372
|
+
* Returns the list of expected columns for a Seed's table.
|
|
373
|
+
* Used by `beech seed:load --diff` to compare current vs expected schema.
|
|
374
374
|
*/
|
|
375
375
|
export function getExpectedColumns(seed) {
|
|
376
376
|
return [
|
|
@@ -389,7 +389,7 @@ export function getExpectedColumns(seed) {
|
|
|
389
389
|
}
|
|
390
390
|
// ---- Serialization / Deserialization ----
|
|
391
391
|
/**
|
|
392
|
-
*
|
|
392
|
+
* Serializes a value for writing to the DB.
|
|
393
393
|
* boolean → 0/1 | date → Unix timestamp | json/asset-list → JSON string
|
|
394
394
|
*/
|
|
395
395
|
export function serializeForDb(branch, value) {
|
|
@@ -427,7 +427,7 @@ export function serializeForDb(branch, value) {
|
|
|
427
427
|
}
|
|
428
428
|
}
|
|
429
429
|
/**
|
|
430
|
-
*
|
|
430
|
+
* Deserializes a value read from the DB for the API response.
|
|
431
431
|
* 0/1 → boolean | Unix timestamp → ISO 8601 | JSON string → object
|
|
432
432
|
*/
|
|
433
433
|
export function deserializeFromDb(branch, value) {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Identifier abstraction. Hides direct calls to {@link crypto.randomUUID}
|
|
3
|
+
* so callers can swap in deterministic generators during tests for stable
|
|
4
|
+
* snapshot assertions and predictable insert ordering.
|
|
5
|
+
*/
|
|
6
|
+
export interface IIdGenerator {
|
|
7
|
+
/**
|
|
8
|
+
* Generates a new universally unique identifier.
|
|
9
|
+
* Production implementation delegates to crypto.randomUUID().
|
|
10
|
+
* Test implementations return deterministic values for snapshot assertions.
|
|
11
|
+
*/
|
|
12
|
+
uuid(): string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Production singleton. Delegates to the host runtime CSPRNG-backed UUID
|
|
16
|
+
* generator. Stateless and therefore safe to share across requests.
|
|
17
|
+
*/
|
|
18
|
+
export declare const SystemIdGenerator: IIdGenerator;
|
|
19
|
+
//# sourceMappingURL=id-generator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"id-generator.d.ts","sourceRoot":"","sources":["../src/id-generator.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,IAAI,IAAI,MAAM,CAAA;CACf;AAED;;;GAGG;AACH,eAAO,MAAM,iBAAiB,EAAE,YAE/B,CAAA"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @beechcms/core - Botanical Engine
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* In v0.4.0
|
|
6
|
-
*
|
|
4
|
+
* Shared package of the Beech CMS monorepo.
|
|
5
|
+
* In v0.4.0 the Botanical Engine is a SQL schema compiler: it reads TypeScript
|
|
6
|
+
* Seeds and generates deterministic DDL + parameterized queries.
|
|
7
7
|
*
|
|
8
8
|
* @module @beechcms/core
|
|
9
9
|
*/
|
|
@@ -20,4 +20,21 @@ export * from './idempotency.repository.js';
|
|
|
20
20
|
export * from './media.repository.js';
|
|
21
21
|
export * from './storage.js';
|
|
22
22
|
export * from './policies.js';
|
|
23
|
+
export * from './auth/hash-provider.js';
|
|
24
|
+
export * from './auth/token-service.js';
|
|
25
|
+
export * from './auth/user.repository.js';
|
|
26
|
+
export * from './auth/session.repository.js';
|
|
27
|
+
export * from './auth/password-reset-token.repository.js';
|
|
28
|
+
export * from './rate-limit/rate-limiter.js';
|
|
29
|
+
export * from './observability/activity-logger.js';
|
|
30
|
+
export * from './observability/activity-log.repository.js';
|
|
31
|
+
export * from './observability/analytics.repository.js';
|
|
32
|
+
export * from './notifications/notification.repository.js';
|
|
33
|
+
export * from './notifications/notification-service.js';
|
|
34
|
+
export * from './widget/widget.repository.js';
|
|
35
|
+
export * from './search/search.repository.js';
|
|
36
|
+
export * from './content-scan.repository.js';
|
|
37
|
+
export * from './clock.js';
|
|
38
|
+
export * from './id-generator.js';
|
|
39
|
+
export * from './seed-registry.js';
|
|
23
40
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,cAAc,YAAY,CAAA;AAC1B,cAAc,kBAAkB,CAAA;AAChC,cAAc,YAAY,CAAA;AAC1B,cAAc,aAAa,CAAA;AAC3B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,eAAe,CAAA;AAC7B,cAAc,sBAAsB,CAAA;AACpC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,yBAAyB,CAAA;AACvC,cAAc,6BAA6B,CAAA;AAC3C,cAAc,uBAAuB,CAAA;AACrC,cAAc,cAAc,CAAA;AAC5B,cAAc,eAAe,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,cAAc,YAAY,CAAA;AAC1B,cAAc,kBAAkB,CAAA;AAChC,cAAc,YAAY,CAAA;AAC1B,cAAc,aAAa,CAAA;AAC3B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,eAAe,CAAA;AAC7B,cAAc,sBAAsB,CAAA;AACpC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,yBAAyB,CAAA;AACvC,cAAc,6BAA6B,CAAA;AAC3C,cAAc,uBAAuB,CAAA;AACrC,cAAc,cAAc,CAAA;AAC5B,cAAc,eAAe,CAAA;AAC7B,cAAc,yBAAyB,CAAA;AACvC,cAAc,yBAAyB,CAAA;AACvC,cAAc,2BAA2B,CAAA;AACzC,cAAc,8BAA8B,CAAA;AAC5C,cAAc,2CAA2C,CAAA;AACzD,cAAc,8BAA8B,CAAA;AAC5C,cAAc,oCAAoC,CAAA;AAClD,cAAc,4CAA4C,CAAA;AAC1D,cAAc,yCAAyC,CAAA;AACvD,cAAc,4CAA4C,CAAA;AAC1D,cAAc,yCAAyC,CAAA;AACvD,cAAc,+BAA+B,CAAA;AAC7C,cAAc,+BAA+B,CAAA;AAC7C,cAAc,8BAA8B,CAAA;AAC5C,cAAc,YAAY,CAAA;AAC1B,cAAc,mBAAmB,CAAA;AACjC,cAAc,oBAAoB,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @beechcms/core - Botanical Engine
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* In v0.4.0
|
|
6
|
-
*
|
|
4
|
+
* Shared package of the Beech CMS monorepo.
|
|
5
|
+
* In v0.4.0 the Botanical Engine is a SQL schema compiler: it reads TypeScript
|
|
6
|
+
* Seeds and generates deterministic DDL + parameterized queries.
|
|
7
7
|
*
|
|
8
8
|
* @module @beechcms/core
|
|
9
9
|
*/
|
|
@@ -20,3 +20,20 @@ export * from './idempotency.repository.js';
|
|
|
20
20
|
export * from './media.repository.js';
|
|
21
21
|
export * from './storage.js';
|
|
22
22
|
export * from './policies.js';
|
|
23
|
+
export * from './auth/hash-provider.js';
|
|
24
|
+
export * from './auth/token-service.js';
|
|
25
|
+
export * from './auth/user.repository.js';
|
|
26
|
+
export * from './auth/session.repository.js';
|
|
27
|
+
export * from './auth/password-reset-token.repository.js';
|
|
28
|
+
export * from './rate-limit/rate-limiter.js';
|
|
29
|
+
export * from './observability/activity-logger.js';
|
|
30
|
+
export * from './observability/activity-log.repository.js';
|
|
31
|
+
export * from './observability/analytics.repository.js';
|
|
32
|
+
export * from './notifications/notification.repository.js';
|
|
33
|
+
export * from './notifications/notification-service.js';
|
|
34
|
+
export * from './widget/widget.repository.js';
|
|
35
|
+
export * from './search/search.repository.js';
|
|
36
|
+
export * from './content-scan.repository.js';
|
|
37
|
+
export * from './clock.js';
|
|
38
|
+
export * from './id-generator.js';
|
|
39
|
+
export * from './seed-registry.js';
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Notification service contract.
|
|
3
|
+
*
|
|
4
|
+
* High-level port that callers use to emit a notification without knowing
|
|
5
|
+
* about the underlying repository, the database, or background scheduling.
|
|
6
|
+
* Mirrors the email module pattern: handlers depend on the service, the
|
|
7
|
+
* service depends on the repository, the repository depends on D1.
|
|
8
|
+
*
|
|
9
|
+
* @module @beechcms/core/notifications/notification-service
|
|
10
|
+
*/
|
|
11
|
+
import type { NotificationType } from './notification.repository.js';
|
|
12
|
+
export interface CreateNotificationInput {
|
|
13
|
+
title: string;
|
|
14
|
+
message: string;
|
|
15
|
+
type?: NotificationType;
|
|
16
|
+
}
|
|
17
|
+
export interface INotificationService {
|
|
18
|
+
/**
|
|
19
|
+
* Emit a notification.
|
|
20
|
+
*
|
|
21
|
+
* Implementations decide whether to fire-and-forget (production: schedules
|
|
22
|
+
* the underlying repository write through `executionCtx.waitUntil`) or to
|
|
23
|
+
* wait inline (tests). Like the activity logger, this method MUST NOT
|
|
24
|
+
* throw to the caller: a failed notification must never break the public
|
|
25
|
+
* request that triggered it.
|
|
26
|
+
*/
|
|
27
|
+
notify(input: CreateNotificationInput): Promise<void> | void;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=notification-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notification-service.d.ts","sourceRoot":"","sources":["../../src/notifications/notification-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAA;AAEpE,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,gBAAgB,CAAA;CACxB;AAED,MAAM,WAAW,oBAAoB;IACnC;;;;;;;;OAQG;IACH,MAAM,CAAC,KAAK,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;CAC7D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Notification repository contract.
|
|
3
|
+
*
|
|
4
|
+
* Persists in-app notifications (admin inbox) and exposes the aggregate stats
|
|
5
|
+
* the GET /notifications handler needs to build a strong ETag without reading
|
|
6
|
+
* every row.
|
|
7
|
+
*
|
|
8
|
+
* @module @beechcms/core/notifications/notification.repository
|
|
9
|
+
*/
|
|
10
|
+
export type NotificationType = 'info' | 'success' | 'warning' | 'error';
|
|
11
|
+
export interface NotificationRecord {
|
|
12
|
+
id: string;
|
|
13
|
+
title: string;
|
|
14
|
+
message: string;
|
|
15
|
+
type: NotificationType;
|
|
16
|
+
isRead: boolean;
|
|
17
|
+
createdAt: number;
|
|
18
|
+
}
|
|
19
|
+
export interface NotificationStats {
|
|
20
|
+
totalCount: number;
|
|
21
|
+
latestCreatedAt: number;
|
|
22
|
+
readCount: number;
|
|
23
|
+
}
|
|
24
|
+
export interface INotificationRepository {
|
|
25
|
+
/**
|
|
26
|
+
* Return the most recent notifications, newest first.
|
|
27
|
+
*
|
|
28
|
+
* The `limit` parameter is required to prevent unbounded reads — the inbox
|
|
29
|
+
* could grow large over time and clients only ever render a window.
|
|
30
|
+
*/
|
|
31
|
+
list(limit: number): Promise<NotificationRecord[]>;
|
|
32
|
+
/**
|
|
33
|
+
* Return aggregate counters used to build the GET /notifications ETag.
|
|
34
|
+
*
|
|
35
|
+
* Computing `totalCount`, `latestCreatedAt` and `readCount` on the database
|
|
36
|
+
* side lets the handler skip serialising the full list when nothing has
|
|
37
|
+
* changed since the client's last poll, saving bandwidth on the dashboard.
|
|
38
|
+
*/
|
|
39
|
+
stats(): Promise<NotificationStats>;
|
|
40
|
+
/**
|
|
41
|
+
* Insert a new notification and return its generated id so callers (e.g.
|
|
42
|
+
* the public form submission flow) can correlate the notification with the
|
|
43
|
+
* triggering request.
|
|
44
|
+
*/
|
|
45
|
+
create(record: Omit<NotificationRecord, 'id' | 'createdAt' | 'isRead'>): Promise<string>;
|
|
46
|
+
markRead(notificationId: string): Promise<void>;
|
|
47
|
+
markUnread(notificationId: string): Promise<void>;
|
|
48
|
+
markAllRead(): Promise<void>;
|
|
49
|
+
delete(notificationId: string): Promise<void>;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=notification.repository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notification.repository.d.ts","sourceRoot":"","sources":["../../src/notifications/notification.repository.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAA;AAEvE,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,gBAAgB,CAAA;IACtB,MAAM,EAAE,OAAO,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,MAAM,CAAA;IAClB,eAAe,EAAE,MAAM,CAAA;IACvB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,uBAAuB;IACtC;;;;;OAKG;IACH,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAAA;IAElD;;;;;;OAMG;IACH,KAAK,IAAI,OAAO,CAAC,iBAAiB,CAAC,CAAA;IAEnC;;;;OAIG;IACH,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,kBAAkB,EAAE,IAAI,GAAG,WAAW,GAAG,QAAQ,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IAExF,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC/C,UAAU,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACjD,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IAC5B,MAAM,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAC9C"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Notification repository contract.
|
|
3
|
+
*
|
|
4
|
+
* Persists in-app notifications (admin inbox) and exposes the aggregate stats
|
|
5
|
+
* the GET /notifications handler needs to build a strong ETag without reading
|
|
6
|
+
* every row.
|
|
7
|
+
*
|
|
8
|
+
* @module @beechcms/core/notifications/notification.repository
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Activity Log read-side repository contract.
|
|
3
|
+
*
|
|
4
|
+
* Used by handlers that surface the audit trail back to the user (settings
|
|
5
|
+
* activity tab, recent activity feed on the dashboard). Kept separate from
|
|
6
|
+
* {@link IActivityLogger} because writers and readers have different
|
|
7
|
+
* lifecycles: writes are fire-and-forget background tasks; reads are blocking
|
|
8
|
+
* request paths that need filters and pagination.
|
|
9
|
+
*
|
|
10
|
+
* @module @beechcms/core/observability/activity-log.repository
|
|
11
|
+
*/
|
|
12
|
+
import type { ActivityAction, EntityType } from './activity-logger.js';
|
|
13
|
+
export interface ActivityLogRecord {
|
|
14
|
+
id: string;
|
|
15
|
+
userId: string;
|
|
16
|
+
userEmail: string;
|
|
17
|
+
userName: string | null;
|
|
18
|
+
action: ActivityAction;
|
|
19
|
+
entityType: EntityType;
|
|
20
|
+
entityId: string;
|
|
21
|
+
entitySlug: string | null;
|
|
22
|
+
details: Record<string, unknown> | null;
|
|
23
|
+
createdAt: number;
|
|
24
|
+
}
|
|
25
|
+
export interface ActivityLogListOptions {
|
|
26
|
+
/** Restrict the result to a specific user. Used by the per-user activity tab. */
|
|
27
|
+
userId?: string;
|
|
28
|
+
/** Restrict the result to a specific content seed. Used by per-seed feeds. */
|
|
29
|
+
entitySlug?: string;
|
|
30
|
+
/** Hard upper bound on the number of rows returned. */
|
|
31
|
+
limit: number;
|
|
32
|
+
}
|
|
33
|
+
export interface CountSinceOptions {
|
|
34
|
+
action: ActivityAction;
|
|
35
|
+
entityType: EntityType;
|
|
36
|
+
/** Lower bound (inclusive) for `createdAt` in seconds since epoch. */
|
|
37
|
+
sinceTimestamp: number;
|
|
38
|
+
}
|
|
39
|
+
export interface IActivityLogRepository {
|
|
40
|
+
/**
|
|
41
|
+
* Return the most recent activity entries matching the given filters.
|
|
42
|
+
*
|
|
43
|
+
* The list is always ordered by `createdAt` DESC so callers get newest-first
|
|
44
|
+
* data without paying extra sort costs in TypeScript. Used by the settings
|
|
45
|
+
* activity tab (per-user) and by the stats recent-activity feed (global).
|
|
46
|
+
*/
|
|
47
|
+
list(options: ActivityLogListOptions): Promise<ActivityLogRecord[]>;
|
|
48
|
+
/**
|
|
49
|
+
* Count entries with the given `action` and `entityType` whose `createdAt`
|
|
50
|
+
* is greater-than-or-equal to `sinceTimestamp`.
|
|
51
|
+
*
|
|
52
|
+
* Used by the dashboard `/stats/total` widget to surface today/week/month
|
|
53
|
+
* create-event counts without hardcoding SQL in the handler. Each period
|
|
54
|
+
* is one call so the repository contract stays narrow and deterministic.
|
|
55
|
+
*/
|
|
56
|
+
countSince(options: CountSinceOptions): Promise<number>;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=activity-log.repository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"activity-log.repository.d.ts","sourceRoot":"","sources":["../../src/observability/activity-log.repository.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AAEtE,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,MAAM,EAAE,cAAc,CAAA;IACtB,UAAU,EAAE,UAAU,CAAA;IACtB,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;IACvC,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,sBAAsB;IACrC,iFAAiF;IACjF,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,8EAA8E;IAC9E,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,uDAAuD;IACvD,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,cAAc,CAAA;IACtB,UAAU,EAAE,UAAU,CAAA;IACtB,sEAAsE;IACtE,cAAc,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,sBAAsB;IACrC;;;;;;OAMG;IACH,IAAI,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAAA;IAEnE;;;;;;;OAOG;IACH,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;CACxD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Activity Logger contract.
|
|
3
|
+
*
|
|
4
|
+
* Records side-effect events ("a user did X to entity Y") for the audit trail
|
|
5
|
+
* displayed in the dashboard's settings activity tab and the recent activity
|
|
6
|
+
* feed. The interface is intentionally framework-agnostic so it can be
|
|
7
|
+
* fulfilled in production by a D1-backed implementation, in tests by an
|
|
8
|
+
* in-memory implementation, and in the future by remote sinks.
|
|
9
|
+
*
|
|
10
|
+
* @module @beechcms/core/observability/activity-logger
|
|
11
|
+
*/
|
|
12
|
+
export type ActivityAction = 'create' | 'update' | 'delete' | 'upload';
|
|
13
|
+
export type EntityType = 'content' | 'media';
|
|
14
|
+
/**
|
|
15
|
+
* Identifies the human (or machine) actor that triggered the event.
|
|
16
|
+
*
|
|
17
|
+
* The actor MUST be assembled by the caller (typically a Hono handler that has
|
|
18
|
+
* already authenticated the request). The logger never reaches into a
|
|
19
|
+
* framework-specific context to discover who is acting — this avoids hidden
|
|
20
|
+
* coupling and lets the same logger run inside CLI scripts and background
|
|
21
|
+
* jobs.
|
|
22
|
+
*/
|
|
23
|
+
export interface ActivityActor {
|
|
24
|
+
id: string;
|
|
25
|
+
email: string;
|
|
26
|
+
name?: string | null;
|
|
27
|
+
}
|
|
28
|
+
export interface ActivityLogEntry {
|
|
29
|
+
action: ActivityAction;
|
|
30
|
+
entityType: EntityType;
|
|
31
|
+
entityId: string;
|
|
32
|
+
entitySlug?: string;
|
|
33
|
+
details?: Record<string, unknown>;
|
|
34
|
+
actor: ActivityActor;
|
|
35
|
+
}
|
|
36
|
+
export interface IActivityLogger {
|
|
37
|
+
/**
|
|
38
|
+
* Persist a single activity entry.
|
|
39
|
+
*
|
|
40
|
+
* Implementations decide whether to fire-and-forget (Cloudflare
|
|
41
|
+
* `executionCtx.waitUntil`) or to wait inline (test environments).
|
|
42
|
+
* In either case the call MUST NOT throw to the caller: logging is
|
|
43
|
+
* observability, never a hard dependency of the request being served.
|
|
44
|
+
* Internal errors must be swallowed and logged via `console.error` so the
|
|
45
|
+
* mainline response path is never disturbed by audit-trail failures.
|
|
46
|
+
*/
|
|
47
|
+
log(entry: ActivityLogEntry): Promise<void> | void;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=activity-logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"activity-logger.d.ts","sourceRoot":"","sources":["../../src/observability/activity-logger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAA;AACtE,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,OAAO,CAAA;AAE5C;;;;;;;;GAQG;AACH,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,cAAc,CAAA;IACtB,UAAU,EAAE,UAAU,CAAA;IACtB,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACjC,KAAK,EAAE,aAAa,CAAA;CACrB;AAED,MAAM,WAAW,eAAe;IAC9B;;;;;;;;;OASG;IACH,GAAG,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;CACnD"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Activity Logger contract.
|
|
3
|
+
*
|
|
4
|
+
* Records side-effect events ("a user did X to entity Y") for the audit trail
|
|
5
|
+
* displayed in the dashboard's settings activity tab and the recent activity
|
|
6
|
+
* feed. The interface is intentionally framework-agnostic so it can be
|
|
7
|
+
* fulfilled in production by a D1-backed implementation, in tests by an
|
|
8
|
+
* in-memory implementation, and in the future by remote sinks.
|
|
9
|
+
*
|
|
10
|
+
* @module @beechcms/core/observability/activity-logger
|
|
11
|
+
*/
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Metric tracked by the analytics table. Today only request counts and
|
|
3
|
+
* unique-visitor counts are recorded; new metric names should be appended
|
|
4
|
+
* to this union and to the implementation's switch statement.
|
|
5
|
+
*/
|
|
6
|
+
export type AnalyticsMetric = 'requests' | 'visitors';
|
|
7
|
+
/**
|
|
8
|
+
* Read/write contract for the analytics counters that power the dashboard
|
|
9
|
+
* widgets (per-day request totals, sparklines, system health proxy).
|
|
10
|
+
*
|
|
11
|
+
* Implementations must use idempotent upserts so the recording middleware
|
|
12
|
+
* can be invoked once per request without producing duplicates when the
|
|
13
|
+
* same day/seed/metric tuple is touched repeatedly.
|
|
14
|
+
*/
|
|
15
|
+
export interface IAnalyticsRepository {
|
|
16
|
+
/**
|
|
17
|
+
* Upserts a request counter for the given seed at the current day bucket.
|
|
18
|
+
* The day bucket (Unix timestamp truncated to midnight UTC) is computed
|
|
19
|
+
* internally from the implementation's clock so callers never need to
|
|
20
|
+
* pass a timestamp. Implementations must use INSERT ... ON CONFLICT DO
|
|
21
|
+
* UPDATE to remain idempotent under concurrent calls within the same day.
|
|
22
|
+
*/
|
|
23
|
+
recordRequest(seedSlug: string): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Returns the total count for the given metric since sinceTimestamp.
|
|
26
|
+
* Used by the stats handler for total request counts and visitor counts.
|
|
27
|
+
* The aggregation sums the stored counter values, not row counts.
|
|
28
|
+
*/
|
|
29
|
+
sumByMetric(metric: AnalyticsMetric, seedSlug: string, sinceTimestamp: number): Promise<number>;
|
|
30
|
+
/**
|
|
31
|
+
* Returns a map of date strings (YYYY-MM-DD) to request counts since
|
|
32
|
+
* sinceTimestamp, suitable for chart rendering without further
|
|
33
|
+
* transformation by the caller.
|
|
34
|
+
*/
|
|
35
|
+
groupByMetric(seedSlug: string, sinceTimestamp: number): Promise<Record<string, number>>;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=analytics.repository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analytics.repository.d.ts","sourceRoot":"","sources":["../../src/observability/analytics.repository.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG,UAAU,CAAA;AAErD;;;;;;;GAOG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;;;;OAMG;IACH,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAE9C;;;;OAIG;IACH,WAAW,CACT,MAAM,EAAE,eAAe,EACvB,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,MAAM,CAAC,CAAA;IAElB;;;;OAIG;IACH,aAAa,CACX,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;CACnC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { Seed } from '../types.js';
|
|
2
|
+
export interface SearchQueryOptions {
|
|
3
|
+
queryText: string;
|
|
4
|
+
schemaSlug: string | null;
|
|
5
|
+
statusFilter: string | null;
|
|
6
|
+
limit: number;
|
|
7
|
+
cursor: string | null;
|
|
8
|
+
}
|
|
9
|
+
export interface SearchResultRow {
|
|
10
|
+
entryId: string;
|
|
11
|
+
schemaSlug: string;
|
|
12
|
+
slug: string | null;
|
|
13
|
+
status: string;
|
|
14
|
+
title: string | null;
|
|
15
|
+
excerpt: string;
|
|
16
|
+
rank: number;
|
|
17
|
+
}
|
|
18
|
+
export interface SearchCountResult {
|
|
19
|
+
total: number;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Read-only contract for the global FTS5 search route.
|
|
23
|
+
*
|
|
24
|
+
* Implementations encapsulate the UNION ALL query that fans out across all
|
|
25
|
+
* `fts_<seed>` virtual tables. The route handler stays free of D1 and only
|
|
26
|
+
* shapes the final response.
|
|
27
|
+
*/
|
|
28
|
+
export interface ISearchRepository {
|
|
29
|
+
/**
|
|
30
|
+
* Executes a UNION ALL full-text search across all FTS-enabled seed tables.
|
|
31
|
+
* Returns at most options.limit + 1 rows so the caller can detect hasMore
|
|
32
|
+
* without a separate count query for the cursor case.
|
|
33
|
+
* Implementations must propagate the EMPTY_QUERY error thrown by
|
|
34
|
+
* buildFtsQuery so the route handler can return an empty result set rather
|
|
35
|
+
* than a 500.
|
|
36
|
+
*/
|
|
37
|
+
search(options: SearchQueryOptions, seeds: Seed[]): Promise<SearchResultRow[]>;
|
|
38
|
+
/**
|
|
39
|
+
* Runs the count variant of the FTS query to support the `total` field in
|
|
40
|
+
* the search response. Called in parallel with search() by the route handler
|
|
41
|
+
* with the same filter inputs but without limit/cursor.
|
|
42
|
+
*/
|
|
43
|
+
count(options: Omit<SearchQueryOptions, 'limit' | 'cursor'>, seeds: Seed[]): Promise<SearchCountResult>;
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=search.repository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.repository.d.ts","sourceRoot":"","sources":["../../src/search/search.repository.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAA;AAEvC,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;IAClB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAA;CACd;AAED;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;;;;;OAOG;IACH,MAAM,CAAC,OAAO,EAAE,kBAAkB,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAAA;IAE9E;;;;OAIG;IACH,KAAK,CACH,OAAO,EAAE,IAAI,CAAC,kBAAkB,EAAE,OAAO,GAAG,QAAQ,CAAC,EACrD,KAAK,EAAE,IAAI,EAAE,GACZ,OAAO,CAAC,iBAAiB,CAAC,CAAA;CAC9B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { Seed } from './types.js';
|
|
2
|
+
export interface ISeedRegistry {
|
|
3
|
+
/**
|
|
4
|
+
* Returns all seeds as a flat array, preserving insertion order.
|
|
5
|
+
* Decouples callers from the internal storage shape so the registry
|
|
6
|
+
* implementation can change without touching every route handler.
|
|
7
|
+
*/
|
|
8
|
+
all(): Seed[];
|
|
9
|
+
/**
|
|
10
|
+
* Returns the seed with the given slug, or null if not found.
|
|
11
|
+
* Provides a single lookup point that can be overridden in tests
|
|
12
|
+
* without rebuilding the full registry object.
|
|
13
|
+
*/
|
|
14
|
+
get(slug: string): Seed | null;
|
|
15
|
+
/**
|
|
16
|
+
* Returns seeds that are visible in the dashboard sidebar.
|
|
17
|
+
* A seed is visible when dashboard.hidden is not explicitly true.
|
|
18
|
+
* Eliminates the seeds.filter(s => !s.dashboard?.hidden) pattern
|
|
19
|
+
* that would otherwise be duplicated across route handlers.
|
|
20
|
+
*/
|
|
21
|
+
visibleInDashboard(): Seed[];
|
|
22
|
+
/**
|
|
23
|
+
* Returns seeds that have allowPublicRead enabled.
|
|
24
|
+
* Eliminates the seeds.filter(s => s.allowPublicRead) pattern.
|
|
25
|
+
*/
|
|
26
|
+
publicReadable(): Seed[];
|
|
27
|
+
/**
|
|
28
|
+
* Returns seeds that have the draft workflow enabled.
|
|
29
|
+
* Eliminates the seeds.filter(s => s.allowDrafts) pattern.
|
|
30
|
+
*/
|
|
31
|
+
draftEnabled(): Seed[];
|
|
32
|
+
}
|
|
33
|
+
export declare class SeedRegistry implements ISeedRegistry {
|
|
34
|
+
private readonly seedMap;
|
|
35
|
+
private readonly orderedSeeds;
|
|
36
|
+
constructor(seeds: Seed[]);
|
|
37
|
+
all(): Seed[];
|
|
38
|
+
get(slug: string): Seed | null;
|
|
39
|
+
visibleInDashboard(): Seed[];
|
|
40
|
+
publicReadable(): Seed[];
|
|
41
|
+
draftEnabled(): Seed[];
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Named subclass of SeedRegistry for use in test suites.
|
|
45
|
+
* Gives tests a semantic name without requiring any dependency on factory.ts.
|
|
46
|
+
*/
|
|
47
|
+
export declare class InMemorySeedRegistry extends SeedRegistry {
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=seed-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seed-registry.d.ts","sourceRoot":"","sources":["../src/seed-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AAEtC,MAAM,WAAW,aAAa;IAC5B;;;;OAIG;IACH,GAAG,IAAI,IAAI,EAAE,CAAA;IAEb;;;;OAIG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAA;IAE9B;;;;;OAKG;IACH,kBAAkB,IAAI,IAAI,EAAE,CAAA;IAE5B;;;OAGG;IACH,cAAc,IAAI,IAAI,EAAE,CAAA;IAExB;;;OAGG;IACH,YAAY,IAAI,IAAI,EAAE,CAAA;CACvB;AAED,qBAAa,YAAa,YAAW,aAAa;IAChD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmB;IAC3C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAQ;gBAEzB,KAAK,EAAE,IAAI,EAAE;IAKzB,GAAG,IAAI,IAAI,EAAE;IAIb,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAI9B,kBAAkB,IAAI,IAAI,EAAE;IAI5B,cAAc,IAAI,IAAI,EAAE;IAIxB,YAAY,IAAI,IAAI,EAAE;CAGvB;AAED;;;GAGG;AACH,qBAAa,oBAAqB,SAAQ,YAAY;CAAG"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export class SeedRegistry {
|
|
2
|
+
seedMap;
|
|
3
|
+
orderedSeeds;
|
|
4
|
+
constructor(seeds) {
|
|
5
|
+
this.orderedSeeds = seeds;
|
|
6
|
+
this.seedMap = new Map(seeds.map(seed => [seed.slug, seed]));
|
|
7
|
+
}
|
|
8
|
+
all() {
|
|
9
|
+
return this.orderedSeeds;
|
|
10
|
+
}
|
|
11
|
+
get(slug) {
|
|
12
|
+
return this.seedMap.get(slug) ?? null;
|
|
13
|
+
}
|
|
14
|
+
visibleInDashboard() {
|
|
15
|
+
return this.orderedSeeds.filter(seed => seed.dashboard?.hidden !== true);
|
|
16
|
+
}
|
|
17
|
+
publicReadable() {
|
|
18
|
+
return this.orderedSeeds.filter(seed => seed.allowPublicRead === true);
|
|
19
|
+
}
|
|
20
|
+
draftEnabled() {
|
|
21
|
+
return this.orderedSeeds.filter(seed => seed.allowDrafts === true);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Named subclass of SeedRegistry for use in test suites.
|
|
26
|
+
* Gives tests a semantic name without requiring any dependency on factory.ts.
|
|
27
|
+
*/
|
|
28
|
+
export class InMemorySeedRegistry extends SeedRegistry {
|
|
29
|
+
}
|
package/dist/validation.d.ts
CHANGED
|
@@ -21,18 +21,18 @@ export interface ValidateSeedPayloadResult {
|
|
|
21
21
|
hasAnyValidField: boolean;
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
24
|
+
* Common foundation for schema-driven validation and sanitization of payloads.
|
|
25
|
+
* Used by the Public API and reusable in the Botanical Engine.
|
|
26
26
|
*
|
|
27
|
-
*
|
|
27
|
+
* Mandatory execution order at the time of writing:
|
|
28
28
|
* validate raw → hash (privacy policy) → store
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
29
|
+
* Hashing occurs AFTER validation, not before.
|
|
30
|
+
* This ensures that the validated value is the original raw value,
|
|
31
|
+
* not the digest — avoiding false failures on fields with formats (e.g., email).
|
|
32
32
|
*/
|
|
33
33
|
export declare function validateAndSanitizeSeedPayload(seed: Seed, payload: Record<string, unknown>, options?: ValidateSeedPayloadOptions): ValidateSeedPayloadResult;
|
|
34
34
|
/**
|
|
35
|
-
*
|
|
35
|
+
* Validates content status supported by the CMS.
|
|
36
36
|
*/
|
|
37
37
|
export declare function isValidContentStatus(value: unknown): value is 'draft' | 'review' | 'published';
|
|
38
38
|
/**
|
package/dist/validation.js
CHANGED
|
@@ -452,14 +452,14 @@ function validateBranchValue(branch, alias, rawValue, options) {
|
|
|
452
452
|
}
|
|
453
453
|
}
|
|
454
454
|
/**
|
|
455
|
-
*
|
|
456
|
-
*
|
|
455
|
+
* Common foundation for schema-driven validation and sanitization of payloads.
|
|
456
|
+
* Used by the Public API and reusable in the Botanical Engine.
|
|
457
457
|
*
|
|
458
|
-
*
|
|
458
|
+
* Mandatory execution order at the time of writing:
|
|
459
459
|
* validate raw → hash (privacy policy) → store
|
|
460
|
-
*
|
|
461
|
-
*
|
|
462
|
-
*
|
|
460
|
+
* Hashing occurs AFTER validation, not before.
|
|
461
|
+
* This ensures that the validated value is the original raw value,
|
|
462
|
+
* not the digest — avoiding false failures on fields with formats (e.g., email).
|
|
463
463
|
*/
|
|
464
464
|
export function validateAndSanitizeSeedPayload(seed, payload, options = {}) {
|
|
465
465
|
const normalizedOptions = {
|
|
@@ -564,7 +564,7 @@ export function validateAndSanitizeSeedPayload(seed, payload, options = {}) {
|
|
|
564
564
|
};
|
|
565
565
|
}
|
|
566
566
|
/**
|
|
567
|
-
*
|
|
567
|
+
* Validates content status supported by the CMS.
|
|
568
568
|
*/
|
|
569
569
|
export function isValidContentStatus(value) {
|
|
570
570
|
return statusSchema.safeParse(value).success;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import type { Seed } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Discriminated union describing the aggregate to compute over a content table.
|
|
4
|
+
*
|
|
5
|
+
* The widget routes accept this shape from the dashboard. Implementations are
|
|
6
|
+
* responsible for translating each variant into a safe SQL expression and must
|
|
7
|
+
* never interpolate the column or value fields without prior validation.
|
|
8
|
+
*/
|
|
9
|
+
export type AggregateFormula = {
|
|
10
|
+
op: 'count';
|
|
11
|
+
} | {
|
|
12
|
+
op: 'sum';
|
|
13
|
+
column: string;
|
|
14
|
+
} | {
|
|
15
|
+
op: 'avg';
|
|
16
|
+
column: string;
|
|
17
|
+
} | {
|
|
18
|
+
op: 'min';
|
|
19
|
+
column: string;
|
|
20
|
+
} | {
|
|
21
|
+
op: 'max';
|
|
22
|
+
column: string;
|
|
23
|
+
} | {
|
|
24
|
+
op: 'countWhere';
|
|
25
|
+
column: string;
|
|
26
|
+
value: unknown;
|
|
27
|
+
} | {
|
|
28
|
+
op: 'percentageOf';
|
|
29
|
+
numeratorColumn: string;
|
|
30
|
+
denominatorColumn: string;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Time range applied as a WHERE filter for widget queries.
|
|
34
|
+
*
|
|
35
|
+
* "all" means no temporal restriction. The other values bracket the most recent
|
|
36
|
+
* 7 days, 1 month, and 1 year respectively, anchored on `created_at`.
|
|
37
|
+
*/
|
|
38
|
+
export type TimeWindow = 'week' | 'month' | 'year' | 'all';
|
|
39
|
+
export interface LeaderboardEntry {
|
|
40
|
+
id: string;
|
|
41
|
+
label: string;
|
|
42
|
+
score: number | string;
|
|
43
|
+
}
|
|
44
|
+
export interface LeaderboardOptions {
|
|
45
|
+
scoreColumn: string;
|
|
46
|
+
limit: number;
|
|
47
|
+
orderDirection: 'ASC' | 'DESC';
|
|
48
|
+
}
|
|
49
|
+
export interface TimeseriesPoint {
|
|
50
|
+
label: string;
|
|
51
|
+
value: number;
|
|
52
|
+
}
|
|
53
|
+
export interface WidgetListFilter {
|
|
54
|
+
column: string;
|
|
55
|
+
op: string;
|
|
56
|
+
value: unknown;
|
|
57
|
+
}
|
|
58
|
+
export interface WidgetListOptions {
|
|
59
|
+
limit: number;
|
|
60
|
+
offset: number;
|
|
61
|
+
search?: string;
|
|
62
|
+
filters?: WidgetListFilter[];
|
|
63
|
+
orderByColumn?: string;
|
|
64
|
+
orderDirection?: 'ASC' | 'DESC';
|
|
65
|
+
}
|
|
66
|
+
export interface WidgetListResult {
|
|
67
|
+
entries: Array<Record<string, unknown>>;
|
|
68
|
+
totalCount: number;
|
|
69
|
+
}
|
|
70
|
+
export interface GrowthResult {
|
|
71
|
+
currentValue: number;
|
|
72
|
+
previousValue: number;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Read-only data access contract for widget routes.
|
|
76
|
+
*
|
|
77
|
+
* Implementations must validate every column alias derived from user input
|
|
78
|
+
* against the seed before composing SQL, and must bind every user-supplied
|
|
79
|
+
* value via parameterised statements. SQL keywords (ORDER direction, aggregate
|
|
80
|
+
* function names) must be selected via hardcoded branches, never interpolated.
|
|
81
|
+
*/
|
|
82
|
+
export interface IWidgetRepository {
|
|
83
|
+
/**
|
|
84
|
+
* Returns the formula result for the given time window. Always returns a
|
|
85
|
+
* number; implementations must return 0 when the query produces no rows.
|
|
86
|
+
*/
|
|
87
|
+
aggregate(seed: Seed, formula: AggregateFormula, window: TimeWindow): Promise<number>;
|
|
88
|
+
/**
|
|
89
|
+
* Evaluates the formula twice — once for the current window period and once
|
|
90
|
+
* for the equivalent previous period — to support trend calculations.
|
|
91
|
+
* Implementations must return { currentValue: 0, previousValue: 0 } on
|
|
92
|
+
* empty results.
|
|
93
|
+
*/
|
|
94
|
+
growth(seed: Seed, formula: AggregateFormula, window: TimeWindow): Promise<GrowthResult>;
|
|
95
|
+
/**
|
|
96
|
+
* Returns entries sorted by scoreColumn, excluding nulls. label resolves
|
|
97
|
+
* from seed.displayNameAlias; falls back to id when not set.
|
|
98
|
+
*/
|
|
99
|
+
leaderboard(seed: Seed, options: LeaderboardOptions): Promise<LeaderboardEntry[]>;
|
|
100
|
+
/**
|
|
101
|
+
* Paginated read of content entries. Filters and search are applied
|
|
102
|
+
* server-side. The caller is responsible for deserialising branch values
|
|
103
|
+
* from the raw Record.
|
|
104
|
+
*/
|
|
105
|
+
list(seed: Seed, options: WidgetListOptions): Promise<WidgetListResult>;
|
|
106
|
+
/**
|
|
107
|
+
* Groups entries by a date bucket derived from groupColumn and aggregates
|
|
108
|
+
* the formula. Days with no entries are omitted (no zero-fill). Points are
|
|
109
|
+
* ordered ascending by label.
|
|
110
|
+
*/
|
|
111
|
+
timeseries(seed: Seed, formula: AggregateFormula, window: TimeWindow, groupColumn: string): Promise<TimeseriesPoint[]>;
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=widget.repository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"widget.repository.d.ts","sourceRoot":"","sources":["../../src/widget/widget.repository.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAA;AAEvC;;;;;;GAMG;AACH,MAAM,MAAM,gBAAgB,GACxB;IAAE,EAAE,EAAE,OAAO,CAAA;CAAE,GACf;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC7B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC7B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC7B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC7B;IAAE,EAAE,EAAE,YAAY,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,GACpD;IAAE,EAAE,EAAE,cAAc,CAAC;IAAC,eAAe,EAAE,MAAM,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAA;CAAE,CAAA;AAE9E;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,KAAK,CAAA;AAE1D,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,GAAG,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAA;IACnB,KAAK,EAAE,MAAM,CAAA;IACb,cAAc,EAAE,KAAK,GAAG,MAAM,CAAA;CAC/B;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAA;IACd,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,OAAO,CAAA;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,gBAAgB,EAAE,CAAA;IAC5B,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,cAAc,CAAC,EAAE,KAAK,GAAG,MAAM,CAAA;CAChC;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;IACvC,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,MAAM,CAAA;IACpB,aAAa,EAAE,MAAM,CAAA;CACtB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IAErF;;;;;OAKG;IACH,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;IAExF;;;OAGG;IACH,WAAW,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAA;IAEjF;;;;OAIG;IACH,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAA;IAEvE;;;;OAIG;IACH,UAAU,CACR,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,gBAAgB,EACzB,MAAM,EAAE,UAAU,EAClB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,eAAe,EAAE,CAAC,CAAA;CAC9B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@beechcms/core",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -18,18 +18,24 @@
|
|
|
18
18
|
"build": "tsc",
|
|
19
19
|
"dev": "tsc -w --preserveWatchOutput",
|
|
20
20
|
"lint": "eslint .",
|
|
21
|
-
"type-check": "tsc --noEmit"
|
|
21
|
+
"type-check": "tsc --noEmit",
|
|
22
|
+
"test": "vitest run",
|
|
23
|
+
"test:coverage": "vitest run --coverage"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
27
|
+
"vitest": "^3.2.4"
|
|
22
28
|
},
|
|
23
29
|
"dependencies": {
|
|
24
30
|
"@tiptap/core": "^3.22.3",
|
|
25
31
|
"@tiptap/extension-highlight": "^3.22.3",
|
|
26
32
|
"@tiptap/extension-image": "^3.22.3",
|
|
27
33
|
"@tiptap/extension-link": "^3.22.3",
|
|
34
|
+
"@tiptap/extension-mathematics": "^3.22.3",
|
|
28
35
|
"@tiptap/extension-subscript": "^3.22.3",
|
|
29
36
|
"@tiptap/extension-superscript": "^3.22.3",
|
|
30
37
|
"@tiptap/extension-table": "^3.22.3",
|
|
31
38
|
"@tiptap/extension-text-align": "^3.22.3",
|
|
32
|
-
"@tiptap/extension-mathematics": "^3.22.3",
|
|
33
39
|
"@tiptap/html": "^3.22.3",
|
|
34
40
|
"@tiptap/starter-kit": "^3.22.3",
|
|
35
41
|
"katex": "^0.16.11",
|