@beechcms/core 0.6.0-preview.3 → 0.6.0-preview.4

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/index.d.ts CHANGED
@@ -45,6 +45,8 @@ export * from './automations.runner.interface.js';
45
45
  export * from './automations.repository.interface.js';
46
46
  export * from './scheduler.interface.js';
47
47
  export * from './scheduler.stub.js';
48
+ export * from './queue.interface.js';
49
+ export * from './queue.stub.js';
48
50
  export * from './webhook-validation.js';
49
51
  export * from './relations.js';
50
52
  export * from './site-settings.repository.js';
@@ -60,4 +62,6 @@ export * from './seed.repository.js';
60
62
  export * from './seed-validation.js';
61
63
  export * from './seed-ddl.js';
62
64
  export * from './schema-mutator.js';
65
+ export * from './seed-types-generator.js';
66
+ export * from './webhook-crypto.js';
63
67
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AAEH,cAAc,YAAY,CAAA;AAC1B,cAAc,iBAAiB,CAAA;AAC/B,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,YAAY,CAAA;AAC1B,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;AAClC,cAAc,wBAAwB,CAAA;AACtC,cAAc,gCAAgC,CAAA;AAC9C,cAAc,mCAAmC,CAAA;AACjD,cAAc,uCAAuC,CAAA;AACrD,cAAc,0BAA0B,CAAA;AACxC,cAAc,qBAAqB,CAAA;AACnC,cAAc,yBAAyB,CAAA;AACvC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,+BAA+B,CAAA;AAC7C,cAAc,2BAA2B,CAAA;AACzC,cAAc,kBAAkB,CAAA;AAChC,cAAc,6BAA6B,CAAA;AAC3C,cAAc,yBAAyB,CAAA;AACvC,cAAc,uBAAuB,CAAA;AACrC,cAAc,uBAAuB,CAAA;AACrC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,kCAAkC,CAAA;AAChD,cAAc,sBAAsB,CAAA;AACpC,cAAc,sBAAsB,CAAA;AACpC,cAAc,eAAe,CAAA;AAC7B,cAAc,qBAAqB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AAEH,cAAc,YAAY,CAAA;AAC1B,cAAc,iBAAiB,CAAA;AAC/B,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,YAAY,CAAA;AAC1B,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;AAClC,cAAc,wBAAwB,CAAA;AACtC,cAAc,gCAAgC,CAAA;AAC9C,cAAc,mCAAmC,CAAA;AACjD,cAAc,uCAAuC,CAAA;AACrD,cAAc,0BAA0B,CAAA;AACxC,cAAc,qBAAqB,CAAA;AACnC,cAAc,sBAAsB,CAAA;AACpC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,yBAAyB,CAAA;AACvC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,+BAA+B,CAAA;AAC7C,cAAc,2BAA2B,CAAA;AACzC,cAAc,kBAAkB,CAAA;AAChC,cAAc,6BAA6B,CAAA;AAC3C,cAAc,yBAAyB,CAAA;AACvC,cAAc,uBAAuB,CAAA;AACrC,cAAc,uBAAuB,CAAA;AACrC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,kCAAkC,CAAA;AAChD,cAAc,sBAAsB,CAAA;AACpC,cAAc,sBAAsB,CAAA;AACpC,cAAc,eAAe,CAAA;AAC7B,cAAc,qBAAqB,CAAA;AACnC,cAAc,2BAA2B,CAAA;AACzC,cAAc,qBAAqB,CAAA"}
package/dist/index.js CHANGED
@@ -47,6 +47,8 @@ export * from './automations.runner.interface.js';
47
47
  export * from './automations.repository.interface.js';
48
48
  export * from './scheduler.interface.js';
49
49
  export * from './scheduler.stub.js';
50
+ export * from './queue.interface.js';
51
+ export * from './queue.stub.js';
50
52
  export * from './webhook-validation.js';
51
53
  export * from './relations.js';
52
54
  export * from './site-settings.repository.js';
@@ -62,3 +64,5 @@ export * from './seed.repository.js';
62
64
  export * from './seed-validation.js';
63
65
  export * from './seed-ddl.js';
64
66
  export * from './schema-mutator.js';
