@beechcms/core 0.4.0-preview.9 → 0.4.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/auth/hash-provider.d.ts +13 -0
- package/dist/auth/hash-provider.d.ts.map +1 -0
- package/dist/auth/hash-provider.js +1 -0
- package/dist/auth/password-reset-token.repository.d.ts +29 -0
- package/dist/auth/password-reset-token.repository.d.ts.map +1 -0
- package/dist/auth/password-reset-token.repository.js +1 -0
- package/dist/auth/session.repository.d.ts +47 -0
- package/dist/auth/session.repository.d.ts.map +1 -0
- package/dist/auth/session.repository.js +1 -0
- package/dist/auth/token-service.d.ts +23 -0
- package/dist/auth/token-service.d.ts.map +1 -0
- package/dist/auth/token-service.js +1 -0
- package/dist/auth/user.repository.d.ts +47 -0
- package/dist/auth/user.repository.d.ts.map +1 -0
- package/dist/auth/user.repository.js +1 -0
- package/dist/content.repository.d.ts +95 -0
- package/dist/content.repository.d.ts.map +1 -0
- package/dist/content.repository.js +29 -0
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +76 -12
- package/dist/idempotency.repository.d.ts +13 -0
- package/dist/idempotency.repository.d.ts.map +1 -0
- package/dist/idempotency.repository.js +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/media.repository.d.ts +61 -0
- package/dist/media.repository.d.ts.map +1 -0
- package/dist/media.repository.js +1 -0
- package/dist/rate-limit/rate-limiter.d.ts +13 -0
- package/dist/rate-limit/rate-limiter.d.ts.map +1 -0
- package/dist/rate-limit/rate-limiter.js +1 -0
- package/dist/storage.d.ts +72 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +1 -0
- package/dist/types.d.ts +3 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +8 -2
- package/package.json +1 -1
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface IHashProvider {
|
|
2
|
+
/**
|
|
3
|
+
* Hashes a plaintext password using a one-way algorithm so that the original
|
|
4
|
+
* value can never be recovered from the stored digest.
|
|
5
|
+
*/
|
|
6
|
+
hash(plaintextPassword: string): Promise<string>;
|
|
7
|
+
/**
|
|
8
|
+
* Verifies a plaintext password against a stored hash using a constant-time
|
|
9
|
+
* comparison to prevent timing-based attacks that could reveal whether a hash exists.
|
|
10
|
+
*/
|
|
11
|
+
verify(plaintextPassword: string, storedHash: string): Promise<boolean>;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=hash-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hash-provider.d.ts","sourceRoot":"","sources":["../../src/auth/hash-provider.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,IAAI,CAAC,iBAAiB,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IAEhD;;;OAGG;IACH,MAAM,CAAC,iBAAiB,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;CACxE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface NewPasswordResetToken {
|
|
2
|
+
id: string;
|
|
3
|
+
userId: string;
|
|
4
|
+
tokenHash: string;
|
|
5
|
+
expiresAt: number;
|
|
6
|
+
}
|
|
7
|
+
export interface ValidatedResetToken {
|
|
8
|
+
id: string;
|
|
9
|
+
userId: string;
|
|
10
|
+
email: string;
|
|
11
|
+
}
|
|
12
|
+
export interface IPasswordResetTokenRepository {
|
|
13
|
+
/**
|
|
14
|
+
* Marks all pending (unused) tokens for the user as consumed before issuing a new one.
|
|
15
|
+
* Ensures only one active reset token exists per user at any time.
|
|
16
|
+
*/
|
|
17
|
+
invalidatePending(userId: string, nowTimestamp: number): Promise<void>;
|
|
18
|
+
/** Stores a new password reset token. Only the hash is persisted, never the plaintext. */
|
|
19
|
+
create(record: NewPasswordResetToken): Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* Finds a valid reset token by its hash, joining the users table to return the
|
|
22
|
+
* associated email in the same query to avoid a second round-trip.
|
|
23
|
+
* Returns null if the token is expired, already used, or not found.
|
|
24
|
+
*/
|
|
25
|
+
findValidByHashWithEmail(tokenHash: string, nowTimestamp: number): Promise<ValidatedResetToken | null>;
|
|
26
|
+
/** Marks a token as consumed so it cannot be used again. */
|
|
27
|
+
markUsed(tokenId: string, nowTimestamp: number): Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=password-reset-token.repository.d.ts.map
|
|
@@ -0,0 +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,EAAE,EAAE,MAAM,CAAA;IACV,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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export interface NewRefreshToken {
|
|
2
|
+
id: string;
|
|
3
|
+
userId: string;
|
|
4
|
+
tokenHash: string;
|
|
5
|
+
expiresAt: number;
|
|
6
|
+
}
|
|
7
|
+
export interface RefreshTokenRecord {
|
|
8
|
+
id: string;
|
|
9
|
+
userId: string;
|
|
10
|
+
tokenHash: string;
|
|
11
|
+
expiresAt: number;
|
|
12
|
+
createdAt: number;
|
|
13
|
+
revokedAt: number | null;
|
|
14
|
+
}
|
|
15
|
+
export interface ActiveSessionSummary {
|
|
16
|
+
id: string;
|
|
17
|
+
createdAt: number;
|
|
18
|
+
expiresAt: number;
|
|
19
|
+
}
|
|
20
|
+
export interface ISessionRepository {
|
|
21
|
+
/** Stores a new refresh token record. Only the hash is persisted, never the plaintext. */
|
|
22
|
+
saveRefreshToken(record: NewRefreshToken): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* Finds an active refresh token by its hash.
|
|
25
|
+
* nowTimestamp is compared against expiresAt and revokedAt to guarantee the token
|
|
26
|
+
* is both unexpired and not revoked before returning it.
|
|
27
|
+
*/
|
|
28
|
+
findActiveByHash(tokenHash: string, nowTimestamp: number): Promise<RefreshTokenRecord | null>;
|
|
29
|
+
/**
|
|
30
|
+
* Marks a refresh token as revoked so it cannot be used again.
|
|
31
|
+
* Returns true if the token was found and revoked, false if it was already revoked or absent.
|
|
32
|
+
*/
|
|
33
|
+
revokeByHash(tokenHash: string, nowTimestamp: number): Promise<boolean>;
|
|
34
|
+
/**
|
|
35
|
+
* Revokes all active refresh tokens for a user.
|
|
36
|
+
* Must be called on password change to invalidate all existing sessions immediately.
|
|
37
|
+
*/
|
|
38
|
+
revokeAllForUser(userId: string, nowTimestamp: number): Promise<void>;
|
|
39
|
+
/** Returns a paginated list of active sessions for the user, ordered newest first. */
|
|
40
|
+
listActiveForUser(userId: string, nowTimestamp: number, limit: number): Promise<ActiveSessionSummary[]>;
|
|
41
|
+
/**
|
|
42
|
+
* Revokes a specific session by its database ID, scoped to the owning user
|
|
43
|
+
* to prevent one user from revoking another user's sessions.
|
|
44
|
+
*/
|
|
45
|
+
revokeById(sessionId: string, userId: string, nowTimestamp: number): Promise<boolean>;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=session.repository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.repository.d.ts","sourceRoot":"","sources":["../../src/auth/session.repository.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,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;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CACzB;AAED,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAA;IACV,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,0FAA0F;IAC1F,gBAAgB,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAExD;;;;OAIG;IACH,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAAA;IAE7F;;;OAGG;IACH,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAEvE;;;OAGG;IACH,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAErE,sFAAsF;IACtF,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAAA;IAEvG;;;OAGG;IACH,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;CACtF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface JwtClaims {
|
|
2
|
+
sub: string;
|
|
3
|
+
email?: string;
|
|
4
|
+
name?: string;
|
|
5
|
+
[key: string]: unknown;
|
|
6
|
+
}
|
|
7
|
+
export interface IssueTokenOptions {
|
|
8
|
+
/** Time to live in seconds. Defaults to 900 (15 minutes). */
|
|
9
|
+
ttlSeconds?: number;
|
|
10
|
+
}
|
|
11
|
+
export interface ITokenService {
|
|
12
|
+
/**
|
|
13
|
+
* Issues a signed JWT for the given claims. The token is short-lived by design;
|
|
14
|
+
* callers that need a longer-lived session should use a refresh token instead.
|
|
15
|
+
*/
|
|
16
|
+
issue(claims: JwtClaims, options?: IssueTokenOptions): Promise<string>;
|
|
17
|
+
/**
|
|
18
|
+
* Verifies a JWT and returns its decoded claims, or null on ANY failure
|
|
19
|
+
* (expired, tampered, wrong issuer/audience, malformed). Never throws.
|
|
20
|
+
*/
|
|
21
|
+
verify(token: string): Promise<JwtClaims | null>;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=token-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-service.d.ts","sourceRoot":"","sources":["../../src/auth/token-service.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC,6DAA6D;IAC7D,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,KAAK,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IAEtE;;;OAGG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAAA;CACjD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export interface UserRecord {
|
|
2
|
+
id: string;
|
|
3
|
+
email: string;
|
|
4
|
+
name: string | null;
|
|
5
|
+
passwordHash: string;
|
|
6
|
+
role: string;
|
|
7
|
+
avatarUrl: string | null;
|
|
8
|
+
notificationPreferences: string;
|
|
9
|
+
}
|
|
10
|
+
export interface NewUserInput {
|
|
11
|
+
id: string;
|
|
12
|
+
email: string;
|
|
13
|
+
passwordHash: string;
|
|
14
|
+
role: string;
|
|
15
|
+
name: string | null;
|
|
16
|
+
}
|
|
17
|
+
export interface IUserRepository {
|
|
18
|
+
/**
|
|
19
|
+
* Returns the total number of registered users.
|
|
20
|
+
* Used to block re-setup when at least one administrator account already exists.
|
|
21
|
+
*/
|
|
22
|
+
countAll(): Promise<number>;
|
|
23
|
+
/** Retrieves a user by their unique identifier, or null if not found. */
|
|
24
|
+
findById(userId: string): Promise<UserRecord | null>;
|
|
25
|
+
/** Retrieves a user by their email address, or null if not found. */
|
|
26
|
+
findByEmail(email: string): Promise<UserRecord | null>;
|
|
27
|
+
/** Inserts a new user record. */
|
|
28
|
+
create(user: NewUserInput): Promise<void>;
|
|
29
|
+
/** Updates the user's display name and/or email address. */
|
|
30
|
+
updateProfile(userId: string, fields: {
|
|
31
|
+
name?: string;
|
|
32
|
+
email?: string;
|
|
33
|
+
}): Promise<void>;
|
|
34
|
+
/** Replaces the user's stored password hash after a successful password change. */
|
|
35
|
+
updatePasswordHash(userId: string, newPasswordHash: string): Promise<void>;
|
|
36
|
+
/** Sets or clears the user's avatar URL. */
|
|
37
|
+
updateAvatarUrl(userId: string, avatarUrl: string | null): Promise<void>;
|
|
38
|
+
/** Persists the user's notification preferences as a JSON string. */
|
|
39
|
+
updateNotificationPreferences(userId: string, preferencesJson: string): Promise<void>;
|
|
40
|
+
/**
|
|
41
|
+
* Checks whether the given email is already registered to a different user.
|
|
42
|
+
* Used during an email change to detect conflicts without exposing whether
|
|
43
|
+
* an unrelated account exists.
|
|
44
|
+
*/
|
|
45
|
+
emailBelongsToAnotherUser(email: string, currentUserId: string): Promise<boolean>;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=user.repository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user.repository.d.ts","sourceRoot":"","sources":["../../src/auth/user.repository.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,YAAY,EAAE,MAAM,CAAA;IACpB,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,uBAAuB,EAAE,MAAM,CAAA;CAChC;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,YAAY,EAAE,MAAM,CAAA;IACpB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC,CAAA;IAE3B,yEAAyE;IACzE,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAA;IAEpD,qEAAqE;IACrE,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAA;IAEtD,iCAAiC;IACjC,MAAM,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAEzC,4DAA4D;IAC5D,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAEvF,mFAAmF;IACnF,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAE1E,4CAA4C;IAC5C,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAExE,qEAAqE;IACrE,6BAA6B,CAAC,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAErF;;;;OAIG;IACH,yBAAyB,CAAC,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;CAClF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { Seed, SelectOptions } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Base Repository Error
|
|
4
|
+
*/
|
|
5
|
+
export declare class RepositoryError extends Error {
|
|
6
|
+
cause?: unknown | undefined;
|
|
7
|
+
constructor(message: string, cause?: unknown | undefined);
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Thrown when an entry is not found by ID or Slug
|
|
11
|
+
*/
|
|
12
|
+
export declare class EntryNotFoundError extends RepositoryError {
|
|
13
|
+
constructor(message: string);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Thrown when a slug already exists for a content type
|
|
17
|
+
*/
|
|
18
|
+
export declare class SlugConflictError extends RepositoryError {
|
|
19
|
+
constructor(message: string);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Interface defining the standard operations for content persistence.
|
|
23
|
+
* This is platform-agnostic and should be implemented for specific databases (e.g., D1).
|
|
24
|
+
*/
|
|
25
|
+
export interface ContentRepository {
|
|
26
|
+
/**
|
|
27
|
+
* Retrieves a paginated and filtered list of entries.
|
|
28
|
+
*/
|
|
29
|
+
findMany(seed: Seed, options: SelectOptions): Promise<{
|
|
30
|
+
items: Record<string, any>[];
|
|
31
|
+
total: number;
|
|
32
|
+
}>;
|
|
33
|
+
/**
|
|
34
|
+
* Finds a single entry by its unique ID.
|
|
35
|
+
* Throws EntryNotFoundError if not found.
|
|
36
|
+
*/
|
|
37
|
+
findById(seed: Seed, id: string): Promise<Record<string, any>>;
|
|
38
|
+
/**
|
|
39
|
+
* Finds a single entry by its unique slug.
|
|
40
|
+
* Throws EntryNotFoundError if not found.
|
|
41
|
+
*/
|
|
42
|
+
findBySlug(seed: Seed, slug: string): Promise<Record<string, any>>;
|
|
43
|
+
/**
|
|
44
|
+
* Computes facets (status counts and distinct tags) for a content type.
|
|
45
|
+
*/
|
|
46
|
+
getFacets(seed: Seed): Promise<{
|
|
47
|
+
statuses: Record<string, number>;
|
|
48
|
+
tagsByColumn: Record<string, string[]>;
|
|
49
|
+
}>;
|
|
50
|
+
/**
|
|
51
|
+
* Creates a new content entry in the live table.
|
|
52
|
+
* Throws SlugConflictError if the slug is already taken.
|
|
53
|
+
*/
|
|
54
|
+
create(seed: Seed, id: string, slug: string, status: string, data: Record<string, any>): Promise<void>;
|
|
55
|
+
/**
|
|
56
|
+
* Partially updates an existing entry in the live table.
|
|
57
|
+
* Throws EntryNotFoundError if the ID does not exist.
|
|
58
|
+
*/
|
|
59
|
+
update(seed: Seed, id: string, data: Record<string, any>, status?: string): Promise<void>;
|
|
60
|
+
/**
|
|
61
|
+
* Deletes an entry from the live table.
|
|
62
|
+
* Returns the deleted row data (useful for media cleanup).
|
|
63
|
+
* Throws EntryNotFoundError if the ID does not exist.
|
|
64
|
+
*/
|
|
65
|
+
delete(seed: Seed, id: string): Promise<{
|
|
66
|
+
row: Record<string, any>;
|
|
67
|
+
}>;
|
|
68
|
+
/**
|
|
69
|
+
* Saves or updates a pending draft in the mirror table.
|
|
70
|
+
*/
|
|
71
|
+
saveDraft(seed: Seed, entryId: string, data: Record<string, any>): Promise<void>;
|
|
72
|
+
/**
|
|
73
|
+
* Retrieves the pending draft for a given entry.
|
|
74
|
+
* Returns null if no draft exists.
|
|
75
|
+
*/
|
|
76
|
+
getDraft(seed: Seed, entryId: string): Promise<Record<string, any> | null>;
|
|
77
|
+
/**
|
|
78
|
+
* Atomic promotion of a draft to the live table.
|
|
79
|
+
* Must use a transaction (db.batch) to update live and delete draft.
|
|
80
|
+
*/
|
|
81
|
+
publishDraft(seed: Seed, entryId: string): Promise<void>;
|
|
82
|
+
/**
|
|
83
|
+
* Discards the pending draft.
|
|
84
|
+
*/
|
|
85
|
+
deleteDraft(seed: Seed, entryId: string): Promise<void>;
|
|
86
|
+
/**
|
|
87
|
+
* Checks if a slug is already taken by another entry.
|
|
88
|
+
*/
|
|
89
|
+
existsSlug(seed: Seed, slug: string, excludeId?: string): Promise<boolean>;
|
|
90
|
+
/**
|
|
91
|
+
* Checks if an entry has a pending draft.
|
|
92
|
+
*/
|
|
93
|
+
hasDraft(seed: Seed, entryId: string): Promise<boolean>;
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=content.repository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content.repository.d.ts","sourceRoot":"","sources":["../src/content.repository.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base Repository Error
|
|
3
|
+
*/
|
|
4
|
+
export class RepositoryError extends Error {
|
|
5
|
+
cause;
|
|
6
|
+
constructor(message, cause) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.cause = cause;
|
|
9
|
+
this.name = 'RepositoryError';
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Thrown when an entry is not found by ID or Slug
|
|
14
|
+
*/
|
|
15
|
+
export class EntryNotFoundError extends RepositoryError {
|
|
16
|
+
constructor(message) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.name = 'EntryNotFoundError';
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Thrown when a slug already exists for a content type
|
|
23
|
+
*/
|
|
24
|
+
export class SlugConflictError extends RepositoryError {
|
|
25
|
+
constructor(message) {
|
|
26
|
+
super(message);
|
|
27
|
+
this.name = 'SlugConflictError';
|
|
28
|
+
}
|
|
29
|
+
}
|
package/dist/engine.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EACV,IAAI,EACJ,MAAM,EAKN,aAAa,EACb,kBAAkB,EACnB,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EACV,IAAI,EACJ,MAAM,EAKN,aAAa,EACb,kBAAkB,EACnB,MAAM,YAAY,CAAA;AAgFnB;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAsBtD;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,CAuB5D;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAGpE;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,EAAE,CAkBpD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,CAc1D;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,EAAE,CAgCxD;AAID;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,GAAE,aAAkB,GAAG,kBAAkB,CAqE5F;AA+GD,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;IACpC,OAAO,EAAE,OAAO,CAAA;IAChB,IAAI,EAAE,OAAO,CAAA;CACd;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,IAAI,GAAG,YAAY,EAAE,CAc7D;AAID;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAmCrF;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CA6BzE"}
|
package/dist/engine.js
CHANGED
|
@@ -6,6 +6,7 @@ const BRANCH_TYPE_SQL = {
|
|
|
6
6
|
json: { sqlType: 'TEXT' }, // JSON serializzato
|
|
7
7
|
richtext: { sqlType: 'TEXT' },
|
|
8
8
|
file: { sqlType: 'TEXT' }, // URL singolo o JSON array di URL
|
|
9
|
+
tags: { sqlType: 'TEXT' }, // JSON array di stringhe
|
|
9
10
|
};
|
|
10
11
|
const SYSTEM_COLUMNS = new Set(['id', 'slug', 'status', 'created_at', 'updated_at']);
|
|
11
12
|
// ---- Private helpers ----
|
|
@@ -277,6 +278,32 @@ export function buildSelectQuery(seed, options = {}) {
|
|
|
277
278
|
}
|
|
278
279
|
return { sql, bindings };
|
|
279
280
|
}
|
|
281
|
+
function normalizeFilterValue(type, value) {
|
|
282
|
+
if (value === null || value === undefined)
|
|
283
|
+
return null;
|
|
284
|
+
if (type === 'date') {
|
|
285
|
+
if (typeof value === 'number')
|
|
286
|
+
return value;
|
|
287
|
+
if (typeof value === 'string' && value.trim() !== '') {
|
|
288
|
+
const d = new Date(value);
|
|
289
|
+
return isNaN(d.getTime()) ? null : Math.floor(d.getTime() / 1000);
|
|
290
|
+
}
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
if (type === 'boolean') {
|
|
294
|
+
return value ? 1 : 0;
|
|
295
|
+
}
|
|
296
|
+
if (type === 'number') {
|
|
297
|
+
if (typeof value === 'number')
|
|
298
|
+
return value;
|
|
299
|
+
if (typeof value === 'string' && value.trim() !== '') {
|
|
300
|
+
const n = Number(value);
|
|
301
|
+
return isNaN(n) ? null : n;
|
|
302
|
+
}
|
|
303
|
+
return null;
|
|
304
|
+
}
|
|
305
|
+
return value;
|
|
306
|
+
}
|
|
280
307
|
function buildFilterCondition(col, type, cond, bindings) {
|
|
281
308
|
const { op, value } = cond;
|
|
282
309
|
if (op === 'is_empty') {
|
|
@@ -285,23 +312,58 @@ function buildFilterCondition(col, type, cond, bindings) {
|
|
|
285
312
|
if (op === 'is_not_empty') {
|
|
286
313
|
return type === 'text' ? `(${col} IS NOT NULL AND ${col} != '')` : `${col} IS NOT NULL`;
|
|
287
314
|
}
|
|
288
|
-
if (
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
315
|
+
if (op === 'in' || op === 'not_in') {
|
|
316
|
+
if (!Array.isArray(value) || value.length === 0)
|
|
317
|
+
return null;
|
|
318
|
+
const normalizedValues = value
|
|
319
|
+
.map((v) => normalizeFilterValue(type, v))
|
|
320
|
+
.filter((v) => v !== null);
|
|
321
|
+
if (normalizedValues.length === 0)
|
|
322
|
+
return null;
|
|
323
|
+
const placeholders = normalizedValues.map(() => '?').join(', ');
|
|
324
|
+
bindings.push(...normalizedValues);
|
|
325
|
+
return `${col} ${op === 'in' ? 'IN' : 'NOT IN'} (${placeholders})`;
|
|
293
326
|
}
|
|
294
|
-
if (op === '
|
|
295
|
-
if (type
|
|
296
|
-
|
|
297
|
-
|
|
327
|
+
if (op === 'has_tag' || op === 'has_any_tag' || op === 'has_all_tags') {
|
|
328
|
+
if (type !== 'tags' && type !== 'json')
|
|
329
|
+
return null;
|
|
330
|
+
const tags = (op === 'has_tag' ? [value] : Array.isArray(value) ? value : [value])
|
|
331
|
+
.map(t => String(t));
|
|
332
|
+
if (tags.length === 0)
|
|
333
|
+
return null;
|
|
334
|
+
if (op === 'has_tag' || op === 'has_any_tag') {
|
|
335
|
+
const placeholders = tags.map(() => '?').join(', ');
|
|
336
|
+
bindings.push(...tags);
|
|
337
|
+
return `EXISTS (SELECT 1 FROM json_each(${col}) WHERE value IN (${placeholders}))`;
|
|
298
338
|
}
|
|
299
|
-
|
|
300
|
-
|
|
339
|
+
// has_all_tags: each tag must exist
|
|
340
|
+
const clauses = tags.map(() => `EXISTS (SELECT 1 FROM json_each(${col}) WHERE value = ?)`);
|
|
341
|
+
bindings.push(...tags);
|
|
342
|
+
return `(${clauses.join(' AND ')})`;
|
|
343
|
+
}
|
|
344
|
+
const normalized = normalizeFilterValue(type, value);
|
|
345
|
+
if (normalized === null)
|
|
346
|
+
return null;
|
|
347
|
+
if (op === 'eq' || op === 'neq') {
|
|
348
|
+
const sqlOp = op === 'eq' ? '=' : '!=';
|
|
349
|
+
bindings.push(normalized);
|
|
350
|
+
return `${col} ${sqlOp} ?`;
|
|
351
|
+
}
|
|
352
|
+
if (op === 'contains' || op === 'not_contains' || op === 'starts_with' || op === 'ends_with') {
|
|
353
|
+
const sqlOp = op === 'not_contains' ? 'NOT LIKE' : 'LIKE';
|
|
354
|
+
let pattern = String(value);
|
|
355
|
+
if (op === 'contains' || op === 'not_contains')
|
|
356
|
+
pattern = `%${pattern}%`;
|
|
357
|
+
else if (op === 'starts_with')
|
|
358
|
+
pattern = `${pattern}%`;
|
|
359
|
+
else if (op === 'ends_with')
|
|
360
|
+
pattern = `%${pattern}`;
|
|
361
|
+
bindings.push(pattern);
|
|
362
|
+
return `${col} ${sqlOp} ?`;
|
|
301
363
|
}
|
|
302
364
|
const mathOps = { gt: '>', gte: '>=', lt: '<', lte: '<=' };
|
|
303
365
|
if (mathOps[op]) {
|
|
304
|
-
bindings.push(
|
|
366
|
+
bindings.push(normalized);
|
|
305
367
|
return `${col} ${mathOps[op]} ?`;
|
|
306
368
|
}
|
|
307
369
|
return null;
|
|
@@ -337,6 +399,7 @@ export function serializeForDb(branch, value) {
|
|
|
337
399
|
case 'boolean':
|
|
338
400
|
return value ? 1 : 0;
|
|
339
401
|
case 'json':
|
|
402
|
+
case 'tags':
|
|
340
403
|
case 'richtext':
|
|
341
404
|
return typeof value === 'string' ? value : JSON.stringify(value);
|
|
342
405
|
case 'date': {
|
|
@@ -374,6 +437,7 @@ export function deserializeFromDb(branch, value) {
|
|
|
374
437
|
case 'boolean':
|
|
375
438
|
return value === 1 || value === true;
|
|
376
439
|
case 'json':
|
|
440
|
+
case 'tags':
|
|
377
441
|
case 'richtext': {
|
|
378
442
|
if (typeof value === 'string') {
|
|
379
443
|
try {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface IdempotencyRecord {
|
|
2
|
+
key: string;
|
|
3
|
+
fingerprint: string;
|
|
4
|
+
responseStatus: number;
|
|
5
|
+
responseBody: string;
|
|
6
|
+
expiresAt: number;
|
|
7
|
+
}
|
|
8
|
+
export interface IdempotencyRepository {
|
|
9
|
+
lookup(key: string): Promise<IdempotencyRecord | null>;
|
|
10
|
+
store(record: IdempotencyRecord): Promise<void>;
|
|
11
|
+
cleanup(now: number): Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=idempotency.repository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"idempotency.repository.d.ts","sourceRoot":"","sources":["../src/idempotency.repository.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,CAAA;IACX,WAAW,EAAE,MAAM,CAAA;IACnB,cAAc,EAAE,MAAM,CAAA;IACtB,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAAA;IACtD,KAAK,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC/C,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACpC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.d.ts
CHANGED
|
@@ -15,5 +15,9 @@ export * from './validation.js';
|
|
|
15
15
|
export * from './richtext.js';
|
|
16
16
|
export * from './richtext-render.js';
|
|
17
17
|
export * from './slug-utils.js';
|
|
18
|
+
export * from './content.repository.js';
|
|
19
|
+
export * from './idempotency.repository.js';
|
|
20
|
+
export * from './media.repository.js';
|
|
21
|
+
export * from './storage.js';
|
|
18
22
|
export * from './policies.js';
|
|
19
23
|
//# 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,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"}
|
package/dist/index.js
CHANGED
|
@@ -15,4 +15,8 @@ export * from './validation.js';
|
|
|
15
15
|
export * from './richtext.js';
|
|
16
16
|
export * from './richtext-render.js';
|
|
17
17
|
export * from './slug-utils.js';
|
|
18
|
+
export * from './content.repository.js';
|
|
19
|
+
export * from './idempotency.repository.js';
|
|
20
|
+
export * from './media.repository.js';
|
|
21
|
+
export * from './storage.js';
|
|
18
22
|
export * from './policies.js';
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MediaRepository - Interface for tracking media assets in the database.
|
|
3
|
+
*/
|
|
4
|
+
export interface MediaObject {
|
|
5
|
+
key: string;
|
|
6
|
+
filename: string;
|
|
7
|
+
mime_type: string;
|
|
8
|
+
size_bytes: number;
|
|
9
|
+
uploaded_by: string;
|
|
10
|
+
created_at: number;
|
|
11
|
+
}
|
|
12
|
+
export interface MediaRepository {
|
|
13
|
+
/**
|
|
14
|
+
* Tracks a new file upload in the database.
|
|
15
|
+
*/
|
|
16
|
+
trackUpload(media: Omit<MediaObject, 'created_at'>): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Retrieves tracking info for a file by its key.
|
|
19
|
+
*/
|
|
20
|
+
getByKey(key: string): Promise<MediaObject | null>;
|
|
21
|
+
/**
|
|
22
|
+
* Removes tracking info for a file.
|
|
23
|
+
*/
|
|
24
|
+
untrack(key: string): Promise<void>;
|
|
25
|
+
/**
|
|
26
|
+
* Lists all tracked media with pagination.
|
|
27
|
+
*/
|
|
28
|
+
list(options: {
|
|
29
|
+
limit: number;
|
|
30
|
+
offset: number;
|
|
31
|
+
}): Promise<{
|
|
32
|
+
items: MediaObject[];
|
|
33
|
+
total: number;
|
|
34
|
+
}>;
|
|
35
|
+
/**
|
|
36
|
+
* Gets the total number of tracked media objects.
|
|
37
|
+
*/
|
|
38
|
+
count(): Promise<number>;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* SystemStatsRepository - Interface for managing global system metrics.
|
|
42
|
+
*/
|
|
43
|
+
export interface SystemStatsRepository {
|
|
44
|
+
/**
|
|
45
|
+
* Increments the total storage counter.
|
|
46
|
+
*/
|
|
47
|
+
incrementStorage(bytes: number): Promise<void>;
|
|
48
|
+
/**
|
|
49
|
+
* Decrements the total storage counter.
|
|
50
|
+
*/
|
|
51
|
+
decrementStorage(bytes: number): Promise<void>;
|
|
52
|
+
/**
|
|
53
|
+
* Sets a specific value for a storage stat (e.g. after a full sync).
|
|
54
|
+
*/
|
|
55
|
+
setStorage(bytes: number): Promise<void>;
|
|
56
|
+
/**
|
|
57
|
+
* Gets the current total storage usage in bytes.
|
|
58
|
+
*/
|
|
59
|
+
getStorageUsage(): Promise<number>;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=media.repository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media.repository.d.ts","sourceRoot":"","sources":["../src/media.repository.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B;;OAEG;IACH,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC,WAAW,EAAE,YAAY,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEnE;;OAEG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAEnD;;OAEG;IACH,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEpC;;OAEG;IACH,IAAI,CAAC,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,WAAW,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAEnG;;OAEG;IACH,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC;;OAEG;IACH,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE/C;;OAEG;IACH,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE/C;;OAEG;IACH,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEzC;;OAEG;IACH,eAAe,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CACpC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface RateLimitResult {
|
|
2
|
+
isAllowed: boolean;
|
|
3
|
+
retryAfterSeconds?: number;
|
|
4
|
+
}
|
|
5
|
+
export interface IRateLimiter {
|
|
6
|
+
/**
|
|
7
|
+
* Checks whether the given key is within the rate limit.
|
|
8
|
+
* The key should combine the client IP address and an endpoint-specific prefix
|
|
9
|
+
* to prevent one endpoint's limit from being shared with another.
|
|
10
|
+
*/
|
|
11
|
+
checkLimit(key: string): Promise<RateLimitResult>;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=rate-limiter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../../src/rate-limit/rate-limiter.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,OAAO,CAAA;IAClB,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAC3B;AAED,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAAA;CAClD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BeechBucket - S3-like interface for object storage in BeechCMS.
|
|
3
|
+
*
|
|
4
|
+
* This interface abstracts the underlying storage provider (R2, S3, or Local)
|
|
5
|
+
* allowing the application to scale without being tied to a specific vendor.
|
|
6
|
+
*/
|
|
7
|
+
export interface PutBucketOptions {
|
|
8
|
+
contentType?: string;
|
|
9
|
+
metadata?: Record<string, string>;
|
|
10
|
+
}
|
|
11
|
+
export interface GetBucketResult {
|
|
12
|
+
body: ReadableStream | ArrayBuffer;
|
|
13
|
+
contentType?: string;
|
|
14
|
+
size: number;
|
|
15
|
+
metadata?: Record<string, string>;
|
|
16
|
+
}
|
|
17
|
+
export interface BeechBucket {
|
|
18
|
+
/**
|
|
19
|
+
* Uploads an object to the bucket.
|
|
20
|
+
* @param key The unique key (path) for the object.
|
|
21
|
+
* @param body The data to store.
|
|
22
|
+
* @param options Optional metadata like content type.
|
|
23
|
+
*/
|
|
24
|
+
put(key: string, body: ArrayBuffer | Uint8Array | ReadableStream, options?: PutBucketOptions): Promise<void>;
|
|
25
|
+
/**
|
|
26
|
+
* Retrieves an object from the bucket.
|
|
27
|
+
* @param key The unique key for the object.
|
|
28
|
+
* @returns The object data and metadata, or null if not found.
|
|
29
|
+
*/
|
|
30
|
+
get(key: string): Promise<GetBucketResult | null>;
|
|
31
|
+
/**
|
|
32
|
+
* Deletes an object from the bucket.
|
|
33
|
+
* @param key The unique key for the object.
|
|
34
|
+
*/
|
|
35
|
+
delete(key: string): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Retrieves metadata for an object without downloading its content.
|
|
38
|
+
* @param key The unique key for the object.
|
|
39
|
+
*/
|
|
40
|
+
head(key: string): Promise<{
|
|
41
|
+
size: number;
|
|
42
|
+
contentType?: string;
|
|
43
|
+
metadata?: Record<string, string>;
|
|
44
|
+
} | null>;
|
|
45
|
+
/**
|
|
46
|
+
* Returns a publicly accessible URL for the given key.
|
|
47
|
+
* The implementation decides if this is a direct vendor URL (CDN)
|
|
48
|
+
* or a proxied URL through the Beech API.
|
|
49
|
+
* @param key The unique key for the object.
|
|
50
|
+
*/
|
|
51
|
+
getUrl(key: string): string;
|
|
52
|
+
/**
|
|
53
|
+
* Calculates the total size of all objects in the bucket.
|
|
54
|
+
* Warning: This may be an expensive operation on some providers.
|
|
55
|
+
*/
|
|
56
|
+
getTotalSize(): Promise<number>;
|
|
57
|
+
/**
|
|
58
|
+
* Lists objects in the bucket.
|
|
59
|
+
*/
|
|
60
|
+
list(options?: {
|
|
61
|
+
prefix?: string;
|
|
62
|
+
limit?: number;
|
|
63
|
+
cursor?: string;
|
|
64
|
+
}): Promise<{
|
|
65
|
+
objects: Array<{
|
|
66
|
+
key: string;
|
|
67
|
+
size: number;
|
|
68
|
+
}>;
|
|
69
|
+
cursor?: string;
|
|
70
|
+
}>;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=storage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,cAAc,GAAG,WAAW,CAAC;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,WAAW;IAC1B;;;;;OAKG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,UAAU,GAAG,cAAc,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE7G;;;;OAIG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC;IAElD;;;OAGG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEnC;;;OAGG;IACH,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;IAE7G;;;;;OAKG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;IAE5B;;;OAGG;IACH,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAEhC;;OAEG;IACH,IAAI,CAAC,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,KAAK,CAAC;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACnJ"}
|
package/dist/storage.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type BranchType = 'text' | 'number' | 'boolean' | 'json' | 'date' | 'richtext' | 'file';
|
|
1
|
+
export type BranchType = 'text' | 'number' | 'boolean' | 'json' | 'date' | 'richtext' | 'file' | 'tags';
|
|
2
2
|
/** Branch: definizione di una proprietà. alias = nome colonna SQL. */
|
|
3
3
|
export interface Branch {
|
|
4
4
|
/** Alias human-readable, usato nel payload API e come nome colonna SQL nella tabella dedicata */
|
|
@@ -96,8 +96,8 @@ export interface Seed {
|
|
|
96
96
|
/** Optional dashboard-specific UI config. Ignored by the Botanical Engine. */
|
|
97
97
|
dashboard?: DashboardSeedConfig;
|
|
98
98
|
}
|
|
99
|
-
export type FilterOperator = 'eq' | 'gt' | 'gte' | 'lt' | 'lte' | 'contains' | 'is_empty' | 'is_not_empty';
|
|
100
|
-
export type FilterType = 'text' | 'number' | 'date' | 'boolean' | 'tags' | 'select' | 'system';
|
|
99
|
+
export type FilterOperator = 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'contains' | 'not_contains' | 'starts_with' | 'ends_with' | 'is_empty' | 'is_not_empty' | 'in' | 'not_in' | 'has_tag' | 'has_any_tag' | 'has_all_tags';
|
|
100
|
+
export type FilterType = 'text' | 'number' | 'date' | 'boolean' | 'tags' | 'select' | 'system' | 'json';
|
|
101
101
|
export interface FilterCondition {
|
|
102
102
|
op: FilterOperator;
|
|
103
103
|
value: string | number | boolean | null;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,GAAG,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/validation.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAU,IAAI,EAAE,MAAM,YAAY,CAAA;AAG9C,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,0BAA0B;IACzC,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,SAAS,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAA;IAC/B,2BAA2B,CAAC,EAAE,OAAO,CAAA;IACrC,qBAAqB,CAAC,EAAE,OAAO,CAAA;IAC/B,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC7B,OAAO,EAAE,gBAAgB,EAAE,CAAA;IAC3B,cAAc,EAAE,MAAM,EAAE,CAAA;IACxB,eAAe,EAAE,MAAM,EAAE,CAAA;IACzB,qBAAqB,EAAE,MAAM,EAAE,CAAA;IAC/B,gBAAgB,EAAE,OAAO,CAAA;CAC1B;
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAU,IAAI,EAAE,MAAM,YAAY,CAAA;AAG9C,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,0BAA0B;IACzC,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,SAAS,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAA;IAC/B,2BAA2B,CAAC,EAAE,OAAO,CAAA;IACrC,qBAAqB,CAAC,EAAE,OAAO,CAAA;IAC/B,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC7B,OAAO,EAAE,gBAAgB,EAAE,CAAA;IAC3B,cAAc,EAAE,MAAM,EAAE,CAAA;IACxB,eAAe,EAAE,MAAM,EAAE,CAAA;IACzB,qBAAqB,EAAE,MAAM,EAAE,CAAA;IAC/B,gBAAgB,EAAE,OAAO,CAAA;CAC1B;AA6fD;;;;;;;;;GASG;AACH,wBAAgB,8BAA8B,CAC5C,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,OAAO,GAAE,0BAA+B,GACvC,yBAAyB,CA6G3B;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,OAAO,GAAG,QAAQ,GAAG,WAAW,CAE9F;AAED;;;;;GAKG"}
|
package/dist/validation.js
CHANGED
|
@@ -278,7 +278,8 @@ function buildBranchSchema(branch, options) {
|
|
|
278
278
|
.refine((value) => isIsoDateString(value), { message: 'Expected date(ISO)' });
|
|
279
279
|
return nullable ? z.union([schema, nullable]) : schema;
|
|
280
280
|
}
|
|
281
|
-
case 'json':
|
|
281
|
+
case 'json':
|
|
282
|
+
case 'tags': {
|
|
282
283
|
const schema = jsonObjectOrArraySchema;
|
|
283
284
|
return nullable ? z.union([schema, nullable]) : schema;
|
|
284
285
|
}
|
|
@@ -316,6 +317,8 @@ function buildBranchSchema(branch, options) {
|
|
|
316
317
|
.pipe(z.url());
|
|
317
318
|
return nullable ? z.union([schema, nullable]) : schema;
|
|
318
319
|
}
|
|
320
|
+
default:
|
|
321
|
+
throw new Error(`Unhandled branch type: ${branch.type}`);
|
|
319
322
|
}
|
|
320
323
|
}
|
|
321
324
|
const compiledSchemaCache = new Map();
|
|
@@ -422,7 +425,8 @@ function validateBranchValue(branch, alias, rawValue, options) {
|
|
|
422
425
|
}
|
|
423
426
|
return { ok: true, value };
|
|
424
427
|
}
|
|
425
|
-
case 'json':
|
|
428
|
+
case 'json':
|
|
429
|
+
case 'tags': {
|
|
426
430
|
const isValidJsonLike = (typeof rawValue === 'object' && rawValue !== null) || Array.isArray(rawValue);
|
|
427
431
|
if (!isValidJsonLike || typeof rawValue === 'string') {
|
|
428
432
|
return { ok: false, detail: makeDetail(alias, 'object|array', rawValue) };
|
|
@@ -443,6 +447,8 @@ function validateBranchValue(branch, alias, rawValue, options) {
|
|
|
443
447
|
}
|
|
444
448
|
return { ok: true, value: singleUrl };
|
|
445
449
|
}
|
|
450
|
+
default:
|
|
451
|
+
throw new Error(`Unhandled branch type: ${branch.type}`);
|
|
446
452
|
}
|
|
447
453
|
}
|
|
448
454
|
/**
|