@bernierllc/content-storage-adapter 1.0.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.
Files changed (54) hide show
  1. package/.eslintrc.json +22 -0
  2. package/README.md +350 -0
  3. package/__tests__/ContentBatchOperations.test.ts +130 -0
  4. package/__tests__/ContentStorageAdapter.test.ts +604 -0
  5. package/__tests__/mocks/MockDatabaseAdapter.test.ts +97 -0
  6. package/__tests__/mocks/MockDatabaseAdapter.ts +85 -0
  7. package/dist/ContentBatchOperations.d.ts +22 -0
  8. package/dist/ContentBatchOperations.d.ts.map +1 -0
  9. package/dist/ContentBatchOperations.js +60 -0
  10. package/dist/ContentBatchOperations.js.map +1 -0
  11. package/dist/ContentStorageAdapter.d.ts +75 -0
  12. package/dist/ContentStorageAdapter.d.ts.map +1 -0
  13. package/dist/ContentStorageAdapter.js +382 -0
  14. package/dist/ContentStorageAdapter.js.map +1 -0
  15. package/dist/errors/ContentNotFoundError.d.ts +7 -0
  16. package/dist/errors/ContentNotFoundError.d.ts.map +1 -0
  17. package/dist/errors/ContentNotFoundError.js +21 -0
  18. package/dist/errors/ContentNotFoundError.js.map +1 -0
  19. package/dist/errors/DuplicateSlugError.d.ts +7 -0
  20. package/dist/errors/DuplicateSlugError.d.ts.map +1 -0
  21. package/dist/errors/DuplicateSlugError.js +21 -0
  22. package/dist/errors/DuplicateSlugError.js.map +1 -0
  23. package/dist/errors/OptimisticLockError.d.ts +7 -0
  24. package/dist/errors/OptimisticLockError.d.ts.map +1 -0
  25. package/dist/errors/OptimisticLockError.js +21 -0
  26. package/dist/errors/OptimisticLockError.js.map +1 -0
  27. package/dist/index.d.ts +10 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +27 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/models/IContent.d.ts +92 -0
  32. package/dist/models/IContent.d.ts.map +1 -0
  33. package/dist/models/IContent.js +42 -0
  34. package/dist/models/IContent.js.map +1 -0
  35. package/dist/models/IContentFilters.d.ts +29 -0
  36. package/dist/models/IContentFilters.d.ts.map +1 -0
  37. package/dist/models/IContentFilters.js +10 -0
  38. package/dist/models/IContentFilters.js.map +1 -0
  39. package/dist/models/IContentVersion.d.ts +15 -0
  40. package/dist/models/IContentVersion.d.ts.map +1 -0
  41. package/dist/models/IContentVersion.js +10 -0
  42. package/dist/models/IContentVersion.js.map +1 -0
  43. package/jest.config.cjs +30 -0
  44. package/package.json +38 -0
  45. package/src/ContentBatchOperations.ts +72 -0
  46. package/src/ContentStorageAdapter.ts +476 -0
  47. package/src/errors/ContentNotFoundError.ts +17 -0
  48. package/src/errors/DuplicateSlugError.ts +17 -0
  49. package/src/errors/OptimisticLockError.ts +17 -0
  50. package/src/index.ts +33 -0
  51. package/src/models/IContent.ts +122 -0
  52. package/src/models/IContentFilters.ts +38 -0
  53. package/src/models/IContentVersion.ts +22 -0
  54. package/tsconfig.json +26 -0
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ /*
3
+ Copyright (c) 2025 Bernier LLC
4
+
5
+ This file is licensed to the client under a limited-use license.
6
+ The client may use and modify this code *only within the scope of the project it was delivered for*.
7
+ Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.ContentNotFoundError = void 0;
11
+ /**
12
+ * Error thrown when content is not found
13
+ */
14
+ class ContentNotFoundError extends Error {
15
+ constructor(id) {
16
+ super(`Content not found or already deleted: ${id}`);
17
+ this.name = 'ContentNotFoundError';
18
+ }
19
+ }
20
+ exports.ContentNotFoundError = ContentNotFoundError;
21
+ //# sourceMappingURL=ContentNotFoundError.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ContentNotFoundError.js","sourceRoot":"","sources":["../../src/errors/ContentNotFoundError.ts"],"names":[],"mappings":";AAAA;;;;;;EAME;;;AAEF;;GAEG;AACH,MAAa,oBAAqB,SAAQ,KAAK;IAC7C,YAAY,EAAU;QACpB,KAAK,CAAC,yCAAyC,EAAE,EAAE,CAAC,CAAC;QACrD,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC;IACrC,CAAC;CACF;AALD,oDAKC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Error thrown when slug already exists
3
+ */
4
+ export declare class DuplicateSlugError extends Error {
5
+ constructor(slug: string);
6
+ }
7
+ //# sourceMappingURL=DuplicateSlugError.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DuplicateSlugError.d.ts","sourceRoot":"","sources":["../../src/errors/DuplicateSlugError.ts"],"names":[],"mappings":"AAQA;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,IAAI,EAAE,MAAM;CAIzB"}
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ /*
3
+ Copyright (c) 2025 Bernier LLC
4
+
5
+ This file is licensed to the client under a limited-use license.
6
+ The client may use and modify this code *only within the scope of the project it was delivered for*.
7
+ Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.DuplicateSlugError = void 0;
11
+ /**
12
+ * Error thrown when slug already exists
13
+ */
14
+ class DuplicateSlugError extends Error {
15
+ constructor(slug) {
16
+ super(`Slug already exists: ${slug}`);
17
+ this.name = 'DuplicateSlugError';
18
+ }
19
+ }
20
+ exports.DuplicateSlugError = DuplicateSlugError;
21
+ //# sourceMappingURL=DuplicateSlugError.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DuplicateSlugError.js","sourceRoot":"","sources":["../../src/errors/DuplicateSlugError.ts"],"names":[],"mappings":";AAAA;;;;;;EAME;;;AAEF;;GAEG;AACH,MAAa,kBAAmB,SAAQ,KAAK;IAC3C,YAAY,IAAY;QACtB,KAAK,CAAC,wBAAwB,IAAI,EAAE,CAAC,CAAC;QACtC,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;IACnC,CAAC;CACF;AALD,gDAKC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Error thrown when optimistic locking fails
3
+ */
4
+ export declare class OptimisticLockError extends Error {
5
+ constructor(id: string);
6
+ }
7
+ //# sourceMappingURL=OptimisticLockError.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OptimisticLockError.d.ts","sourceRoot":"","sources":["../../src/errors/OptimisticLockError.ts"],"names":[],"mappings":"AAQA;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;gBAChC,EAAE,EAAE,MAAM;CAIvB"}
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ /*
3
+ Copyright (c) 2025 Bernier LLC
4
+
5
+ This file is licensed to the client under a limited-use license.
6
+ The client may use and modify this code *only within the scope of the project it was delivered for*.
7
+ Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.OptimisticLockError = void 0;
11
+ /**
12
+ * Error thrown when optimistic locking fails
13
+ */
14
+ class OptimisticLockError extends Error {
15
+ constructor(id) {
16
+ super(`Optimistic lock failed for content ${id}: Content was modified by another user`);
17
+ this.name = 'OptimisticLockError';
18
+ }
19
+ }
20
+ exports.OptimisticLockError = OptimisticLockError;
21
+ //# sourceMappingURL=OptimisticLockError.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OptimisticLockError.js","sourceRoot":"","sources":["../../src/errors/OptimisticLockError.ts"],"names":[],"mappings":";AAAA;;;;;;EAME;;;AAEF;;GAEG;AACH,MAAa,mBAAoB,SAAQ,KAAK;IAC5C,YAAY,EAAU;QACpB,KAAK,CAAC,sCAAsC,EAAE,wCAAwC,CAAC,CAAC;QACxF,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;IACpC,CAAC;CACF;AALD,kDAKC"}
@@ -0,0 +1,10 @@
1
+ export { ContentStorageAdapter } from './ContentStorageAdapter';
2
+ export { ContentBatchOperations } from './ContentBatchOperations';
3
+ export type { IContent, IContentCreateData, IContentUpdateData } from './models/IContent';
4
+ export { ContentType, ContentFormat, ContentStatus } from './models/IContent';
5
+ export type { IContentVersion } from './models/IContentVersion';
6
+ export type { IContentFilters, IPaginatedResult } from './models/IContentFilters';
7
+ export { ContentNotFoundError } from './errors/ContentNotFoundError';
8
+ export { OptimisticLockError } from './errors/OptimisticLockError';
9
+ export { DuplicateSlugError } from './errors/DuplicateSlugError';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAGlE,YAAY,EACV,QAAQ,EACR,kBAAkB,EAClB,kBAAkB,EACnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,WAAW,EACX,aAAa,EACb,aAAa,EACd,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAChE,YAAY,EACV,eAAe,EACf,gBAAgB,EACjB,MAAM,0BAA0B,CAAC;AAGlC,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ /*
3
+ Copyright (c) 2025 Bernier LLC
4
+
5
+ This file is licensed to the client under a limited-use license.
6
+ The client may use and modify this code *only within the scope of the project it was delivered for*.
7
+ Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.DuplicateSlugError = exports.OptimisticLockError = exports.ContentNotFoundError = exports.ContentStatus = exports.ContentFormat = exports.ContentType = exports.ContentBatchOperations = exports.ContentStorageAdapter = void 0;
11
+ // Main classes
12
+ var ContentStorageAdapter_1 = require("./ContentStorageAdapter");
13
+ Object.defineProperty(exports, "ContentStorageAdapter", { enumerable: true, get: function () { return ContentStorageAdapter_1.ContentStorageAdapter; } });
14
+ var ContentBatchOperations_1 = require("./ContentBatchOperations");
15
+ Object.defineProperty(exports, "ContentBatchOperations", { enumerable: true, get: function () { return ContentBatchOperations_1.ContentBatchOperations; } });
16
+ var IContent_1 = require("./models/IContent");
17
+ Object.defineProperty(exports, "ContentType", { enumerable: true, get: function () { return IContent_1.ContentType; } });
18
+ Object.defineProperty(exports, "ContentFormat", { enumerable: true, get: function () { return IContent_1.ContentFormat; } });
19
+ Object.defineProperty(exports, "ContentStatus", { enumerable: true, get: function () { return IContent_1.ContentStatus; } });
20
+ // Errors
21
+ var ContentNotFoundError_1 = require("./errors/ContentNotFoundError");
22
+ Object.defineProperty(exports, "ContentNotFoundError", { enumerable: true, get: function () { return ContentNotFoundError_1.ContentNotFoundError; } });
23
+ var OptimisticLockError_1 = require("./errors/OptimisticLockError");
24
+ Object.defineProperty(exports, "OptimisticLockError", { enumerable: true, get: function () { return OptimisticLockError_1.OptimisticLockError; } });
25
+ var DuplicateSlugError_1 = require("./errors/DuplicateSlugError");
26
+ Object.defineProperty(exports, "DuplicateSlugError", { enumerable: true, get: function () { return DuplicateSlugError_1.DuplicateSlugError; } });
27
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;EAME;;;AAEF,eAAe;AACf,iEAAgE;AAAvD,8HAAA,qBAAqB,OAAA;AAC9B,mEAAkE;AAAzD,gIAAA,sBAAsB,OAAA;AAQ/B,8CAI2B;AAHzB,uGAAA,WAAW,OAAA;AACX,yGAAA,aAAa,OAAA;AACb,yGAAA,aAAa,OAAA;AAQf,SAAS;AACT,sEAAqE;AAA5D,4HAAA,oBAAoB,OAAA;AAC7B,oEAAmE;AAA1D,0HAAA,mBAAmB,OAAA;AAC5B,kEAAiE;AAAxD,wHAAA,kBAAkB,OAAA"}
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Content type enum
3
+ */
4
+ export declare enum ContentType {
5
+ BLOG = "blog",
6
+ ARTICLE = "article",
7
+ PAGE = "page",
8
+ SOCIAL = "social"
9
+ }
10
+ /**
11
+ * Content format enum
12
+ */
13
+ export declare enum ContentFormat {
14
+ MARKDOWN = "markdown",
15
+ HTML = "html",
16
+ PLAINTEXT = "plaintext"
17
+ }
18
+ /**
19
+ * Content status enum
20
+ */
21
+ export declare enum ContentStatus {
22
+ DRAFT = "draft",
23
+ IN_REVIEW = "in_review",
24
+ APPROVED = "approved",
25
+ SCHEDULED = "scheduled",
26
+ PUBLISHED = "published",
27
+ ARCHIVED = "archived"
28
+ }
29
+ /**
30
+ * Core content model
31
+ */
32
+ export interface IContent {
33
+ id: string;
34
+ userId: string;
35
+ title: string;
36
+ slug: string;
37
+ body: string;
38
+ excerpt?: string;
39
+ type: ContentType;
40
+ format: ContentFormat;
41
+ status: ContentStatus;
42
+ workflowStage?: string;
43
+ assignedTo?: string;
44
+ aiReviewed: boolean;
45
+ aiReviewScore?: number;
46
+ aiProvider?: string;
47
+ publishedAt?: Date;
48
+ scheduledFor?: Date;
49
+ deletedAt?: Date;
50
+ metaTitle?: string;
51
+ metaDescription?: string;
52
+ keywords?: string[];
53
+ viewCount: number;
54
+ shareCount: number;
55
+ generatedFromCommit: boolean;
56
+ sourceCommitSha?: string;
57
+ sourceRepository?: string;
58
+ createdAt: Date;
59
+ updatedAt: Date;
60
+ }
61
+ /**
62
+ * Content creation data
63
+ */
64
+ export interface IContentCreateData {
65
+ userId: string;
66
+ title: string;
67
+ body: string;
68
+ excerpt?: string;
69
+ type?: ContentType;
70
+ format?: ContentFormat;
71
+ status?: ContentStatus;
72
+ metaTitle?: string;
73
+ metaDescription?: string;
74
+ keywords?: string[];
75
+ }
76
+ /**
77
+ * Content update data
78
+ */
79
+ export interface IContentUpdateData {
80
+ title?: string;
81
+ body?: string;
82
+ excerpt?: string;
83
+ status?: ContentStatus;
84
+ workflowStage?: string;
85
+ assignedTo?: string;
86
+ publishedAt?: Date;
87
+ scheduledFor?: Date;
88
+ metaTitle?: string;
89
+ metaDescription?: string;
90
+ keywords?: string[];
91
+ }
92
+ //# sourceMappingURL=IContent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IContent.d.ts","sourceRoot":"","sources":["../../src/models/IContent.ts"],"names":[],"mappings":"AAQA;;GAEG;AACH,oBAAY,WAAW;IACrB,IAAI,SAAS;IACb,OAAO,YAAY;IACnB,IAAI,SAAS;IACb,MAAM,WAAW;CAClB;AAED;;GAEG;AACH,oBAAY,aAAa;IACvB,QAAQ,aAAa;IACrB,IAAI,SAAS;IACb,SAAS,cAAc;CACxB;AAED;;GAEG;AACH,oBAAY,aAAa;IACvB,KAAK,UAAU;IACf,SAAS,cAAc;IACvB,QAAQ,aAAa;IACrB,SAAS,cAAc;IACvB,SAAS,cAAc;IACvB,QAAQ,aAAa;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IAGf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IAGjB,IAAI,EAAE,WAAW,CAAC;IAClB,MAAM,EAAE,aAAa,CAAC;IACtB,MAAM,EAAE,aAAa,CAAC;IAGtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IAGpB,UAAU,EAAE,OAAO,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IAGpB,WAAW,CAAC,EAAE,IAAI,CAAC;IACnB,YAAY,CAAC,EAAE,IAAI,CAAC;IACpB,SAAS,CAAC,EAAE,IAAI,CAAC;IAGjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IAGpB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IAGnB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAG1B,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,IAAI,CAAC;IACnB,YAAY,CAAC,EAAE,IAAI,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB"}
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ /*
3
+ Copyright (c) 2025 Bernier LLC
4
+
5
+ This file is licensed to the client under a limited-use license.
6
+ The client may use and modify this code *only within the scope of the project it was delivered for*.
7
+ Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.ContentStatus = exports.ContentFormat = exports.ContentType = void 0;
11
+ /**
12
+ * Content type enum
13
+ */
14
+ var ContentType;
15
+ (function (ContentType) {
16
+ ContentType["BLOG"] = "blog";
17
+ ContentType["ARTICLE"] = "article";
18
+ ContentType["PAGE"] = "page";
19
+ ContentType["SOCIAL"] = "social";
20
+ })(ContentType || (exports.ContentType = ContentType = {}));
21
+ /**
22
+ * Content format enum
23
+ */
24
+ var ContentFormat;
25
+ (function (ContentFormat) {
26
+ ContentFormat["MARKDOWN"] = "markdown";
27
+ ContentFormat["HTML"] = "html";
28
+ ContentFormat["PLAINTEXT"] = "plaintext";
29
+ })(ContentFormat || (exports.ContentFormat = ContentFormat = {}));
30
+ /**
31
+ * Content status enum
32
+ */
33
+ var ContentStatus;
34
+ (function (ContentStatus) {
35
+ ContentStatus["DRAFT"] = "draft";
36
+ ContentStatus["IN_REVIEW"] = "in_review";
37
+ ContentStatus["APPROVED"] = "approved";
38
+ ContentStatus["SCHEDULED"] = "scheduled";
39
+ ContentStatus["PUBLISHED"] = "published";
40
+ ContentStatus["ARCHIVED"] = "archived";
41
+ })(ContentStatus || (exports.ContentStatus = ContentStatus = {}));
42
+ //# sourceMappingURL=IContent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IContent.js","sourceRoot":"","sources":["../../src/models/IContent.ts"],"names":[],"mappings":";AAAA;;;;;;EAME;;;AAEF;;GAEG;AACH,IAAY,WAKX;AALD,WAAY,WAAW;IACrB,4BAAa,CAAA;IACb,kCAAmB,CAAA;IACnB,4BAAa,CAAA;IACb,gCAAiB,CAAA;AACnB,CAAC,EALW,WAAW,2BAAX,WAAW,QAKtB;AAED;;GAEG;AACH,IAAY,aAIX;AAJD,WAAY,aAAa;IACvB,sCAAqB,CAAA;IACrB,8BAAa,CAAA;IACb,wCAAuB,CAAA;AACzB,CAAC,EAJW,aAAa,6BAAb,aAAa,QAIxB;AAED;;GAEG;AACH,IAAY,aAOX;AAPD,WAAY,aAAa;IACvB,gCAAe,CAAA;IACf,wCAAuB,CAAA;IACvB,sCAAqB,CAAA;IACrB,wCAAuB,CAAA;IACvB,wCAAuB,CAAA;IACvB,sCAAqB,CAAA;AACvB,CAAC,EAPW,aAAa,6BAAb,aAAa,QAOxB"}
@@ -0,0 +1,29 @@
1
+ import { ContentStatus, ContentType } from './IContent';
2
+ /**
3
+ * Content query filters
4
+ */
5
+ export interface IContentFilters {
6
+ userId?: string;
7
+ status?: ContentStatus | ContentStatus[];
8
+ type?: ContentType | ContentType[];
9
+ workflowStage?: string;
10
+ publishedAfter?: Date;
11
+ publishedBefore?: Date;
12
+ searchQuery?: string;
13
+ limit?: number;
14
+ offset?: number;
15
+ orderBy?: 'createdAt' | 'updatedAt' | 'publishedAt' | 'title';
16
+ orderDirection?: 'ASC' | 'DESC';
17
+ includeDeleted?: boolean;
18
+ }
19
+ /**
20
+ * Paginated results
21
+ */
22
+ export interface IPaginatedResult<T> {
23
+ data: T[];
24
+ total: number;
25
+ limit: number;
26
+ offset: number;
27
+ hasMore: boolean;
28
+ }
29
+ //# sourceMappingURL=IContentFilters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IContentFilters.d.ts","sourceRoot":"","sources":["../../src/models/IContentFilters.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAExD;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,aAAa,GAAG,aAAa,EAAE,CAAC;IACzC,IAAI,CAAC,EAAE,WAAW,GAAG,WAAW,EAAE,CAAC;IACnC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,IAAI,CAAC;IACtB,eAAe,CAAC,EAAE,IAAI,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,WAAW,GAAG,WAAW,GAAG,aAAa,GAAG,OAAO,CAAC;IAC9D,cAAc,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IAChC,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB,CAAC,CAAC;IACjC,IAAI,EAAE,CAAC,EAAE,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;CAClB"}
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ /*
3
+ Copyright (c) 2025 Bernier LLC
4
+
5
+ This file is licensed to the client under a limited-use license.
6
+ The client may use and modify this code *only within the scope of the project it was delivered for*.
7
+ Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ //# sourceMappingURL=IContentFilters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IContentFilters.js","sourceRoot":"","sources":["../../src/models/IContentFilters.ts"],"names":[],"mappings":";AAAA;;;;;;EAME"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Content version model
3
+ */
4
+ export interface IContentVersion {
5
+ id: string;
6
+ contentId: string;
7
+ versionNumber: number;
8
+ title: string;
9
+ body: string;
10
+ changeSummary?: string;
11
+ isAutosave: boolean;
12
+ createdAt: Date;
13
+ createdBy: string;
14
+ }
15
+ //# sourceMappingURL=IContentVersion.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IContentVersion.d.ts","sourceRoot":"","sources":["../../src/models/IContentVersion.ts"],"names":[],"mappings":"AAQA;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB"}
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ /*
3
+ Copyright (c) 2025 Bernier LLC
4
+
5
+ This file is licensed to the client under a limited-use license.
6
+ The client may use and modify this code *only within the scope of the project it was delivered for*.
7
+ Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ //# sourceMappingURL=IContentVersion.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IContentVersion.js","sourceRoot":"","sources":["../../src/models/IContentVersion.ts"],"names":[],"mappings":";AAAA;;;;;;EAME"}
@@ -0,0 +1,30 @@
1
+ /*
2
+ Copyright (c) 2025 Bernier LLC
3
+
4
+ This file is licensed to the client under a limited-use license.
5
+ The client may use and modify this code *only within the scope of the project it was delivered for*.
6
+ Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
7
+ */
8
+
9
+ module.exports = {
10
+ preset: 'ts-jest',
11
+ testEnvironment: '<rootDir>/../../../jest-environment-node-fixed.cjs',
12
+ roots: ['<rootDir>/__tests__'],
13
+ testMatch: ['**/*.test.ts'],
14
+ collectCoverageFrom: [
15
+ 'src/**/*.ts',
16
+ '!src/**/*.d.ts',
17
+ '!src/**/index.ts'
18
+ ],
19
+ coverageThreshold: {
20
+ global: {
21
+ branches: 90,
22
+ functions: 90,
23
+ lines: 90,
24
+ statements: 90
25
+ }
26
+ },
27
+ moduleNameMapper: {
28
+ '^@/(.*)$': '<rootDir>/src/$1'
29
+ }
30
+ };
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@bernierllc/content-storage-adapter",
3
+ "version": "1.0.0",
4
+ "description": "Type-safe CRUD operations for content management with soft-delete, version tracking, and optimistic locking",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "keywords": [
8
+ "content",
9
+ "storage",
10
+ "crud",
11
+ "database",
12
+ "soft-delete",
13
+ "versioning"
14
+ ],
15
+ "author": "Bernier LLC",
16
+ "license": "SEE LICENSE IN LICENSE",
17
+ "dependencies": {
18
+ "@bernierllc/database-adapter-core": "1.0.0",
19
+ "@bernierllc/slug-generator": "1.0.1"
20
+ },
21
+ "devDependencies": {
22
+ "@types/jest": "^29.5.0",
23
+ "@types/node": "^20.0.0",
24
+ "@typescript-eslint/eslint-plugin": "^6.0.0",
25
+ "@typescript-eslint/parser": "^6.0.0",
26
+ "eslint": "^8.0.0",
27
+ "jest": "^29.0.0",
28
+ "ts-jest": "^29.0.0",
29
+ "typescript": "^5.0.0"
30
+ },
31
+ "scripts": {
32
+ "build": "tsc",
33
+ "test": "jest --watch",
34
+ "test:run": "jest",
35
+ "test:coverage": "jest --coverage",
36
+ "lint": "eslint src __tests__ --ext .ts"
37
+ }
38
+ }
@@ -0,0 +1,72 @@
1
+ /*
2
+ Copyright (c) 2025 Bernier LLC
3
+
4
+ This file is licensed to the client under a limited-use license.
5
+ The client may use and modify this code *only within the scope of the project it was delivered for*.
6
+ Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
7
+ */
8
+
9
+ import { ContentStorageAdapter } from './ContentStorageAdapter';
10
+ import { ContentStatus } from './models/IContent';
11
+
12
+ /**
13
+ * Batch operations for content management
14
+ */
15
+ export class ContentBatchOperations {
16
+ constructor(private adapter: ContentStorageAdapter) {}
17
+
18
+ /**
19
+ * Bulk update status for multiple content items
20
+ */
21
+ async bulkUpdateStatus(
22
+ ids: string[],
23
+ status: ContentStatus
24
+ ): Promise<number> {
25
+ let updated = 0;
26
+
27
+ for (const id of ids) {
28
+ try {
29
+ await this.adapter.update(id, { status });
30
+ updated++;
31
+ } catch (error) {
32
+ // Continue on individual failures
33
+ }
34
+ }
35
+
36
+ return updated;
37
+ }
38
+
39
+ /**
40
+ * Bulk soft delete
41
+ */
42
+ async bulkDelete(ids: string[]): Promise<number> {
43
+ const db = (this.adapter as unknown as { db: { query: (sql: string, params: unknown[]) => Promise<{ rowCount: number }> } }).db;
44
+ const result = await db.query(
45
+ `
46
+ UPDATE content
47
+ SET deleted_at = NOW()
48
+ WHERE id = ANY($1) AND deleted_at IS NULL
49
+ `,
50
+ [ids]
51
+ );
52
+
53
+ return result.rowCount;
54
+ }
55
+
56
+ /**
57
+ * Bulk restore
58
+ */
59
+ async bulkRestore(ids: string[]): Promise<number> {
60
+ const db = (this.adapter as unknown as { db: { query: (sql: string, params: unknown[]) => Promise<{ rowCount: number }> } }).db;
61
+ const result = await db.query(
62
+ `
63
+ UPDATE content
64
+ SET deleted_at = NULL
65
+ WHERE id = ANY($1) AND deleted_at IS NOT NULL
66
+ `,
67
+ [ids]
68
+ );
69
+
70
+ return result.rowCount;
71
+ }
72
+ }