67
+ export * from './seed-types-generator.js';
68
+ export * from './webhook-crypto.js';
@@ -0,0 +1,39 @@
1
+ import type { ContentRepository } from './content.repository.js';
2
+ import type { BeechBucket } from './storage.js';
3
+ import type { IClock } from './clock.js';
4
+ import type { IIdGenerator } from './id-generator.js';
5
+ /**
6
+ * The envelope put on the wire / queue. `name` selects the handler from the
7
+ * JobRegistry; `payload` is the developer-supplied, JSON-serializable body.
8
+ */
9
+ export interface QueueMessage<T = unknown> {
10
+ name: string;
11
+ payload: T;
12
+ }
13
+ /**
14
+ * Execution context handed to every job. Botanical Invariant: jobs receive the
15
+ * engine-mediated repository, NEVER a raw D1Database. `env` is a read-only,
16
+ * stringly-typed view of bindings/secrets for things like fetch targets — it
17
+ * deliberately does not expose `DB`.
18
+ */
19
+ export interface JobContext {
20
+ repository: ContentRepository;
21
+ bucket: BeechBucket;
22
+ clock: IClock;
23
+ idGenerator: IIdGenerator;
24
+ env: Record<string, string | undefined>;
25
+ }
26
+ /** A single background worker. Receives the decoded payload + context. */
27
+ export type JobHandler<T = unknown> = (payload: T, context: JobContext) => Promise<void>;
28
+ /** Name -> handler map, supplied via BeechConfig.jobs. */
29
+ export type JobRegistry = Record<string, JobHandler<any>>;
30
+ /**
31
+ * Producer port. `enqueue` schedules deferred work and returns once the message
32
+ * is accepted by the transport. Like INotificationService, implementations MUST
33
+ * decide their own fire-and-forget vs inline semantics and MUST NOT let a
34
+ * transport failure crash the request that called enqueue.
35
+ */
36
+ export interface IQueueService {
37
+ enqueue<T>(name: string, payload: T): Promise<void>;
38
+ }
39
+ //# sourceMappingURL=queue.interface.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue.interface.d.ts","sourceRoot":"","sources":["../src/queue.interface.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AAChE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AACxC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAErD;;;GAGG;AACH,MAAM,WAAW,YAAY,CAAC,CAAC,GAAG,OAAO;IACvC,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,CAAC,CAAA;CACX;AAED;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,iBAAiB,CAAA;IAC7B,MAAM,EAAE,WAAW,CAAA;IACnB,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,YAAY,CAAA;IACzB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;CACxC;AAED,0EAA0E;AAC1E,MAAM,MAAM,UAAU,CAAC,CAAC,GAAG,OAAO,IAAI,CACpC,OAAO,EAAE,CAAC,EACV,OAAO,EAAE,UAAU,KAChB,OAAO,CAAC,IAAI,CAAC,CAAA;AAElB,0DAA0D;AAC1D,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC,CAAA;AAEzD;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACpD"}
@@ -0,0 +1,3 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Copyright (c) 2024–2026 Flavio De Musso
3
+ export {};
@@ -0,0 +1,6 @@
1
+ import type { IQueueService } from './queue.interface.js';
2
+ /** Safe no-op producer (e.g. unit tests that don't assert on enqueue). */
3
+ export declare class NoOpQueueService implements IQueueService {
4
+ enqueue<T>(_name: string, _payload: T): Promise<void>;
5
+ }
6
+ //# sourceMappingURL=queue.stub.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue.stub.d.ts","sourceRoot":"","sources":["../src/queue.stub.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAEzD,0EAA0E;AAC1E,qBAAa,gBAAiB,YAAW,aAAa;IAC9C,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;CAG5D"}
@@ -0,0 +1,8 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Copyright (c) 2024–2026 Flavio De Musso
3
+ /** Safe no-op producer (e.g. unit tests that don't assert on enqueue). */
4
+ export class NoOpQueueService {
5
+ async enqueue(_name, _payload) {
6
+ // intentionally empty
7
+ }
8
+ }
@@ -0,0 +1,10 @@
1
+ import type { Seed, Branch } from './types.js';
2
+ /** slug/alias → PascalCase identifier. 'blog-posts' → 'BlogPosts'. */
3
+ export declare function pascalCase(input: string): string;
4
+ /** Maps a single Branch to its TypeScript type expression (no optional marker). */
5
+ export declare function tsTypeForBranch(branch: Branch): string;
6
+ /** Emits one exported interface for a single Seed. */
7
+ export declare function interfaceForSeed(seed: Seed): string;
8
+ /** Pure entry point. Deterministic: sorts seeds by slug for stable diffs. */
9
+ export declare function generateSeedTypes(seeds: Seed[]): string;
10
+ //# sourceMappingURL=seed-types-generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seed-types-generator.d.ts","sourceRoot":"","sources":["../src/seed-types-generator.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAe9C,sEAAsE;AACtE,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMhD;AAMD,mFAAmF;AACnF,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAoCtD;AAWD,sDAAsD;AACtD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAYnD;AAED,6EAA6E;AAC7E,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,MAAM,CASvD"}
@@ -0,0 +1,86 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Copyright (c) 2024–2026 Flavio De Musso
3
+ const HEADER = '// Questo file è generato automaticamente da BeechCMS CLI. Non modificarlo direttamente.\n';
4
+ /** System columns emitted by generateCreateTable — always present, never optional. */
5
+ const SYSTEM_FIELDS = ` id: string\n` +
6
+ ` slug: string\n` +
7
+ ` status: 'draft' | 'review' | 'published' | 'archived'\n`;
8
+ const SYSTEM_TIMESTAMPS = ` created_at: number\n` +
9
+ ` updated_at: number\n`;
10
+ /** slug/alias → PascalCase identifier. 'blog-posts' → 'BlogPosts'. */
11
+ export function pascalCase(input) {
12
+ return input
13
+ .split(/[^A-Za-z0-9]+/)
14
+ .filter(Boolean)
15
+ .map(w => w.charAt(0).toUpperCase() + w.slice(1))
16
+ .join('');
17
+ }
18
+ function literalUnion(options) {
19
+ return options.map(o => `'${o.replace(/'/g, "\\'")}'`).join(' | ');
20
+ }
21
+ /** Maps a single Branch to its TypeScript type expression (no optional marker). */
22
+ export function tsTypeForBranch(branch) {
23
+ switch (branch.type) {
24
+ case 'number':
25
+ case 'date':
26
+ return 'number';
27
+ case 'boolean':
28
+ return 'boolean';
29
+ case 'json':
30
+ return 'unknown';
31
+ case 'text':
32
+ case 'richtext':
33
+ case 'file': {
34
+ const base = branch.type === 'text' && branch.options?.length
35
+ ? literalUnion(branch.options)
36
+ : 'string';
37
+ return branch.type === 'file' && branch.multiple ? `${base}[]` : base;
38
+ }
39
+ case 'tags': {
40
+ const base = branch.options?.length ? `(${literalUnion(branch.options)})` : 'string';
41
+ return `${base}[]`;
42
+ }
43
+ case 'relation':
44
+ return branch.multiple ? 'string[]' : 'string';
45
+ case 'repeater': {
46
+ const inner = (branch.fields ?? [])
47
+ .map(f => `${propName(f)}${optional(f)}: ${tsTypeForBranch(f)}`)
48
+ .join('; ');
49
+ return `Array<{ ${inner} }>`;
50
+ }
51
+ default: {
52
+ // Exhaustiveness guard — fails the build if BranchType gains a member.
53
+ const _never = branch.type;
54
+ return _never;
55
+ }
56
+ }
57
+ }
58
+ function optional(branch) {
59
+ return branch.requiredOnCreate ? '' : '?';
60
+ }
61
+ function propName(branch) {
62
+ // alias is a valid SQL column; quote only if not a plain identifier.
63
+ return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(branch.alias) ? branch.alias : `'${branch.alias}'`;
64
+ }
65
+ /** Emits one exported interface for a single Seed. */
66
+ export function interfaceForSeed(seed) {
67
+ const name = pascalCase(seed.slug);
68
+ const props = seed.branches
69
+ .map(b => ` ${propName(b)}${optional(b)}: ${tsTypeForBranch(b)}`)
70
+ .join('\n');
71
+ return (`export interface ${name} {\n` +
72
+ SYSTEM_FIELDS +
73
+ (props ? props + '\n' : '') +
74
+ SYSTEM_TIMESTAMPS +
75
+ `}\n`);
76
+ }
77
+ /** Pure entry point. Deterministic: sorts seeds by slug for stable diffs. */
78
+ export function generateSeedTypes(seeds) {
79
+ const sorted = [...seeds].sort((a, b) => a.slug.localeCompare(b.slug));
80
+ const interfaces = sorted.map(interfaceForSeed).join('\n');
81
+ const registry = `export interface SeedRegistryTypes {\n` +
82
+ sorted.map(s => ` ${propName({ alias: s.slug })}: ${pascalCase(s.slug)}`).join('\n') +
83
+ (sorted.length ? '\n' : '') +
84
+ `}\n`;
85
+ return `${HEADER}\n${interfaces}\n${registry}`;
86
+ }
@@ -0,0 +1,8 @@
1
+ /** Signs a raw request body. Returns `sha256=<hex>`. */
2
+ export declare function signWebhookBody(body: string, secret: string): Promise<string>;
3
+ /**
4
+ * Verifies an inbound signature against the recomputed HMAC.
5
+ * Accepts the `sha256=` prefix on the provided signature (optional).
6
+ */
7
+ export declare function verifyWebhookSignature(body: string, signature: string | null | undefined, secret: string): Promise<boolean>;
8
+ //# sourceMappingURL=webhook-crypto.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhook-crypto.d.ts","sourceRoot":"","sources":["../src/webhook-crypto.ts"],"names":[],"mappings":"AAqBA,wDAAwD;AACxD,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAInF;AAUD;;;GAGG;AACH,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACpC,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,OAAO,CAAC,CAKlB"}
@@ -0,0 +1,37 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Copyright (c) 2024–2026 Flavio De Musso
3
+ const SIG_PREFIX = 'sha256=';
4
+ function toHex(buf) {
5
+ return Array.from(new Uint8Array(buf))
6
+ .map((b) => b.toString(16).padStart(2, '0'))
7
+ .join('');
8
+ }
9
+ async function hmacKey(secret) {
10
+ return crypto.subtle.importKey('raw', new TextEncoder().encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
11
+ }
12
+ /** Signs a raw request body. Returns `sha256=<hex>`. */
13
+ export async function signWebhookBody(body, secret) {
14
+ const key = await hmacKey(secret);
15
+ const sig = await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(body));
16
+ return SIG_PREFIX + toHex(sig);
17
+ }
18
+ /** Constant-time comparison of two equal-or-unequal-length hex strings. */
19
+ function timingSafeEqual(a, b) {
20
+ if (a.length !== b.length)
21
+ return false;
22
+ let mismatch = 0;
23
+ for (let i = 0; i < a.length; i++)
24
+ mismatch |= a.charCodeAt(i) ^ b.charCodeAt(i);
25
+ return mismatch === 0;
26
+ }
27
+ /**
28
+ * Verifies an inbound signature against the recomputed HMAC.
29
+ * Accepts the `sha256=` prefix on the provided signature (optional).
30
+ */
31
+ export async function verifyWebhookSignature(body, signature, secret) {
32
+ if (!signature || !secret)
33
+ return false;
34
+ const expected = await signWebhookBody(body, secret);
35
+ const provided = signature.startsWith(SIG_PREFIX) ? signature : SIG_PREFIX + signature;
36
+ return timingSafeEqual(expected, provided);
37
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@beechcms/core",
3
- "version": "0.6.0-preview.3",
3
+ "version": "0.6.0-preview.4",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -12,6 +12,10 @@
12
12
  ".": {
13
13
  "import": "./dist/index.js",
14
14
  "types": "./dist/index.d.ts"
15
+ },
16
+ "./webhook-crypto": {
17
+ "import": "./dist/webhook-crypto.js",
18
+ "types": "./dist/webhook-crypto.d.ts"
15
19
  }
16
20
  },
17
21
  "devDependencies": {