@storyshelf/core 0.1.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 (189) hide show
  1. package/README.md +127 -0
  2. package/dist/adapter/auth.d.mts +50 -0
  3. package/dist/adapter/auth.mjs +10 -0
  4. package/dist/adapter/auth.mjs.map +1 -0
  5. package/dist/adapter/capture-runner.d.mts +70 -0
  6. package/dist/adapter/capture-runner.mjs +4 -0
  7. package/dist/adapter/database.d.mts +43 -0
  8. package/dist/adapter/database.mjs +4 -0
  9. package/dist/adapter/git-host/helpers.d.mts +12 -0
  10. package/dist/adapter/git-host/helpers.mjs +29 -0
  11. package/dist/adapter/git-host/helpers.mjs.map +1 -0
  12. package/dist/adapter/git-host.d.mts +46 -0
  13. package/dist/adapter/git-host.mjs +4 -0
  14. package/dist/adapter/storage.d.mts +22 -0
  15. package/dist/adapter/storage.mjs +4 -0
  16. package/dist/adapters/capture-queue.d.mts +49 -0
  17. package/dist/adapters/metadata.d.mts +24 -0
  18. package/dist/adapters/webhook-events.mjs +39 -0
  19. package/dist/adapters/webhook-events.mjs.map +1 -0
  20. package/dist/capture/adapter.d.mts +29 -0
  21. package/dist/capture/adapter.mjs +13 -0
  22. package/dist/capture/adapter.mjs.map +1 -0
  23. package/dist/capture/orchestrator.d.mts +23 -0
  24. package/dist/capture/orchestrator.mjs +103 -0
  25. package/dist/capture/orchestrator.mjs.map +1 -0
  26. package/dist/capture/pipeline.d.mts +41 -0
  27. package/dist/capture/pipeline.mjs +119 -0
  28. package/dist/capture/pipeline.mjs.map +1 -0
  29. package/dist/capture/queue.d.mts +35 -0
  30. package/dist/capture/queue.mjs +96 -0
  31. package/dist/capture/queue.mjs.map +1 -0
  32. package/dist/capture/status-fanout.mjs +66 -0
  33. package/dist/capture/status-fanout.mjs.map +1 -0
  34. package/dist/capture/storybook.d.mts +14 -0
  35. package/dist/capture/storybook.mjs +40 -0
  36. package/dist/capture/storybook.mjs.map +1 -0
  37. package/dist/config.d.mts +68 -0
  38. package/dist/config.mjs +69 -0
  39. package/dist/config.mjs.map +1 -0
  40. package/dist/ddl.d.mts +7 -0
  41. package/dist/ddl.mjs +155 -0
  42. package/dist/ddl.mjs.map +1 -0
  43. package/dist/diff/engine.d.mts +8 -0
  44. package/dist/diff/engine.mjs +79 -0
  45. package/dist/diff/engine.mjs.map +1 -0
  46. package/dist/diff/options.d.mts +40 -0
  47. package/dist/diff/options.mjs +15 -0
  48. package/dist/diff/options.mjs.map +1 -0
  49. package/dist/index.d.mts +48 -0
  50. package/dist/index.mjs +327 -0
  51. package/dist/index.mjs.map +1 -0
  52. package/dist/logger.d.mts +39 -0
  53. package/dist/logger.mjs +35 -0
  54. package/dist/logger.mjs.map +1 -0
  55. package/dist/middleware/csrf.mjs +39 -0
  56. package/dist/middleware/csrf.mjs.map +1 -0
  57. package/dist/middleware/rate-limit.mjs +38 -0
  58. package/dist/middleware/rate-limit.mjs.map +1 -0
  59. package/dist/models/baseline.d.mts +20 -0
  60. package/dist/models/baseline.mjs +89 -0
  61. package/dist/models/baseline.mjs.map +1 -0
  62. package/dist/models/build.d.mts +35 -0
  63. package/dist/models/build.mjs +77 -0
  64. package/dist/models/build.mjs.map +1 -0
  65. package/dist/models/comment.d.mts +20 -0
  66. package/dist/models/comment.mjs +42 -0
  67. package/dist/models/comment.mjs.map +1 -0
  68. package/dist/models/label.d.mts +43 -0
  69. package/dist/models/label.mjs +107 -0
  70. package/dist/models/label.mjs.map +1 -0
  71. package/dist/models/member.d.mts +27 -0
  72. package/dist/models/member.mjs +54 -0
  73. package/dist/models/member.mjs.map +1 -0
  74. package/dist/models/project.d.mts +42 -0
  75. package/dist/models/project.mjs +78 -0
  76. package/dist/models/project.mjs.map +1 -0
  77. package/dist/models/snapshot.d.mts +46 -0
  78. package/dist/models/snapshot.mjs +75 -0
  79. package/dist/models/snapshot.mjs.map +1 -0
  80. package/dist/models/status-config.mjs +61 -0
  81. package/dist/models/status-config.mjs.map +1 -0
  82. package/dist/models/token.d.mts +33 -0
  83. package/dist/models/token.mjs +60 -0
  84. package/dist/models/token.mjs.map +1 -0
  85. package/dist/models/webhook.d.mts +37 -0
  86. package/dist/models/webhook.mjs +67 -0
  87. package/dist/models/webhook.mjs.map +1 -0
  88. package/dist/pages/build-detail.mjs +333 -0
  89. package/dist/pages/build-detail.mjs.map +1 -0
  90. package/dist/pages/build-diff.mjs +437 -0
  91. package/dist/pages/build-diff.mjs.map +1 -0
  92. package/dist/pages/compute-jobs.mjs +196 -0
  93. package/dist/pages/compute-jobs.mjs.map +1 -0
  94. package/dist/pages/login.mjs +77 -0
  95. package/dist/pages/login.mjs.map +1 -0
  96. package/dist/pages/project-builds.mjs +228 -0
  97. package/dist/pages/project-builds.mjs.map +1 -0
  98. package/dist/pages/project-create.mjs +168 -0
  99. package/dist/pages/project-create.mjs.map +1 -0
  100. package/dist/pages/project-settings.mjs +111 -0
  101. package/dist/pages/project-settings.mjs.map +1 -0
  102. package/dist/pages/projects.mjs +178 -0
  103. package/dist/pages/projects.mjs.map +1 -0
  104. package/dist/pages/root.mjs +90 -0
  105. package/dist/pages/root.mjs.map +1 -0
  106. package/dist/pages/settings-general.mjs +191 -0
  107. package/dist/pages/settings-general.mjs.map +1 -0
  108. package/dist/pages/settings-labels.mjs +149 -0
  109. package/dist/pages/settings-labels.mjs.map +1 -0
  110. package/dist/pages/settings-members.mjs +124 -0
  111. package/dist/pages/settings-members.mjs.map +1 -0
  112. package/dist/pages/settings-status.mjs +150 -0
  113. package/dist/pages/settings-status.mjs.map +1 -0
  114. package/dist/pages/settings-tokens.mjs +95 -0
  115. package/dist/pages/settings-tokens.mjs.map +1 -0
  116. package/dist/pages/settings-webhooks.mjs +149 -0
  117. package/dist/pages/settings-webhooks.mjs.map +1 -0
  118. package/dist/retention/purge.d.mts +28 -0
  119. package/dist/retention/purge.mjs +66 -0
  120. package/dist/retention/purge.mjs.map +1 -0
  121. package/dist/routers/admin.mjs +41 -0
  122. package/dist/routers/admin.mjs.map +1 -0
  123. package/dist/routers/assets.mjs +24 -0
  124. package/dist/routers/assets.mjs.map +1 -0
  125. package/dist/routers/auth.mjs +77 -0
  126. package/dist/routers/auth.mjs.map +1 -0
  127. package/dist/routers/builds.handlers.mjs +87 -0
  128. package/dist/routers/builds.handlers.mjs.map +1 -0
  129. package/dist/routers/builds.mjs +162 -0
  130. package/dist/routers/builds.mjs.map +1 -0
  131. package/dist/routers/comments.mjs +89 -0
  132. package/dist/routers/comments.mjs.map +1 -0
  133. package/dist/routers/helpers.mjs +76 -0
  134. package/dist/routers/helpers.mjs.map +1 -0
  135. package/dist/routers/htmx.mjs +18 -0
  136. package/dist/routers/htmx.mjs.map +1 -0
  137. package/dist/routers/labels.mjs +77 -0
  138. package/dist/routers/labels.mjs.map +1 -0
  139. package/dist/routers/media.mjs +54 -0
  140. package/dist/routers/media.mjs.map +1 -0
  141. package/dist/routers/members.mjs +101 -0
  142. package/dist/routers/members.mjs.map +1 -0
  143. package/dist/routers/projects.mjs +121 -0
  144. package/dist/routers/projects.mjs.map +1 -0
  145. package/dist/routers/schemas.mjs +188 -0
  146. package/dist/routers/schemas.mjs.map +1 -0
  147. package/dist/routers/settings.mjs +235 -0
  148. package/dist/routers/settings.mjs.map +1 -0
  149. package/dist/routers/snapshots.mjs +143 -0
  150. package/dist/routers/snapshots.mjs.map +1 -0
  151. package/dist/routers/status-configs.mjs +100 -0
  152. package/dist/routers/status-configs.mjs.map +1 -0
  153. package/dist/routers/tokens.mjs +80 -0
  154. package/dist/routers/tokens.mjs.map +1 -0
  155. package/dist/routers/ui.mjs +142 -0
  156. package/dist/routers/ui.mjs.map +1 -0
  157. package/dist/routers/webhooks.mjs +89 -0
  158. package/dist/routers/webhooks.mjs.map +1 -0
  159. package/dist/schema.d.mts +4372 -0
  160. package/dist/schema.mjs +158 -0
  161. package/dist/schema.mjs.map +1 -0
  162. package/dist/store.mjs +18 -0
  163. package/dist/store.mjs.map +1 -0
  164. package/dist/types.d.mts +18 -0
  165. package/dist/types.mjs +49 -0
  166. package/dist/types.mjs.map +1 -0
  167. package/dist/ui/components.mjs +127 -0
  168. package/dist/ui/components.mjs.map +1 -0
  169. package/dist/ui/document.d.mts +8 -0
  170. package/dist/ui/document.mjs +338 -0
  171. package/dist/ui/document.mjs.map +1 -0
  172. package/dist/ui/styles.mjs +173 -0
  173. package/dist/ui/styles.mjs.map +1 -0
  174. package/dist/ui/theme.mjs +42 -0
  175. package/dist/ui/theme.mjs.map +1 -0
  176. package/dist/urls.d.mts +19 -0
  177. package/dist/urls.mjs +23 -0
  178. package/dist/urls.mjs.map +1 -0
  179. package/dist/utils/encrypt.mjs +49 -0
  180. package/dist/utils/encrypt.mjs.map +1 -0
  181. package/dist/utils/hash.mjs +22 -0
  182. package/dist/utils/hash.mjs.map +1 -0
  183. package/dist/utils/paths.d.mts +11 -0
  184. package/dist/utils/paths.mjs +23 -0
  185. package/dist/utils/paths.mjs.map +1 -0
  186. package/dist/utils/ulid.d.mts +8 -0
  187. package/dist/utils/ulid.mjs +41 -0
  188. package/dist/utils/ulid.mjs.map +1 -0
  189. package/package.json +162 -0
package/dist/urls.mjs ADDED
@@ -0,0 +1,23 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ //#region src/urls.ts
5
+ function createUrlBuilder(baseUrl, publishedBaseDomain) {
6
+ const root = baseUrl.replace(/\/+$/u, "");
7
+ return {
8
+ projectsList: () => root,
9
+ project: (slug) => `${root}/projects/${slug}`,
10
+ buildsList: (slug) => `${root}/projects/${slug}/builds`,
11
+ build: (slug, buildId) => `${root}/projects/${slug}/builds/${buildId}`,
12
+ labels: (slug) => `${root}/projects/${slug}/labels`,
13
+ label: (slug, key, value) => `${root}/projects/${slug}/labels/${encodeURIComponent(key)}/${encodeURI(value)}`,
14
+ storybook: (slug) => publishedBaseDomain ? `https://${slug}.${publishedBaseDomain}` : `${root}/projects/${slug}/storybook`,
15
+ storybookLabel: (slug, key, value) => `${root}/projects/${slug}/storybook/${encodeURIComponent(key)}/${encodeURI(value)}`,
16
+ storybookBuild: (slug, buildId) => publishedBaseDomain ? `https://${buildId}.${slug}.${publishedBaseDomain}` : `${root}/projects/${slug}/storybook/build/${buildId}`,
17
+ settings: (slug) => `${root}/projects/${slug}/settings`
18
+ };
19
+ }
20
+ //#endregion
21
+ export { createUrlBuilder };
22
+
23
+ //# sourceMappingURL=urls.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"urls.mjs","names":[],"sources":["../src/urls.ts"],"sourcesContent":["export interface UrlBuilder {\n projectsList(): string;\n project(slug: string): string;\n buildsList(slug: string): string;\n build(slug: string, buildId: string): string;\n labels(slug: string): string;\n label(slug: string, key: string, value: string): string;\n storybook(slug: string): string;\n storybookLabel(slug: string, key: string, value: string): string;\n storybookBuild(slug: string, buildId: string): string;\n settings(slug: string): string;\n}\n\nexport function createUrlBuilder(baseUrl: string, publishedBaseDomain?: string): UrlBuilder {\n const root = baseUrl.replace(/\\/+$/u, \"\");\n\n return {\n projectsList: () => root,\n project: (slug) => `${root}/projects/${slug}`,\n buildsList: (slug) => `${root}/projects/${slug}/builds`,\n build: (slug, buildId) => `${root}/projects/${slug}/builds/${buildId}`,\n labels: (slug) => `${root}/projects/${slug}/labels`,\n label: (slug, key, value) => `${root}/projects/${slug}/labels/${encodeURIComponent(key)}/${encodeURI(value)}`,\n storybook: (slug) => (publishedBaseDomain ? `https://${slug}.${publishedBaseDomain}` : `${root}/projects/${slug}/storybook`),\n storybookLabel: (slug, key, value) => `${root}/projects/${slug}/storybook/${encodeURIComponent(key)}/${encodeURI(value)}`,\n storybookBuild: (slug, buildId) =>\n publishedBaseDomain ? `https://${buildId}.${slug}.${publishedBaseDomain}` : `${root}/projects/${slug}/storybook/build/${buildId}`,\n settings: (slug) => `${root}/projects/${slug}/settings`,\n };\n}\n"],"mappings":";;;;AAaA,SAAgB,iBAAiB,SAAiB,qBAA0C;CAC1F,MAAM,OAAO,QAAQ,QAAQ,SAAS,EAAE;CAExC,OAAO;EACL,oBAAoB;EACpB,UAAU,SAAS,GAAG,KAAK,YAAY;EACvC,aAAa,SAAS,GAAG,KAAK,YAAY,KAAK;EAC/C,QAAQ,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,UAAU;EAC7D,SAAS,SAAS,GAAG,KAAK,YAAY,KAAK;EAC3C,QAAQ,MAAM,KAAK,UAAU,GAAG,KAAK,YAAY,KAAK,UAAU,mBAAmB,GAAG,EAAE,GAAG,UAAU,KAAK;EAC1G,YAAY,SAAU,sBAAsB,WAAW,KAAK,GAAG,wBAAwB,GAAG,KAAK,YAAY,KAAK;EAChH,iBAAiB,MAAM,KAAK,UAAU,GAAG,KAAK,YAAY,KAAK,aAAa,mBAAmB,GAAG,EAAE,GAAG,UAAU,KAAK;EACtH,iBAAiB,MAAM,YACrB,sBAAsB,WAAW,QAAQ,GAAG,KAAK,GAAG,wBAAwB,GAAG,KAAK,YAAY,KAAK,mBAAmB;EAC1H,WAAW,SAAS,GAAG,KAAK,YAAY,KAAK;CAC/C;AACF"}
@@ -0,0 +1,49 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ import { createCipheriv, createDecipheriv, createHash, randomBytes } from "node:crypto";
5
+ //#region src/utils/encrypt.ts
6
+ /**
7
+ * Derive a 32-byte AES-256 key from the server secret.
8
+ *
9
+ * Reuses the existing `sha256` pattern (`utils/hash.ts`) so key derivation
10
+ * is consistent with token hashing and session HMAC.
11
+ */
12
+ function deriveKey(secret) {
13
+ return createHash("sha256").update(secret).digest();
14
+ }
15
+ /**
16
+ * Encrypt a plaintext secret for storage.
17
+ *
18
+ * Format: `base64url(iv):base64url(authTag):base64url(ciphertext)` (12B IV).
19
+ * Uses `secret` from `ShelfConfig.secret` — throws if missing so misconfiguration
20
+ * fails loudly like `scratchDir` does in `capture/orchestrator.ts`.
21
+ */
22
+ function encrypt(secret, plaintext) {
23
+ if (!secret) throw new Error("Cannot encrypt: ShelfConfig.secret is not configured");
24
+ const key = deriveKey(secret);
25
+ const iv = randomBytes(12);
26
+ const cipher = createCipheriv("aes-256-gcm", key, iv);
27
+ const enc = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
28
+ const tag = cipher.getAuthTag();
29
+ return `${iv.toString("base64url")}:${tag.toString("base64url")}:${enc.toString("base64url")}`;
30
+ }
31
+ /**
32
+ * Decrypt a value produced by `encrypt`.
33
+ */
34
+ function decrypt(secret, ciphertext) {
35
+ if (!secret) throw new Error("Cannot decrypt: ShelfConfig.secret is not configured");
36
+ const [ivB64, tagB64, encB64] = ciphertext.split(":");
37
+ if (!ivB64 || !tagB64 || !encB64) throw new Error("Invalid encrypted payload format");
38
+ const key = deriveKey(secret);
39
+ const iv = Buffer.from(ivB64, "base64url");
40
+ const tag = Buffer.from(tagB64, "base64url");
41
+ const enc = Buffer.from(encB64, "base64url");
42
+ const decipher = createDecipheriv("aes-256-gcm", key, iv);
43
+ decipher.setAuthTag(tag);
44
+ return Buffer.concat([decipher.update(enc), decipher.final()]).toString("utf8");
45
+ }
46
+ //#endregion
47
+ export { decrypt, encrypt };
48
+
49
+ //# sourceMappingURL=encrypt.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encrypt.mjs","names":[],"sources":["../../src/utils/encrypt.ts"],"sourcesContent":["import { createCipheriv, createDecipheriv, createHash, randomBytes } from \"node:crypto\";\n\n/**\n * Derive a 32-byte AES-256 key from the server secret.\n *\n * Reuses the existing `sha256` pattern (`utils/hash.ts`) so key derivation\n * is consistent with token hashing and session HMAC.\n */\nfunction deriveKey(secret: string): Buffer {\n return createHash(\"sha256\").update(secret).digest();\n}\n\n/**\n * Encrypt a plaintext secret for storage.\n *\n * Format: `base64url(iv):base64url(authTag):base64url(ciphertext)` (12B IV).\n * Uses `secret` from `ShelfConfig.secret` — throws if missing so misconfiguration\n * fails loudly like `scratchDir` does in `capture/orchestrator.ts`.\n */\nexport function encrypt(secret: string | undefined, plaintext: string): string {\n if (!secret) {\n throw new Error(\"Cannot encrypt: ShelfConfig.secret is not configured\");\n }\n const key = deriveKey(secret);\n const iv = randomBytes(12);\n const cipher = createCipheriv(\"aes-256-gcm\", key, iv);\n const enc = Buffer.concat([cipher.update(plaintext, \"utf8\"), cipher.final()]);\n const tag = cipher.getAuthTag();\n return `${iv.toString(\"base64url\")}:${tag.toString(\"base64url\")}:${enc.toString(\"base64url\")}`;\n}\n\n/**\n * Decrypt a value produced by `encrypt`.\n */\nexport function decrypt(secret: string | undefined, ciphertext: string): string {\n if (!secret) {\n throw new Error(\"Cannot decrypt: ShelfConfig.secret is not configured\");\n }\n const [ivB64, tagB64, encB64] = ciphertext.split(\":\");\n if (!ivB64 || !tagB64 || !encB64) {\n throw new Error(\"Invalid encrypted payload format\");\n }\n const key = deriveKey(secret);\n const iv = Buffer.from(ivB64, \"base64url\");\n const tag = Buffer.from(tagB64, \"base64url\");\n const enc = Buffer.from(encB64, \"base64url\");\n const decipher = createDecipheriv(\"aes-256-gcm\", key, iv);\n decipher.setAuthTag(tag);\n const dec = Buffer.concat([decipher.update(enc), decipher.final()]);\n return dec.toString(\"utf8\");\n}\n"],"mappings":";;;;;;;;;;;AAQA,SAAS,UAAU,QAAwB;CACzC,OAAO,WAAW,QAAQ,CAAC,CAAC,OAAO,MAAM,CAAC,CAAC,OAAO;AACpD;;;;;;;;AASA,SAAgB,QAAQ,QAA4B,WAA2B;CAC7E,IAAI,CAAC,QACH,MAAM,IAAI,MAAM,sDAAsD;CAExE,MAAM,MAAM,UAAU,MAAM;CAC5B,MAAM,KAAK,YAAY,EAAE;CACzB,MAAM,SAAS,eAAe,eAAe,KAAK,EAAE;CACpD,MAAM,MAAM,OAAO,OAAO,CAAC,OAAO,OAAO,WAAW,MAAM,GAAG,OAAO,MAAM,CAAC,CAAC;CAC5E,MAAM,MAAM,OAAO,WAAW;CAC9B,OAAO,GAAG,GAAG,SAAS,WAAW,EAAE,GAAG,IAAI,SAAS,WAAW,EAAE,GAAG,IAAI,SAAS,WAAW;AAC7F;;;;AAKA,SAAgB,QAAQ,QAA4B,YAA4B;CAC9E,IAAI,CAAC,QACH,MAAM,IAAI,MAAM,sDAAsD;CAExE,MAAM,CAAC,OAAO,QAAQ,UAAU,WAAW,MAAM,GAAG;CACpD,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,QACxB,MAAM,IAAI,MAAM,kCAAkC;CAEpD,MAAM,MAAM,UAAU,MAAM;CAC5B,MAAM,KAAK,OAAO,KAAK,OAAO,WAAW;CACzC,MAAM,MAAM,OAAO,KAAK,QAAQ,WAAW;CAC3C,MAAM,MAAM,OAAO,KAAK,QAAQ,WAAW;CAC3C,MAAM,WAAW,iBAAiB,eAAe,KAAK,EAAE;CACxD,SAAS,WAAW,GAAG;CAEvB,OADY,OAAO,OAAO,CAAC,SAAS,OAAO,GAAG,GAAG,SAAS,MAAM,CAAC,CACxD,CAAC,CAAC,SAAS,MAAM;AAC5B"}
@@ -0,0 +1,22 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ import { createHash, createHmac, randomBytes } from "node:crypto";
5
+ //#region src/utils/hash.ts
6
+ function sha256(value) {
7
+ return createHash("sha256").update(value).digest("hex");
8
+ }
9
+ function hmacSha256(secret, value) {
10
+ return createHmac("sha256", secret).update(value).digest("hex");
11
+ }
12
+ function randomToken(prefix) {
13
+ const value = `${prefix}${randomBytes(24).toString("base64url")}`;
14
+ return {
15
+ value,
16
+ hash: sha256(value)
17
+ };
18
+ }
19
+ //#endregion
20
+ export { hmacSha256, randomToken, sha256 };
21
+
22
+ //# sourceMappingURL=hash.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hash.mjs","names":[],"sources":["../../src/utils/hash.ts"],"sourcesContent":["import { createHash, createHmac, randomBytes, timingSafeEqual } from \"node:crypto\";\n\nexport function sha256(value: string): string {\n return createHash(\"sha256\").update(value).digest(\"hex\");\n}\n\nexport function hmacSha256(secret: string, value: string): string {\n return createHmac(\"sha256\", secret).update(value).digest(\"hex\");\n}\n\nexport function randomToken(prefix: string): { value: string; hash: string } {\n const value = `${prefix}${randomBytes(24).toString(\"base64url\")}`;\n return { value, hash: sha256(value) };\n}\n\nexport function timingSafeEqualString(a: string, b: string): boolean {\n const ab = Buffer.from(a);\n const bb = Buffer.from(b);\n return ab.length === bb.length && timingSafeEqual(ab, bb);\n}\n"],"mappings":";;;;;AAEA,SAAgB,OAAO,OAAuB;CAC5C,OAAO,WAAW,QAAQ,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO,KAAK;AACxD;AAEA,SAAgB,WAAW,QAAgB,OAAuB;CAChE,OAAO,WAAW,UAAU,MAAM,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO,KAAK;AAChE;AAEA,SAAgB,YAAY,QAAiD;CAC3E,MAAM,QAAQ,GAAG,SAAS,YAAY,EAAE,CAAC,CAAC,SAAS,WAAW;CAC9D,OAAO;EAAE;EAAO,MAAM,OAAO,KAAK;CAAE;AACtC"}
@@ -0,0 +1,11 @@
1
+ import __tsdown_shims_path from 'node:path';
2
+ import __tsdown_shims_url from 'node:url';
3
+ //#region src/utils/paths.d.ts
4
+ declare function screenshotPath(projectId: string, buildId: string, storyId: string, viewport: string): string;
5
+ declare function diffPath(projectId: string, buildId: string, storyId: string, viewport: string): string;
6
+ declare function baselinePath(projectId: string, branch: string, storyId: string, viewport: string): string;
7
+ declare function storybookDir(projectId: string, buildId: string): string;
8
+ declare function storybookZipPath(projectId: string, buildId: string): string;
9
+ //#endregion
10
+ export { baselinePath, diffPath, screenshotPath, storybookDir, storybookZipPath };
11
+ //# sourceMappingURL=paths.d.mts.map
@@ -0,0 +1,23 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ //#region src/utils/paths.ts
5
+ function screenshotPath(projectId, buildId, storyId, viewport) {
6
+ return `${projectId}/builds/${buildId}/screenshots/${storyId}/${viewport}.png`;
7
+ }
8
+ function diffPath(projectId, buildId, storyId, viewport) {
9
+ return `${projectId}/builds/${buildId}/diffs/${storyId}/${viewport}.png`;
10
+ }
11
+ function baselinePath(projectId, branch, storyId, viewport) {
12
+ return `${projectId}/baselines/${branch}/${storyId}/${viewport}.png`;
13
+ }
14
+ function storybookDir(projectId, buildId) {
15
+ return `${projectId}/builds/${buildId}/storybook`;
16
+ }
17
+ function storybookZipPath(projectId, buildId) {
18
+ return `${projectId}/builds/${buildId}/storybook.zip`;
19
+ }
20
+ //#endregion
21
+ export { baselinePath, diffPath, screenshotPath, storybookDir, storybookZipPath };
22
+
23
+ //# sourceMappingURL=paths.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.mjs","names":[],"sources":["../../src/utils/paths.ts"],"sourcesContent":["export function screenshotPath(projectId: string, buildId: string, storyId: string, viewport: string): string {\n return `${projectId}/builds/${buildId}/screenshots/${storyId}/${viewport}.png`;\n}\n\nexport function diffPath(projectId: string, buildId: string, storyId: string, viewport: string): string {\n return `${projectId}/builds/${buildId}/diffs/${storyId}/${viewport}.png`;\n}\n\nexport function baselinePath(projectId: string, branch: string, storyId: string, viewport: string): string {\n return `${projectId}/baselines/${branch}/${storyId}/${viewport}.png`;\n}\n\nexport function storybookDir(projectId: string, buildId: string): string {\n return `${projectId}/builds/${buildId}/storybook`;\n}\n\nexport function storybookZipPath(projectId: string, buildId: string): string {\n return `${projectId}/builds/${buildId}/storybook.zip`;\n}\n"],"mappings":";;;;AAAA,SAAgB,eAAe,WAAmB,SAAiB,SAAiB,UAA0B;CAC5G,OAAO,GAAG,UAAU,UAAU,QAAQ,eAAe,QAAQ,GAAG,SAAS;AAC3E;AAEA,SAAgB,SAAS,WAAmB,SAAiB,SAAiB,UAA0B;CACtG,OAAO,GAAG,UAAU,UAAU,QAAQ,SAAS,QAAQ,GAAG,SAAS;AACrE;AAEA,SAAgB,aAAa,WAAmB,QAAgB,SAAiB,UAA0B;CACzG,OAAO,GAAG,UAAU,aAAa,OAAO,GAAG,QAAQ,GAAG,SAAS;AACjE;AAEA,SAAgB,aAAa,WAAmB,SAAyB;CACvE,OAAO,GAAG,UAAU,UAAU,QAAQ;AACxC;AAEA,SAAgB,iBAAiB,WAAmB,SAAyB;CAC3E,OAAO,GAAG,UAAU,UAAU,QAAQ;AACxC"}
@@ -0,0 +1,8 @@
1
+ import __tsdown_shims_path from 'node:path';
2
+ import __tsdown_shims_url from 'node:url';
3
+ //#region src/utils/ulid.d.ts
4
+ declare function ulid(): string;
5
+ declare function slugify(value: string): string;
6
+ //#endregion
7
+ export { slugify, ulid };
8
+ //# sourceMappingURL=ulid.d.mts.map
@@ -0,0 +1,41 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ import { randomBytes } from "node:crypto";
5
+ //#region src/utils/ulid.ts
6
+ const CROCKFORD = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
7
+ function encodeTime(time) {
8
+ let out = "";
9
+ let remaining = time;
10
+ for (let i = 9; i >= 0; i -= 1) {
11
+ const mod = remaining % 32;
12
+ out = CROCKFORD[mod] + out;
13
+ remaining = (remaining - mod) / 32;
14
+ }
15
+ return out;
16
+ }
17
+ function encodeRandom(bytes) {
18
+ let out = "";
19
+ let buffer = 0;
20
+ let bits = 0;
21
+ for (const byte of bytes) {
22
+ buffer = buffer << 8 | byte;
23
+ bits += 8;
24
+ while (bits >= 5) {
25
+ out += CROCKFORD[buffer >>> bits - 5 & 31];
26
+ bits -= 5;
27
+ }
28
+ }
29
+ if (bits > 0) out += CROCKFORD[buffer << 5 - bits & 31];
30
+ return out;
31
+ }
32
+ function ulid() {
33
+ return encodeTime(Date.now()) + encodeRandom(randomBytes(10));
34
+ }
35
+ function slugify(value) {
36
+ return value.toLowerCase().replaceAll(/[^a-z0-9]+/gu, "-").replaceAll(/^-+|-+$/gu, "").slice(0, 63);
37
+ }
38
+ //#endregion
39
+ export { slugify, ulid };
40
+
41
+ //# sourceMappingURL=ulid.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ulid.mjs","names":[],"sources":["../../src/utils/ulid.ts"],"sourcesContent":["import { randomBytes } from \"node:crypto\";\n\nconst CROCKFORD = \"0123456789ABCDEFGHJKMNPQRSTVWXYZ\";\n\nfunction encodeTime(time: number): string {\n let out = \"\";\n let remaining = time;\n for (let i = 9; i >= 0; i -= 1) {\n const mod = remaining % 32;\n // eslint-disable-next-line no-non-null-assertion -- index is within CROCKFORD length\n out = CROCKFORD[mod]! + out;\n remaining = (remaining - mod) / 32;\n }\n return out;\n}\n\n/* eslint-disable no-bitwise, no-non-null-assertion -- ULID encoding is inherently bitwise. */\nfunction encodeRandom(bytes: Buffer): string {\n let out = \"\";\n let buffer = 0;\n let bits = 0;\n for (const byte of bytes) {\n buffer = (buffer << 8) | byte;\n bits += 8;\n while (bits >= 5) {\n out += CROCKFORD[(buffer >>> (bits - 5)) & 31]!;\n bits -= 5;\n }\n }\n if (bits > 0) {\n out += CROCKFORD[(buffer << (5 - bits)) & 31]!;\n }\n return out;\n}\n/* eslint-enable no-bitwise, no-non-null-assertion */\n\nexport function ulid(): string {\n return encodeTime(Date.now()) + encodeRandom(randomBytes(10));\n}\n\nexport function slugify(value: string): string {\n return value\n .toLowerCase()\n .replaceAll(/[^a-z0-9]+/gu, \"-\")\n .replaceAll(/^-+|-+$/gu, \"\")\n .slice(0, 63);\n}\n"],"mappings":";;;;;AAEA,MAAM,YAAY;AAElB,SAAS,WAAW,MAAsB;CACxC,IAAI,MAAM;CACV,IAAI,YAAY;CAChB,KAAK,IAAI,IAAI,GAAG,KAAK,GAAG,KAAK,GAAG;EAC9B,MAAM,MAAM,YAAY;EAExB,MAAM,UAAU,OAAQ;EACxB,aAAa,YAAY,OAAO;CAClC;CACA,OAAO;AACT;AAGA,SAAS,aAAa,OAAuB;CAC3C,IAAI,MAAM;CACV,IAAI,SAAS;CACb,IAAI,OAAO;CACX,KAAK,MAAM,QAAQ,OAAO;EACxB,SAAU,UAAU,IAAK;EACzB,QAAQ;EACR,OAAO,QAAQ,GAAG;GAChB,OAAO,UAAW,WAAY,OAAO,IAAM;GAC3C,QAAQ;EACV;CACF;CACA,IAAI,OAAO,GACT,OAAO,UAAW,UAAW,IAAI,OAAS;CAE5C,OAAO;AACT;AAGA,SAAgB,OAAe;CAC7B,OAAO,WAAW,KAAK,IAAI,CAAC,IAAI,aAAa,YAAY,EAAE,CAAC;AAC9D;AAEA,SAAgB,QAAQ,OAAuB;CAC7C,OAAO,MACJ,YAAY,CAAC,CACb,WAAW,gBAAgB,GAAG,CAAC,CAC/B,WAAW,aAAa,EAAE,CAAC,CAC3B,MAAM,GAAG,EAAE;AAChB"}
package/package.json ADDED
@@ -0,0 +1,162 @@
1
+ {
2
+ "name": "@storyshelf/core",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "StoryShelf core: Hono router, adapter interfaces, models, capture pipeline, diff, retention.",
6
+ "author": {
7
+ "name": "Siddhant Gupta",
8
+ "url": "https://guptasiddhant.com"
9
+ },
10
+ "license": "MIT",
11
+ "sideEffects": false,
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/GuptaSiddhant/storyshelf.git",
15
+ "directory": "packages/core"
16
+ },
17
+ "homepage": "https://github.com/GuptaSiddhant/storyshelf#readme",
18
+ "bugs": {
19
+ "url": "https://github.com/GuptaSiddhant/storyshelf/issues"
20
+ },
21
+ "publishConfig": {
22
+ "access": "public",
23
+ "exports": {
24
+ ".": "./dist/index.mjs",
25
+ "./adapter/auth": "./dist/adapter/auth.mjs",
26
+ "./adapter/capture-runner": "./dist/adapter/capture-runner.mjs",
27
+ "./adapter/database": "./dist/adapter/database.mjs",
28
+ "./adapter/git-host": "./dist/adapter/git-host.mjs",
29
+ "./adapter/git-host/helpers": "./dist/adapter/git-host/helpers.mjs",
30
+ "./adapter/storage": "./dist/adapter/storage.mjs",
31
+ "./ddl": "./dist/ddl.mjs",
32
+ "./models/baseline": "./dist/models/baseline.mjs",
33
+ "./models/build": "./dist/models/build.mjs",
34
+ "./models/comment": "./dist/models/comment.mjs",
35
+ "./models/label": "./dist/models/label.mjs",
36
+ "./models/member": "./dist/models/member.mjs",
37
+ "./models/project": "./dist/models/project.mjs",
38
+ "./models/snapshot": "./dist/models/snapshot.mjs",
39
+ "./models/token": "./dist/models/token.mjs",
40
+ "./models/webhook": "./dist/models/webhook.mjs",
41
+ "./schema": "./dist/schema.mjs",
42
+ "./types": "./dist/types.mjs",
43
+ "./package.json": "./package.json"
44
+ }
45
+ },
46
+ "files": [
47
+ "dist"
48
+ ],
49
+ "scripts": {
50
+ "build": "tsdown",
51
+ "dev": "tsdown -w",
52
+ "fmt": "oxfmt -c ../../.oxfmtrc.json ./src",
53
+ "lint": "oxlint --type-aware --type-check ./src",
54
+ "lint:fix": "nub run lint -- --fix",
55
+ "prepublishOnly": "nub run build",
56
+ "test": "vitest run",
57
+ "test:coverage": "vitest run --coverage",
58
+ "test:watch": "vitest"
59
+ },
60
+ "dependencies": {
61
+ "@hono/swagger-ui": "^0.6.1",
62
+ "@hono/zod-openapi": "^1.1.5",
63
+ "adm-zip": "^0.6.0",
64
+ "drizzle-orm": "^0.44.0",
65
+ "hono": "^4.11.0",
66
+ "pino": "^10.3.1",
67
+ "pixelmatch": "^7.1.0",
68
+ "pngjs": "^7.0.0",
69
+ "zod": "^4.2.0"
70
+ },
71
+ "devDependencies": {
72
+ "@types/node": "catalog:",
73
+ "@types/pngjs": "^6.0.5",
74
+ "@vitest/coverage-v8": "catalog:",
75
+ "oxfmt": "catalog:",
76
+ "oxlint": "catalog:",
77
+ "oxlint-tsgolint": "catalog:",
78
+ "tsdown": "catalog:",
79
+ "typescript": "catalog:",
80
+ "vitest": "catalog:"
81
+ },
82
+ "types": "./dist/index.d.mts",
83
+ "exports": {
84
+ ".": {
85
+ "source": "./src/index.tsx",
86
+ "default": "./dist/index.mjs"
87
+ },
88
+ "./adapter/auth": {
89
+ "source": "./src/adapters/auth.ts",
90
+ "default": "./dist/adapter/auth.mjs"
91
+ },
92
+ "./adapter/capture-runner": {
93
+ "source": "./src/adapters/capture-runner.ts",
94
+ "default": "./dist/adapter/capture-runner.mjs"
95
+ },
96
+ "./adapter/database": {
97
+ "source": "./src/adapters/database.ts",
98
+ "default": "./dist/adapter/database.mjs"
99
+ },
100
+ "./adapter/git-host": {
101
+ "source": "./src/adapters/git-host/index.ts",
102
+ "default": "./dist/adapter/git-host.mjs"
103
+ },
104
+ "./adapter/git-host/helpers": {
105
+ "source": "./src/adapters/git-host/helpers.ts",
106
+ "default": "./dist/adapter/git-host/helpers.mjs"
107
+ },
108
+ "./adapter/storage": {
109
+ "source": "./src/adapters/storage.ts",
110
+ "default": "./dist/adapter/storage.mjs"
111
+ },
112
+ "./ddl": {
113
+ "source": "./src/ddl.ts",
114
+ "default": "./dist/ddl.mjs"
115
+ },
116
+ "./models/baseline": {
117
+ "source": "./src/models/baseline.ts",
118
+ "default": "./dist/models/baseline.mjs"
119
+ },
120
+ "./models/build": {
121
+ "source": "./src/models/build.ts",
122
+ "default": "./dist/models/build.mjs"
123
+ },
124
+ "./models/comment": {
125
+ "source": "./src/models/comment.ts",
126
+ "default": "./dist/models/comment.mjs"
127
+ },
128
+ "./models/label": {
129
+ "source": "./src/models/label.ts",
130
+ "default": "./dist/models/label.mjs"
131
+ },
132
+ "./models/member": {
133
+ "source": "./src/models/member.ts",
134
+ "default": "./dist/models/member.mjs"
135
+ },
136
+ "./models/project": {
137
+ "source": "./src/models/project.ts",
138
+ "default": "./dist/models/project.mjs"
139
+ },
140
+ "./models/snapshot": {
141
+ "source": "./src/models/snapshot.ts",
142
+ "default": "./dist/models/snapshot.mjs"
143
+ },
144
+ "./models/token": {
145
+ "source": "./src/models/token.ts",
146
+ "default": "./dist/models/token.mjs"
147
+ },
148
+ "./models/webhook": {
149
+ "source": "./src/models/webhook.ts",
150
+ "default": "./dist/models/webhook.mjs"
151
+ },
152
+ "./schema": {
153
+ "source": "./src/schema.ts",
154
+ "default": "./dist/schema.mjs"
155
+ },
156
+ "./types": {
157
+ "source": "./src/types.ts",
158
+ "default": "./dist/types.mjs"
159
+ },
160
+ "./package.json": "./package.json"
161
+ }
162
+ }