@siteping/adapter-drizzle 0.1.0 → 0.2.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/README.md +1 -1
- package/dist/{chunk-6G6UAERF.js → chunk-6DO75JHR.js} +80 -35
- package/dist/chunk-6DO75JHR.js.map +1 -0
- package/dist/deep-link.d.cts +10 -0
- package/dist/deep-link.d.ts +10 -0
- package/dist/errors.d.cts +46 -16
- package/dist/errors.d.ts +46 -16
- package/dist/i18n.d.cts +14 -10
- package/dist/i18n.d.ts +14 -10
- package/dist/index.d.cts +9 -8
- package/dist/index.d.ts +9 -8
- package/dist/libsql.cjs +86 -41
- package/dist/libsql.cjs.map +1 -1
- package/dist/libsql.d.cts +3 -2
- package/dist/libsql.d.ts +3 -2
- package/dist/libsql.js +12 -2
- package/dist/libsql.js.map +1 -1
- package/dist/logger.d.cts +30 -0
- package/dist/logger.d.ts +30 -0
- package/dist/pg.cjs +86 -41
- package/dist/pg.cjs.map +1 -1
- package/dist/pg.d.cts +3 -2
- package/dist/pg.d.ts +3 -2
- package/dist/pg.js +12 -2
- package/dist/pg.js.map +1 -1
- package/dist/schema.d.cts +31 -0
- package/dist/schema.d.ts +31 -0
- package/dist/screenshot-storage.d.cts +14 -17
- package/dist/screenshot-storage.d.ts +14 -17
- package/dist/siteping-core.d.cts +8 -5
- package/dist/siteping-core.d.ts +8 -5
- package/dist/store-helpers.d.cts +13 -9
- package/dist/store-helpers.d.ts +13 -9
- package/dist/text.d.cts +28 -0
- package/dist/text.d.ts +28 -0
- package/dist/types.d.cts +116 -39
- package/dist/types.d.ts +116 -39
- package/dist/wire.d.cts +6 -1
- package/dist/wire.d.ts +6 -1
- package/package.json +4 -4
- package/dist/chunk-6G6UAERF.js.map +0 -1
package/dist/pg.d.cts
CHANGED
|
@@ -3,7 +3,7 @@ export { DEFAULT_SITEPING_TABLE_NAMES, DrizzleStoreLogger } from './index.cjs';
|
|
|
3
3
|
import * as drizzle_orm_pg_core from 'drizzle-orm/pg-core';
|
|
4
4
|
import { PgDatabase, PgQueryResultHKT } from 'drizzle-orm/pg-core';
|
|
5
5
|
import { ScreenshotRegion, DiagnosticsSnapshot } from './siteping-core.cjs';
|
|
6
|
-
export { FeedbackRecord, ScreenshotStorage, SitepingStore, StoreDuplicateError, StoreLimitError, StoreNotFoundError, StorePersistenceError, isStorePersistence } from './siteping-core.cjs';
|
|
6
|
+
export { FeedbackRecord, ScreenshotStorage, SitepingStore, StoreDuplicateError, StoreLimitError, StoreNotFoundError, StorePersistenceError, StoreValueTooLongError, isStoreDuplicate, isStoreLimit, isStoreNotFound, isStorePersistence, isStoreValueTooLong } from './siteping-core.cjs';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Build the SitePing tables for PostgreSQL. Export them from your Drizzle
|
|
@@ -965,4 +965,5 @@ interface PgSitepingStoreOptions extends DrizzleStoreOptions {
|
|
|
965
965
|
*/
|
|
966
966
|
declare function createPgSitepingStore(db: AnyPgDatabase, options?: PgSitepingStoreOptions): DrizzleStore;
|
|
967
967
|
|
|
968
|
-
export {
|
|
968
|
+
export { DrizzleStore, DrizzleStoreOptions, SitepingTableNames, createPgSitepingStore, createSitepingPgTables };
|
|
969
|
+
export type { AnyPgDatabase, PgSitepingStoreOptions, SitepingPgTables };
|
package/dist/pg.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export { DEFAULT_SITEPING_TABLE_NAMES, DrizzleStoreLogger } from './index.js';
|
|
|
3
3
|
import * as drizzle_orm_pg_core from 'drizzle-orm/pg-core';
|
|
4
4
|
import { PgDatabase, PgQueryResultHKT } from 'drizzle-orm/pg-core';
|
|
5
5
|
import { ScreenshotRegion, DiagnosticsSnapshot } from './siteping-core.js';
|
|
6
|
-
export { FeedbackRecord, ScreenshotStorage, SitepingStore, StoreDuplicateError, StoreLimitError, StoreNotFoundError, StorePersistenceError, isStorePersistence } from './siteping-core.js';
|
|
6
|
+
export { FeedbackRecord, ScreenshotStorage, SitepingStore, StoreDuplicateError, StoreLimitError, StoreNotFoundError, StorePersistenceError, StoreValueTooLongError, isStoreDuplicate, isStoreLimit, isStoreNotFound, isStorePersistence, isStoreValueTooLong } from './siteping-core.js';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Build the SitePing tables for PostgreSQL. Export them from your Drizzle
|
|
@@ -965,4 +965,5 @@ interface PgSitepingStoreOptions extends DrizzleStoreOptions {
|
|
|
965
965
|
*/
|
|
966
966
|
declare function createPgSitepingStore(db: AnyPgDatabase, options?: PgSitepingStoreOptions): DrizzleStore;
|
|
967
967
|
|
|
968
|
-
export {
|
|
968
|
+
export { DrizzleStore, DrizzleStoreOptions, SitepingTableNames, createPgSitepingStore, createSitepingPgTables };
|
|
969
|
+
export type { AnyPgDatabase, PgSitepingStoreOptions, SitepingPgTables };
|
package/dist/pg.js
CHANGED
|
@@ -10,11 +10,16 @@ import {
|
|
|
10
10
|
StoreLimitError,
|
|
11
11
|
StoreNotFoundError,
|
|
12
12
|
StorePersistenceError,
|
|
13
|
+
StoreValueTooLongError,
|
|
13
14
|
buildFeedbackWhere,
|
|
14
15
|
deletedFeedbackColumns,
|
|
15
16
|
feedbackRecordColumns,
|
|
16
17
|
guardedCommentInsert,
|
|
18
|
+
isStoreDuplicate,
|
|
19
|
+
isStoreLimit,
|
|
20
|
+
isStoreNotFound,
|
|
17
21
|
isStorePersistence,
|
|
22
|
+
isStoreValueTooLong,
|
|
18
23
|
monotonicUpdatedAt,
|
|
19
24
|
newestFeedbackFirst,
|
|
20
25
|
primaryOf,
|
|
@@ -22,7 +27,7 @@ import {
|
|
|
22
27
|
selectValues,
|
|
23
28
|
toDeletedFeedbacks,
|
|
24
29
|
withSearchableMessage
|
|
25
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-6DO75JHR.js";
|
|
26
31
|
import {
|
|
27
32
|
DEFAULT_SITEPING_TABLE_NAMES
|
|
28
33
|
} from "./chunk-ICY2AW6S.js";
|
|
@@ -291,8 +296,13 @@ export {
|
|
|
291
296
|
StoreLimitError,
|
|
292
297
|
StoreNotFoundError,
|
|
293
298
|
StorePersistenceError,
|
|
299
|
+
StoreValueTooLongError,
|
|
294
300
|
createPgSitepingStore,
|
|
295
301
|
createSitepingPgTables,
|
|
296
|
-
|
|
302
|
+
isStoreDuplicate,
|
|
303
|
+
isStoreLimit,
|
|
304
|
+
isStoreNotFound,
|
|
305
|
+
isStorePersistence,
|
|
306
|
+
isStoreValueTooLong
|
|
297
307
|
};
|
|
298
308
|
//# sourceMappingURL=pg.js.map
|
package/dist/pg.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/pg/gateway.ts","../src/pg/tables.ts","../src/pg/index.ts"],"sourcesContent":["import { and, type Column, count, eq, getTableColumns, inArray, type SQL, sql, type WithSubquery } from \"drizzle-orm\";\nimport type { PgDatabase, PgQueryResultHKT } from \"drizzle-orm/pg-core\";\nimport { CASE_INSENSITIVE_LIKE_OPERATOR } from \"../constants/search.js\";\nimport {\n GREATEST_VALUE_FUNCTION,\n INSERTED_ANNOTATIONS_CTE_ALIAS,\n INSERTED_FEEDBACK_CTE_ALIAS,\n} from \"../constants/sql.js\";\nimport { guardedCommentInsert } from \"../shared/comments.js\";\nimport { deletedFeedbackColumns, toDeletedFeedbacks } from \"../shared/deletes.js\";\nimport { feedbackRecordColumns, newestFeedbackFirst } from \"../shared/feedbacks.js\";\nimport { buildFeedbackWhere, withSearchableMessage } from \"../shared/filters.js\";\nimport type { FeedbackFilter, SitepingSqlGateway } from \"../shared/gateway.js\";\nimport { recordColumns, selectValues } from \"../shared/rows.js\";\nimport { monotonicUpdatedAt } from \"../shared/timestamps.js\";\nimport type { SitepingPgTables } from \"./tables.js\";\n\n/**\n * Any Drizzle PostgreSQL database — node-postgres, postgres.js, Neon\n * (serverless / HTTP), Vercel Postgres, Supabase, PGlite… The store never\n * opens an interactive `db.transaction`: every write is a single statement,\n * so drivers without interactive transactions (Neon HTTP) work too.\n */\n// biome-ignore lint/suspicious/noExplicitAny: accepts every driver's query-result HKT and schema.\nexport type AnyPgDatabase = PgDatabase<PgQueryResultHKT, any, any>;\n\n/**\n * Type a bound parameter as its target column: parameters inside a `VALUES`\n * list carry no type of their own in PostgreSQL.\n */\nfunction castToColumnType(param: SQL, column: Column): SQL {\n return sql`CAST(${param} AS ${sql.raw(column.getSQLType())})`;\n}\n\n/** The PostgreSQL SQL behind `createPgSitepingStore`. */\nexport function createPgGateway(\n db: AnyPgDatabase,\n { sitepingFeedbacks, sitepingAnnotations, sitepingComments }: SitepingPgTables,\n): SitepingSqlGateway {\n const whereClause = (filter: FeedbackFilter) =>\n buildFeedbackWhere(sitepingFeedbacks, filter, CASE_INSENSITIVE_LIKE_OPERATOR.postgres);\n const feedbackColumns = feedbackRecordColumns(getTableColumns(sitepingFeedbacks));\n const commentColumns = recordColumns(getTableColumns(sitepingComments));\n /** Number of feedback rows matching a where clause — the page `total`. */\n const countMatching = async (where: ReturnType<typeof whereClause>): Promise<number> => {\n const [totals] = await db.select({ total: count() }).from(sitepingFeedbacks).where(where);\n return totals?.total ?? 0;\n };\n\n return {\n /**\n * One statement, no interactive transaction (Neon HTTP has none): the\n * feedback insert is a CTE, and the annotation insert selects from it —\n * so annotations land only with the feedback row, atomically.\n */\n async insertFeedback(feedback, annotations) {\n const insertedFeedback = db\n .$with(INSERTED_FEEDBACK_CTE_ALIAS)\n .as(\n db\n .insert(sitepingFeedbacks)\n .values(withSearchableMessage(feedback))\n .onConflictDoNothing({ target: sitepingFeedbacks.clientId })\n .returning({ id: sitepingFeedbacks.id }),\n );\n const statements: WithSubquery[] = [insertedFeedback];\n if (annotations.length > 0) {\n // Data-modifying CTEs always run to completion, even unreferenced.\n statements.push(\n db.$with(INSERTED_ANNOTATIONS_CTE_ALIAS).as(\n db.insert(sitepingAnnotations).select(\n selectValues(\n sitepingAnnotations,\n annotations.map((annotation, position) => ({ ...annotation, position })),\n sql`EXISTS (SELECT 1 FROM ${insertedFeedback})`,\n castToColumnType,\n ),\n ),\n ),\n );\n }\n const inserted = await db\n .with(...statements)\n .select({ id: insertedFeedback.id })\n .from(insertedFeedback);\n return inserted.length > 0;\n },\n async findFeedbacks(filter, { limit, offset }) {\n const where = whereClause(filter);\n const [rows, total] = await Promise.all([\n db\n .select(feedbackColumns)\n .from(sitepingFeedbacks)\n .where(where)\n .orderBy(...newestFeedbackFirst(sitepingFeedbacks.createdAt, sitepingFeedbacks.creationSequence))\n .limit(limit)\n .offset(offset),\n countMatching(where),\n ]);\n return { rows, total };\n },\n async countFeedbacks(filter) {\n return countMatching(whereClause(filter));\n },\n async findAnnotations(feedbackIds) {\n return db\n .select(recordColumns(getTableColumns(sitepingAnnotations)))\n .from(sitepingAnnotations)\n .where(inArray(sitepingAnnotations.feedbackId, [...feedbackIds]))\n .orderBy(sitepingAnnotations.createdAt, sitepingAnnotations.position);\n },\n async findComments(feedbackIds) {\n return db\n .select(commentColumns)\n .from(sitepingComments)\n .where(inArray(sitepingComments.feedbackId, [...feedbackIds]))\n .orderBy(sitepingComments.createdAt, sitepingComments.position);\n },\n async findCommentByClientId(clientId) {\n const [row] = await db\n .select(commentColumns)\n .from(sitepingComments)\n .where(eq(sitepingComments.clientId, clientId))\n .limit(1);\n return row ?? null;\n },\n async insertComment(comment, maxComments) {\n const { values, condition } = guardedCommentInsert(\n { feedbacks: sitepingFeedbacks, comments: sitepingComments },\n comment,\n maxComments,\n );\n const inserted = await db\n .insert(sitepingComments)\n .select(selectValues(sitepingComments, [values], condition, castToColumnType))\n .onConflictDoNothing({ target: sitepingComments.clientId })\n .returning({ id: sitepingComments.id });\n return inserted.length > 0;\n },\n async deleteComment(feedbackId, commentId) {\n const deleted = await db\n .delete(sitepingComments)\n .where(and(eq(sitepingComments.id, commentId), eq(sitepingComments.feedbackId, feedbackId)))\n .returning({ id: sitepingComments.id });\n return deleted.length > 0;\n },\n async findByClientId(clientId) {\n const [row] = await db\n .select(feedbackColumns)\n .from(sitepingFeedbacks)\n .where(eq(sitepingFeedbacks.clientId, clientId))\n .limit(1);\n return row ?? null;\n },\n async findProjectName(id) {\n const [row] = await db\n .select({ projectName: sitepingFeedbacks.projectName })\n .from(sitepingFeedbacks)\n .where(eq(sitepingFeedbacks.id, id))\n .limit(1);\n return row?.projectName ?? null;\n },\n async updateStatus(id, { status, resolvedAt, updatedAt }) {\n const [row] = await db\n .update(sitepingFeedbacks)\n .set({\n status,\n resolvedAt,\n updatedAt: monotonicUpdatedAt(sitepingFeedbacks, updatedAt, GREATEST_VALUE_FUNCTION.postgres),\n })\n .where(eq(sitepingFeedbacks.id, id))\n .returning(feedbackColumns);\n return row ?? null;\n },\n async deleteById(id, options) {\n const rows = await db\n .delete(sitepingFeedbacks)\n .where(eq(sitepingFeedbacks.id, id))\n .returning(deletedFeedbackColumns(sitepingFeedbacks, options));\n return rows.length > 0 ? toDeletedFeedbacks(rows) : null;\n },\n // Annotations and comments follow their feedback through the `ON DELETE CASCADE` foreign keys.\n async deleteByProject(projectName) {\n await db.delete(sitepingFeedbacks).where(eq(sitepingFeedbacks.projectName, projectName));\n },\n async deleteProjectChunk(projectName, chunkSize) {\n const chunkIds = db\n .select({ id: sitepingFeedbacks.id })\n .from(sitepingFeedbacks)\n .where(eq(sitepingFeedbacks.projectName, projectName))\n .orderBy(sitepingFeedbacks.id)\n .limit(chunkSize);\n const rows = await db\n .delete(sitepingFeedbacks)\n .where(inArray(sitepingFeedbacks.id, chunkIds))\n .returning(deletedFeedbackColumns(sitepingFeedbacks, { collectScreenshotUrls: true }));\n return toDeletedFeedbacks(rows);\n },\n async findReferencedScreenshotUrls(screenshotUrls) {\n const rows = await db\n .selectDistinct({ screenshotUrl: sitepingFeedbacks.screenshotUrl })\n .from(sitepingFeedbacks)\n .where(inArray(sitepingFeedbacks.screenshotUrl, [...screenshotUrls]));\n return new Set(rows.flatMap((row) => (row.screenshotUrl === null ? [] : [row.screenshotUrl])));\n },\n };\n}\n","import type {\n CommentAuthorRole,\n DiagnosticsSnapshot,\n FeedbackStatus,\n FeedbackType,\n ScreenshotRegion,\n} from \"@siteping/core\";\nimport {\n bigint,\n doublePrecision,\n foreignKey,\n getTableConfig,\n index,\n integer,\n jsonb,\n type PgTable,\n pgTable,\n text,\n timestamp,\n uniqueIndex,\n} from \"drizzle-orm/pg-core\";\nimport { DRIZZLE_STORE_MESSAGE_PREFIX } from \"../constants/errors.js\";\nimport { POSTGRES_IDENTIFIER_MAX_BYTES } from \"../constants/sql.js\";\nimport { DEFAULT_SITEPING_TABLE_NAMES, type SitepingTableNames } from \"../constants/table-names.js\";\n\n/**\n * Refuse identifiers PostgreSQL would truncate. It cuts every name past\n * {@link POSTGRES_IDENTIFIER_MAX_BYTES} bytes without an error, so each later\n * `drizzle-kit push` or `generate` sees the declared name missing and drops\n * and recreates the index or foreign key — or two names cut to the same\n * prefix and the migration fails.\n *\n * @param tables - The tables, whose own, index and foreign-key names are checked.\n */\nfunction assertIdentifiersFitPostgres(tables: readonly PgTable[]): void {\n const encoder = new TextEncoder();\n for (const table of tables) {\n const { name, indexes, foreignKeys } = getTableConfig(table);\n const identifiers = [\n name,\n ...indexes.flatMap((tableIndex) => tableIndex.config.name ?? []),\n ...foreignKeys.map((key) => key.getName()),\n ];\n for (const identifier of identifiers) {\n const bytes = encoder.encode(identifier).length;\n if (bytes > POSTGRES_IDENTIFIER_MAX_BYTES) {\n throw new RangeError(\n `${DRIZZLE_STORE_MESSAGE_PREFIX}: \"${identifier}\" is ${bytes} bytes long, over PostgreSQL's ${POSTGRES_IDENTIFIER_MAX_BYTES}-byte identifier limit — pass shorter table names to createSitepingPgTables`,\n );\n }\n }\n }\n}\n\n/**\n * Build the SitePing tables for PostgreSQL. Export them from your Drizzle\n * schema so `drizzle-kit generate` / `push` creates them:\n *\n * ```ts\n * // db/schema.ts\n * export const { sitepingFeedbacks, sitepingAnnotations, sitepingComments } = createSitepingPgTables();\n * ```\n *\n * Index and foreign-key names derive from the table names, which leaves the\n * feedback table at most 36 bytes, annotations 47 and comments 42.\n *\n * @throws `RangeError` when a table, index or foreign-key name would pass\n * PostgreSQL's 63-byte identifier limit.\n */\nexport function createSitepingPgTables(names: SitepingTableNames = DEFAULT_SITEPING_TABLE_NAMES) {\n const sitepingFeedbacks = pgTable(\n names.feedbacks,\n {\n id: text(\"id\").primaryKey(),\n projectName: text(\"project_name\").notNull(),\n type: text(\"type\").$type<FeedbackType>().notNull(),\n message: text(\"message\").notNull(),\n status: text(\"status\").$type<FeedbackStatus>().notNull().default(\"open\"),\n url: text(\"url\").notNull(),\n urlPattern: text(\"url_pattern\"),\n screenshotUrl: text(\"screenshot_url\"),\n screenshotRegion: jsonb(\"screenshot_region\").$type<ScreenshotRegion>(),\n diagnostics: jsonb(\"diagnostics\").$type<DiagnosticsSnapshot>(),\n viewport: text(\"viewport\").notNull(),\n userAgent: text(\"user_agent\").notNull(),\n authorName: text(\"author_name\").notNull(),\n authorEmail: text(\"author_email\").notNull(),\n clientId: text(\"client_id\").notNull(),\n resolvedAt: timestamp(\"resolved_at\", { withTimezone: true, precision: 3 }),\n createdAt: timestamp(\"created_at\", { withTimezone: true, precision: 3 }).notNull().defaultNow(),\n updatedAt: timestamp(\"updated_at\", { withTimezone: true, precision: 3 }).notNull().defaultNow(),\n // Database-wide insertion ordinal: breaks `createdAt` ties between rows created in the\n // same millisecond, by any store instance or process — or by the host application\n // inserting directly — so \"newest first\" and offset pages stay stable. The database\n // assigns it on every insert. Internal — never part of the feedback record.\n creationSequence: bigint(\"creation_sequence\", { mode: \"number\" }).generatedAlwaysAsIdentity(),\n // `message` lowercased in JavaScript (`String.prototype.toLowerCase`, Unicode-aware), the\n // column the text search reads: `ILIKE` folds case with the collation / `LC_CTYPE`, so under\n // `C` `Échec` would not match `échec`. The store fills it on insert (a feedback's message\n // never changes); rows written by the host application stay NULL and the search falls\n // back to `message ILIKE`. Internal — never part of the feedback record.\n messageSearch: text(\"message_search\"),\n },\n (table) => [\n uniqueIndex(`${names.feedbacks}_client_id_key`).on(table.clientId),\n index(`${names.feedbacks}_project_status_created_idx`).on(table.projectName, table.status, table.createdAt),\n index(`${names.feedbacks}_project_url_idx`).on(table.projectName, table.url),\n ],\n );\n\n const sitepingAnnotations = pgTable(\n names.annotations,\n {\n id: text(\"id\").primaryKey(),\n feedbackId: text(\"feedback_id\").notNull(),\n cssSelector: text(\"css_selector\").notNull(),\n xpath: text(\"xpath\").notNull(),\n textSnippet: text(\"text_snippet\").notNull(),\n elementTag: text(\"element_tag\").notNull(),\n elementId: text(\"element_id\"),\n textPrefix: text(\"text_prefix\").notNull(),\n textSuffix: text(\"text_suffix\").notNull(),\n fingerprint: text(\"fingerprint\").notNull(),\n neighborText: text(\"neighbor_text\").notNull(),\n anchorKey: text(\"anchor_key\"),\n xPct: doublePrecision(\"x_pct\").notNull(),\n yPct: doublePrecision(\"y_pct\").notNull(),\n wPct: doublePrecision(\"w_pct\").notNull(),\n hPct: doublePrecision(\"h_pct\").notNull(),\n scrollX: doublePrecision(\"scroll_x\").notNull(),\n scrollY: doublePrecision(\"scroll_y\").notNull(),\n viewportW: integer(\"viewport_w\").notNull(),\n viewportH: integer(\"viewport_h\").notNull(),\n devicePixelRatio: doublePrecision(\"device_pixel_ratio\").notNull().default(1),\n // Submission index within the feedback: `annotations[0]` is the primary anchor, and\n // every annotation of a feedback shares one `createdAt`. Rows the host application\n // inserts without it default to 0 and keep their `createdAt` order.\n position: integer(\"position\").notNull().default(0),\n createdAt: timestamp(\"created_at\", { withTimezone: true, precision: 3 }).notNull().defaultNow(),\n },\n (table) => [\n foreignKey({\n name: `${names.annotations}_feedback_id_fk`,\n columns: [table.feedbackId],\n foreignColumns: [sitepingFeedbacks.id],\n }).onDelete(\"cascade\"),\n index(`${names.annotations}_feedback_id_idx`).on(table.feedbackId),\n ],\n );\n\n const sitepingComments = pgTable(\n names.comments,\n {\n id: text(\"id\").primaryKey(),\n feedbackId: text(\"feedback_id\").notNull(),\n body: text(\"body\").notNull(),\n authorName: text(\"author_name\").notNull(),\n authorEmail: text(\"author_email\").notNull(),\n authorRole: text(\"author_role\").$type<CommentAuthorRole>().notNull().default(\"client\"),\n clientId: text(\"client_id\").notNull(),\n createdAt: timestamp(\"created_at\", { withTimezone: true, precision: 3 }).notNull().defaultNow(),\n // Posting index within the thread, assigned by the insert itself: breaks `createdAt`\n // ties so a thread reads in posting order. Rows the host application inserts without\n // it default to 0 and keep their `createdAt` order.\n position: integer(\"position\").notNull().default(0),\n },\n (table) => [\n foreignKey({\n name: `${names.comments}_feedback_id_fk`,\n columns: [table.feedbackId],\n foreignColumns: [sitepingFeedbacks.id],\n }).onDelete(\"cascade\"),\n uniqueIndex(`${names.comments}_client_id_key`).on(table.clientId),\n index(`${names.comments}_feedback_created_idx`).on(table.feedbackId, table.createdAt),\n ],\n );\n\n assertIdentifiersFitPostgres([sitepingFeedbacks, sitepingAnnotations, sitepingComments]);\n return { sitepingFeedbacks, sitepingAnnotations, sitepingComments };\n}\n\nexport type SitepingPgTables = ReturnType<typeof createSitepingPgTables>;\n","import { primaryOf } from \"../shared/replicas.js\";\nimport { DrizzleSitepingStore, type DrizzleStore, type DrizzleStoreOptions } from \"../shared/store.js\";\nimport { type AnyPgDatabase, createPgGateway } from \"./gateway.js\";\nimport { createSitepingPgTables, type SitepingPgTables } from \"./tables.js\";\n\nexport type { FeedbackRecord, ScreenshotStorage, SitepingStore } from \"@siteping/core\";\nexport {\n isStorePersistence,\n StoreDuplicateError,\n StoreLimitError,\n StoreNotFoundError,\n StorePersistenceError,\n} from \"@siteping/core\";\nexport { DEFAULT_SITEPING_TABLE_NAMES, type SitepingTableNames } from \"../constants/table-names.js\";\nexport type { DrizzleStore, DrizzleStoreLogger, DrizzleStoreOptions } from \"../shared/store.js\";\nexport type { AnyPgDatabase } from \"./gateway.js\";\nexport { createSitepingPgTables, type SitepingPgTables } from \"./tables.js\";\n\nexport interface PgSitepingStoreOptions extends DrizzleStoreOptions {\n /** Tables built with `createSitepingPgTables` — pass them when you customized the names. */\n tables?: SitepingPgTables | undefined;\n}\n\n/**\n * `SitepingStore` on PostgreSQL through Drizzle ORM. Given a database built\n * with `withReplicas`, it runs everything on the primary.\n *\n * @example\n * ```ts\n * import { drizzle } from \"drizzle-orm/node-postgres\";\n * import { createPgSitepingStore } from \"@siteping/adapter-drizzle/pg\";\n *\n * const store = createPgSitepingStore(drizzle(process.env.DATABASE_URL!), { screenshotStorage });\n * ```\n */\nexport function createPgSitepingStore(db: AnyPgDatabase, options: PgSitepingStoreOptions = {}): DrizzleStore {\n const { tables = createSitepingPgTables(), ...storeOptions } = options;\n return new DrizzleSitepingStore(createPgGateway(primaryOf(db), tables), storeOptions);\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/pg/gateway.ts","../src/pg/tables.ts","../src/pg/index.ts"],"sourcesContent":["import { and, type Column, count, eq, getTableColumns, inArray, type SQL, sql, type WithSubquery } from \"drizzle-orm\";\nimport type { PgDatabase, PgQueryResultHKT } from \"drizzle-orm/pg-core\";\nimport { CASE_INSENSITIVE_LIKE_OPERATOR } from \"../constants/search.js\";\nimport {\n GREATEST_VALUE_FUNCTION,\n INSERTED_ANNOTATIONS_CTE_ALIAS,\n INSERTED_FEEDBACK_CTE_ALIAS,\n} from \"../constants/sql.js\";\nimport { guardedCommentInsert } from \"../shared/comments.js\";\nimport { deletedFeedbackColumns, toDeletedFeedbacks } from \"../shared/deletes.js\";\nimport { feedbackRecordColumns, newestFeedbackFirst } from \"../shared/feedbacks.js\";\nimport { buildFeedbackWhere, withSearchableMessage } from \"../shared/filters.js\";\nimport type { FeedbackFilter, SitepingSqlGateway } from \"../shared/gateway.js\";\nimport { recordColumns, selectValues } from \"../shared/rows.js\";\nimport { monotonicUpdatedAt } from \"../shared/timestamps.js\";\nimport type { SitepingPgTables } from \"./tables.js\";\n\n/**\n * Any Drizzle PostgreSQL database — node-postgres, postgres.js, Neon\n * (serverless / HTTP), Vercel Postgres, Supabase, PGlite… The store never\n * opens an interactive `db.transaction`: every write is a single statement,\n * so drivers without interactive transactions (Neon HTTP) work too.\n */\n// biome-ignore lint/suspicious/noExplicitAny: accepts every driver's query-result HKT and schema.\nexport type AnyPgDatabase = PgDatabase<PgQueryResultHKT, any, any>;\n\n/**\n * Type a bound parameter as its target column: parameters inside a `VALUES`\n * list carry no type of their own in PostgreSQL.\n */\nfunction castToColumnType(param: SQL, column: Column): SQL {\n return sql`CAST(${param} AS ${sql.raw(column.getSQLType())})`;\n}\n\n/** The PostgreSQL SQL behind `createPgSitepingStore`. */\nexport function createPgGateway(\n db: AnyPgDatabase,\n { sitepingFeedbacks, sitepingAnnotations, sitepingComments }: SitepingPgTables,\n): SitepingSqlGateway {\n const whereClause = (filter: FeedbackFilter) =>\n buildFeedbackWhere(sitepingFeedbacks, filter, CASE_INSENSITIVE_LIKE_OPERATOR.postgres);\n const feedbackColumns = feedbackRecordColumns(getTableColumns(sitepingFeedbacks));\n const commentColumns = recordColumns(getTableColumns(sitepingComments));\n /** Number of feedback rows matching a where clause — the page `total`. */\n const countMatching = async (where: ReturnType<typeof whereClause>): Promise<number> => {\n const [totals] = await db.select({ total: count() }).from(sitepingFeedbacks).where(where);\n return totals?.total ?? 0;\n };\n\n return {\n /**\n * One statement, no interactive transaction (Neon HTTP has none): the\n * feedback insert is a CTE, and the annotation insert selects from it —\n * so annotations land only with the feedback row, atomically.\n */\n async insertFeedback(feedback, annotations) {\n const insertedFeedback = db\n .$with(INSERTED_FEEDBACK_CTE_ALIAS)\n .as(\n db\n .insert(sitepingFeedbacks)\n .values(withSearchableMessage(feedback))\n .onConflictDoNothing({ target: sitepingFeedbacks.clientId })\n .returning({ id: sitepingFeedbacks.id }),\n );\n const statements: WithSubquery[] = [insertedFeedback];\n if (annotations.length > 0) {\n // Data-modifying CTEs always run to completion, even unreferenced.\n statements.push(\n db.$with(INSERTED_ANNOTATIONS_CTE_ALIAS).as(\n db.insert(sitepingAnnotations).select(\n selectValues(\n sitepingAnnotations,\n annotations.map((annotation, position) => ({ ...annotation, position })),\n sql`EXISTS (SELECT 1 FROM ${insertedFeedback})`,\n castToColumnType,\n ),\n ),\n ),\n );\n }\n const inserted = await db\n .with(...statements)\n .select({ id: insertedFeedback.id })\n .from(insertedFeedback);\n return inserted.length > 0;\n },\n async findFeedbacks(filter, { limit, offset }) {\n const where = whereClause(filter);\n const [rows, total] = await Promise.all([\n db\n .select(feedbackColumns)\n .from(sitepingFeedbacks)\n .where(where)\n .orderBy(...newestFeedbackFirst(sitepingFeedbacks.createdAt, sitepingFeedbacks.creationSequence))\n .limit(limit)\n .offset(offset),\n countMatching(where),\n ]);\n return { rows, total };\n },\n async countFeedbacks(filter) {\n return countMatching(whereClause(filter));\n },\n async findAnnotations(feedbackIds) {\n return db\n .select(recordColumns(getTableColumns(sitepingAnnotations)))\n .from(sitepingAnnotations)\n .where(inArray(sitepingAnnotations.feedbackId, [...feedbackIds]))\n .orderBy(sitepingAnnotations.createdAt, sitepingAnnotations.position);\n },\n async findComments(feedbackIds) {\n return db\n .select(commentColumns)\n .from(sitepingComments)\n .where(inArray(sitepingComments.feedbackId, [...feedbackIds]))\n .orderBy(sitepingComments.createdAt, sitepingComments.position);\n },\n async findCommentByClientId(clientId) {\n const [row] = await db\n .select(commentColumns)\n .from(sitepingComments)\n .where(eq(sitepingComments.clientId, clientId))\n .limit(1);\n return row ?? null;\n },\n async insertComment(comment, maxComments) {\n const { values, condition } = guardedCommentInsert(\n { feedbacks: sitepingFeedbacks, comments: sitepingComments },\n comment,\n maxComments,\n );\n const inserted = await db\n .insert(sitepingComments)\n .select(selectValues(sitepingComments, [values], condition, castToColumnType))\n .onConflictDoNothing({ target: sitepingComments.clientId })\n .returning({ id: sitepingComments.id });\n return inserted.length > 0;\n },\n async deleteComment(feedbackId, commentId) {\n const deleted = await db\n .delete(sitepingComments)\n .where(and(eq(sitepingComments.id, commentId), eq(sitepingComments.feedbackId, feedbackId)))\n .returning({ id: sitepingComments.id });\n return deleted.length > 0;\n },\n async findByClientId(clientId) {\n const [row] = await db\n .select(feedbackColumns)\n .from(sitepingFeedbacks)\n .where(eq(sitepingFeedbacks.clientId, clientId))\n .limit(1);\n return row ?? null;\n },\n async findProjectName(id) {\n const [row] = await db\n .select({ projectName: sitepingFeedbacks.projectName })\n .from(sitepingFeedbacks)\n .where(eq(sitepingFeedbacks.id, id))\n .limit(1);\n return row?.projectName ?? null;\n },\n async updateStatus(id, { status, resolvedAt, updatedAt }) {\n const [row] = await db\n .update(sitepingFeedbacks)\n .set({\n status,\n resolvedAt,\n updatedAt: monotonicUpdatedAt(sitepingFeedbacks, updatedAt, GREATEST_VALUE_FUNCTION.postgres),\n })\n .where(eq(sitepingFeedbacks.id, id))\n .returning(feedbackColumns);\n return row ?? null;\n },\n async deleteById(id, options) {\n const rows = await db\n .delete(sitepingFeedbacks)\n .where(eq(sitepingFeedbacks.id, id))\n .returning(deletedFeedbackColumns(sitepingFeedbacks, options));\n return rows.length > 0 ? toDeletedFeedbacks(rows) : null;\n },\n // Annotations and comments follow their feedback through the `ON DELETE CASCADE` foreign keys.\n async deleteByProject(projectName) {\n await db.delete(sitepingFeedbacks).where(eq(sitepingFeedbacks.projectName, projectName));\n },\n async deleteProjectChunk(projectName, chunkSize) {\n const chunkIds = db\n .select({ id: sitepingFeedbacks.id })\n .from(sitepingFeedbacks)\n .where(eq(sitepingFeedbacks.projectName, projectName))\n .orderBy(sitepingFeedbacks.id)\n .limit(chunkSize);\n const rows = await db\n .delete(sitepingFeedbacks)\n .where(inArray(sitepingFeedbacks.id, chunkIds))\n .returning(deletedFeedbackColumns(sitepingFeedbacks, { collectScreenshotUrls: true }));\n return toDeletedFeedbacks(rows);\n },\n async findReferencedScreenshotUrls(screenshotUrls) {\n const rows = await db\n .selectDistinct({ screenshotUrl: sitepingFeedbacks.screenshotUrl })\n .from(sitepingFeedbacks)\n .where(inArray(sitepingFeedbacks.screenshotUrl, [...screenshotUrls]));\n return new Set(rows.flatMap((row) => (row.screenshotUrl === null ? [] : [row.screenshotUrl])));\n },\n };\n}\n","import type {\n CommentAuthorRole,\n DiagnosticsSnapshot,\n FeedbackStatus,\n FeedbackType,\n ScreenshotRegion,\n} from \"@siteping/core\";\nimport {\n bigint,\n doublePrecision,\n foreignKey,\n getTableConfig,\n index,\n integer,\n jsonb,\n type PgTable,\n pgTable,\n text,\n timestamp,\n uniqueIndex,\n} from \"drizzle-orm/pg-core\";\nimport { DRIZZLE_STORE_MESSAGE_PREFIX } from \"../constants/errors.js\";\nimport { POSTGRES_IDENTIFIER_MAX_BYTES } from \"../constants/sql.js\";\nimport { DEFAULT_SITEPING_TABLE_NAMES, type SitepingTableNames } from \"../constants/table-names.js\";\n\n/**\n * Refuse identifiers PostgreSQL would truncate. It cuts every name past\n * {@link POSTGRES_IDENTIFIER_MAX_BYTES} bytes without an error, so each later\n * `drizzle-kit push` or `generate` sees the declared name missing and drops\n * and recreates the index or foreign key — or two names cut to the same\n * prefix and the migration fails.\n *\n * @param tables - The tables, whose own, index and foreign-key names are checked.\n */\nfunction assertIdentifiersFitPostgres(tables: readonly PgTable[]): void {\n const encoder = new TextEncoder();\n for (const table of tables) {\n const { name, indexes, foreignKeys } = getTableConfig(table);\n const identifiers = [\n name,\n ...indexes.flatMap((tableIndex) => tableIndex.config.name ?? []),\n ...foreignKeys.map((key) => key.getName()),\n ];\n for (const identifier of identifiers) {\n const bytes = encoder.encode(identifier).length;\n if (bytes > POSTGRES_IDENTIFIER_MAX_BYTES) {\n throw new RangeError(\n `${DRIZZLE_STORE_MESSAGE_PREFIX}: \"${identifier}\" is ${bytes} bytes long, over PostgreSQL's ${POSTGRES_IDENTIFIER_MAX_BYTES}-byte identifier limit — pass shorter table names to createSitepingPgTables`,\n );\n }\n }\n }\n}\n\n/**\n * Build the SitePing tables for PostgreSQL. Export them from your Drizzle\n * schema so `drizzle-kit generate` / `push` creates them:\n *\n * ```ts\n * // db/schema.ts\n * export const { sitepingFeedbacks, sitepingAnnotations, sitepingComments } = createSitepingPgTables();\n * ```\n *\n * Index and foreign-key names derive from the table names, which leaves the\n * feedback table at most 36 bytes, annotations 47 and comments 42.\n *\n * @throws `RangeError` when a table, index or foreign-key name would pass\n * PostgreSQL's 63-byte identifier limit.\n */\nexport function createSitepingPgTables(names: SitepingTableNames = DEFAULT_SITEPING_TABLE_NAMES) {\n const sitepingFeedbacks = pgTable(\n names.feedbacks,\n {\n id: text(\"id\").primaryKey(),\n projectName: text(\"project_name\").notNull(),\n type: text(\"type\").$type<FeedbackType>().notNull(),\n message: text(\"message\").notNull(),\n status: text(\"status\").$type<FeedbackStatus>().notNull().default(\"open\"),\n url: text(\"url\").notNull(),\n urlPattern: text(\"url_pattern\"),\n screenshotUrl: text(\"screenshot_url\"),\n screenshotRegion: jsonb(\"screenshot_region\").$type<ScreenshotRegion>(),\n diagnostics: jsonb(\"diagnostics\").$type<DiagnosticsSnapshot>(),\n viewport: text(\"viewport\").notNull(),\n userAgent: text(\"user_agent\").notNull(),\n authorName: text(\"author_name\").notNull(),\n authorEmail: text(\"author_email\").notNull(),\n clientId: text(\"client_id\").notNull(),\n resolvedAt: timestamp(\"resolved_at\", { withTimezone: true, precision: 3 }),\n createdAt: timestamp(\"created_at\", { withTimezone: true, precision: 3 }).notNull().defaultNow(),\n updatedAt: timestamp(\"updated_at\", { withTimezone: true, precision: 3 }).notNull().defaultNow(),\n // Database-wide insertion ordinal: breaks `createdAt` ties between rows created in the\n // same millisecond, by any store instance or process — or by the host application\n // inserting directly — so \"newest first\" and offset pages stay stable. The database\n // assigns it on every insert. Internal — never part of the feedback record.\n creationSequence: bigint(\"creation_sequence\", { mode: \"number\" }).generatedAlwaysAsIdentity(),\n // `message` lowercased in JavaScript (`String.prototype.toLowerCase`, Unicode-aware), the\n // column the text search reads: `ILIKE` folds case with the collation / `LC_CTYPE`, so under\n // `C` `Échec` would not match `échec`. The store fills it on insert (a feedback's message\n // never changes); rows written by the host application stay NULL and the search falls\n // back to `message ILIKE`. Internal — never part of the feedback record.\n messageSearch: text(\"message_search\"),\n },\n (table) => [\n uniqueIndex(`${names.feedbacks}_client_id_key`).on(table.clientId),\n index(`${names.feedbacks}_project_status_created_idx`).on(table.projectName, table.status, table.createdAt),\n index(`${names.feedbacks}_project_url_idx`).on(table.projectName, table.url),\n ],\n );\n\n const sitepingAnnotations = pgTable(\n names.annotations,\n {\n id: text(\"id\").primaryKey(),\n feedbackId: text(\"feedback_id\").notNull(),\n cssSelector: text(\"css_selector\").notNull(),\n xpath: text(\"xpath\").notNull(),\n textSnippet: text(\"text_snippet\").notNull(),\n elementTag: text(\"element_tag\").notNull(),\n elementId: text(\"element_id\"),\n textPrefix: text(\"text_prefix\").notNull(),\n textSuffix: text(\"text_suffix\").notNull(),\n fingerprint: text(\"fingerprint\").notNull(),\n neighborText: text(\"neighbor_text\").notNull(),\n anchorKey: text(\"anchor_key\"),\n xPct: doublePrecision(\"x_pct\").notNull(),\n yPct: doublePrecision(\"y_pct\").notNull(),\n wPct: doublePrecision(\"w_pct\").notNull(),\n hPct: doublePrecision(\"h_pct\").notNull(),\n scrollX: doublePrecision(\"scroll_x\").notNull(),\n scrollY: doublePrecision(\"scroll_y\").notNull(),\n viewportW: integer(\"viewport_w\").notNull(),\n viewportH: integer(\"viewport_h\").notNull(),\n devicePixelRatio: doublePrecision(\"device_pixel_ratio\").notNull().default(1),\n // Submission index within the feedback: `annotations[0]` is the primary anchor, and\n // every annotation of a feedback shares one `createdAt`. Rows the host application\n // inserts without it default to 0 and keep their `createdAt` order.\n position: integer(\"position\").notNull().default(0),\n createdAt: timestamp(\"created_at\", { withTimezone: true, precision: 3 }).notNull().defaultNow(),\n },\n (table) => [\n foreignKey({\n name: `${names.annotations}_feedback_id_fk`,\n columns: [table.feedbackId],\n foreignColumns: [sitepingFeedbacks.id],\n }).onDelete(\"cascade\"),\n index(`${names.annotations}_feedback_id_idx`).on(table.feedbackId),\n ],\n );\n\n const sitepingComments = pgTable(\n names.comments,\n {\n id: text(\"id\").primaryKey(),\n feedbackId: text(\"feedback_id\").notNull(),\n body: text(\"body\").notNull(),\n authorName: text(\"author_name\").notNull(),\n authorEmail: text(\"author_email\").notNull(),\n authorRole: text(\"author_role\").$type<CommentAuthorRole>().notNull().default(\"client\"),\n clientId: text(\"client_id\").notNull(),\n createdAt: timestamp(\"created_at\", { withTimezone: true, precision: 3 }).notNull().defaultNow(),\n // Posting index within the thread, assigned by the insert itself: breaks `createdAt`\n // ties so a thread reads in posting order. Rows the host application inserts without\n // it default to 0 and keep their `createdAt` order.\n position: integer(\"position\").notNull().default(0),\n },\n (table) => [\n foreignKey({\n name: `${names.comments}_feedback_id_fk`,\n columns: [table.feedbackId],\n foreignColumns: [sitepingFeedbacks.id],\n }).onDelete(\"cascade\"),\n uniqueIndex(`${names.comments}_client_id_key`).on(table.clientId),\n index(`${names.comments}_feedback_created_idx`).on(table.feedbackId, table.createdAt),\n ],\n );\n\n assertIdentifiersFitPostgres([sitepingFeedbacks, sitepingAnnotations, sitepingComments]);\n return { sitepingFeedbacks, sitepingAnnotations, sitepingComments };\n}\n\nexport type SitepingPgTables = ReturnType<typeof createSitepingPgTables>;\n","import { primaryOf } from \"../shared/replicas.js\";\nimport { DrizzleSitepingStore, type DrizzleStore, type DrizzleStoreOptions } from \"../shared/store.js\";\nimport { type AnyPgDatabase, createPgGateway } from \"./gateway.js\";\nimport { createSitepingPgTables, type SitepingPgTables } from \"./tables.js\";\n\nexport type { FeedbackRecord, ScreenshotStorage, SitepingStore } from \"@siteping/core\";\nexport {\n isStoreDuplicate,\n isStoreLimit,\n isStoreNotFound,\n isStorePersistence,\n isStoreValueTooLong,\n StoreDuplicateError,\n StoreLimitError,\n StoreNotFoundError,\n StorePersistenceError,\n StoreValueTooLongError,\n} from \"@siteping/core\";\nexport { DEFAULT_SITEPING_TABLE_NAMES, type SitepingTableNames } from \"../constants/table-names.js\";\nexport type { DrizzleStore, DrizzleStoreLogger, DrizzleStoreOptions } from \"../shared/store.js\";\nexport type { AnyPgDatabase } from \"./gateway.js\";\nexport { createSitepingPgTables, type SitepingPgTables } from \"./tables.js\";\n\nexport interface PgSitepingStoreOptions extends DrizzleStoreOptions {\n /** Tables built with `createSitepingPgTables` — pass them when you customized the names. */\n tables?: SitepingPgTables | undefined;\n}\n\n/**\n * `SitepingStore` on PostgreSQL through Drizzle ORM. Given a database built\n * with `withReplicas`, it runs everything on the primary.\n *\n * @example\n * ```ts\n * import { drizzle } from \"drizzle-orm/node-postgres\";\n * import { createPgSitepingStore } from \"@siteping/adapter-drizzle/pg\";\n *\n * const store = createPgSitepingStore(drizzle(process.env.DATABASE_URL!), { screenshotStorage });\n * ```\n */\nexport function createPgSitepingStore(db: AnyPgDatabase, options: PgSitepingStoreOptions = {}): DrizzleStore {\n const { tables = createSitepingPgTables(), ...storeOptions } = options;\n return new DrizzleSitepingStore(createPgGateway(primaryOf(db), tables), storeOptions);\n}\n"],"mappings":"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","names":[]}
|
package/dist/schema.d.cts
CHANGED
|
@@ -52,6 +52,13 @@ export interface FieldDef {
|
|
|
52
52
|
isUnique?: boolean;
|
|
53
53
|
/** Prisma native type attribute (e.g. "Text" for @db.Text) — used for MySQL compatibility on long strings */
|
|
54
54
|
nativeType?: PrismaNativeType;
|
|
55
|
+
/**
|
|
56
|
+
* Longest value the HTTP handler accepts for this column, in characters —
|
|
57
|
+
* set on every `String` column filled from a request body, and checked
|
|
58
|
+
* against the handler's own caps. The CLI sizes the column from it where a
|
|
59
|
+
* plain `String` is shorter: on MySQL, a `VARCHAR(191)`.
|
|
60
|
+
*/
|
|
61
|
+
maxLength?: number;
|
|
55
62
|
/** Prisma @updatedAt attribute */
|
|
56
63
|
isUpdatedAt?: boolean;
|
|
57
64
|
}
|
|
@@ -82,6 +89,7 @@ declare const _SITEPING_MODELS: {
|
|
|
82
89
|
};
|
|
83
90
|
readonly projectName: {
|
|
84
91
|
readonly type: "String";
|
|
92
|
+
readonly maxLength: 191;
|
|
85
93
|
};
|
|
86
94
|
readonly type: {
|
|
87
95
|
readonly type: "String";
|
|
@@ -89,6 +97,7 @@ declare const _SITEPING_MODELS: {
|
|
|
89
97
|
readonly message: {
|
|
90
98
|
readonly type: "String";
|
|
91
99
|
readonly nativeType: "Text";
|
|
100
|
+
readonly maxLength: 5000;
|
|
92
101
|
};
|
|
93
102
|
readonly status: {
|
|
94
103
|
readonly type: "String";
|
|
@@ -96,15 +105,18 @@ declare const _SITEPING_MODELS: {
|
|
|
96
105
|
};
|
|
97
106
|
readonly url: {
|
|
98
107
|
readonly type: "String";
|
|
108
|
+
readonly maxLength: 2000;
|
|
99
109
|
};
|
|
100
110
|
readonly urlPattern: {
|
|
101
111
|
readonly type: "String";
|
|
102
112
|
readonly optional: true;
|
|
113
|
+
readonly maxLength: 2000;
|
|
103
114
|
};
|
|
104
115
|
readonly screenshotUrl: {
|
|
105
116
|
readonly type: "String";
|
|
106
117
|
readonly optional: true;
|
|
107
118
|
readonly nativeType: "Text";
|
|
119
|
+
readonly maxLength: 1500000;
|
|
108
120
|
};
|
|
109
121
|
readonly screenshotRegion: {
|
|
110
122
|
readonly type: "Json";
|
|
@@ -116,19 +128,24 @@ declare const _SITEPING_MODELS: {
|
|
|
116
128
|
};
|
|
117
129
|
readonly viewport: {
|
|
118
130
|
readonly type: "String";
|
|
131
|
+
readonly maxLength: 50;
|
|
119
132
|
};
|
|
120
133
|
readonly userAgent: {
|
|
121
134
|
readonly type: "String";
|
|
135
|
+
readonly maxLength: 500;
|
|
122
136
|
};
|
|
123
137
|
readonly authorName: {
|
|
124
138
|
readonly type: "String";
|
|
139
|
+
readonly maxLength: 200;
|
|
125
140
|
};
|
|
126
141
|
readonly authorEmail: {
|
|
127
142
|
readonly type: "String";
|
|
143
|
+
readonly maxLength: 200;
|
|
128
144
|
};
|
|
129
145
|
readonly clientId: {
|
|
130
146
|
readonly type: "String";
|
|
131
147
|
readonly isUnique: true;
|
|
148
|
+
readonly maxLength: 191;
|
|
132
149
|
};
|
|
133
150
|
readonly resolvedAt: {
|
|
134
151
|
readonly type: "DateTime";
|
|
@@ -188,40 +205,50 @@ declare const _SITEPING_MODELS: {
|
|
|
188
205
|
readonly cssSelector: {
|
|
189
206
|
readonly type: "String";
|
|
190
207
|
readonly nativeType: "Text";
|
|
208
|
+
readonly maxLength: 2000;
|
|
191
209
|
};
|
|
192
210
|
readonly xpath: {
|
|
193
211
|
readonly type: "String";
|
|
194
212
|
readonly nativeType: "Text";
|
|
213
|
+
readonly maxLength: 2000;
|
|
195
214
|
};
|
|
196
215
|
readonly textSnippet: {
|
|
197
216
|
readonly type: "String";
|
|
198
217
|
readonly nativeType: "Text";
|
|
218
|
+
readonly maxLength: 500;
|
|
199
219
|
};
|
|
200
220
|
readonly elementTag: {
|
|
201
221
|
readonly type: "String";
|
|
222
|
+
readonly maxLength: 191;
|
|
202
223
|
};
|
|
203
224
|
readonly elementId: {
|
|
204
225
|
readonly type: "String";
|
|
205
226
|
readonly optional: true;
|
|
227
|
+
readonly maxLength: 191;
|
|
206
228
|
};
|
|
207
229
|
readonly textPrefix: {
|
|
208
230
|
readonly type: "String";
|
|
209
231
|
readonly nativeType: "Text";
|
|
232
|
+
readonly maxLength: 200;
|
|
210
233
|
};
|
|
211
234
|
readonly textSuffix: {
|
|
212
235
|
readonly type: "String";
|
|
213
236
|
readonly nativeType: "Text";
|
|
237
|
+
readonly maxLength: 200;
|
|
214
238
|
};
|
|
215
239
|
readonly fingerprint: {
|
|
216
240
|
readonly type: "String";
|
|
241
|
+
readonly maxLength: 200;
|
|
217
242
|
};
|
|
218
243
|
readonly neighborText: {
|
|
219
244
|
readonly type: "String";
|
|
220
245
|
readonly nativeType: "Text";
|
|
246
|
+
readonly maxLength: 500;
|
|
221
247
|
};
|
|
222
248
|
readonly anchorKey: {
|
|
223
249
|
readonly type: "String";
|
|
224
250
|
readonly optional: true;
|
|
251
|
+
readonly maxLength: 200;
|
|
225
252
|
};
|
|
226
253
|
readonly xPct: {
|
|
227
254
|
readonly type: "Float";
|
|
@@ -283,12 +310,15 @@ declare const _SITEPING_MODELS: {
|
|
|
283
310
|
readonly body: {
|
|
284
311
|
readonly type: "String";
|
|
285
312
|
readonly nativeType: "Text";
|
|
313
|
+
readonly maxLength: 5000;
|
|
286
314
|
};
|
|
287
315
|
readonly authorName: {
|
|
288
316
|
readonly type: "String";
|
|
317
|
+
readonly maxLength: 200;
|
|
289
318
|
};
|
|
290
319
|
readonly authorEmail: {
|
|
291
320
|
readonly type: "String";
|
|
321
|
+
readonly maxLength: 200;
|
|
292
322
|
};
|
|
293
323
|
readonly authorRole: {
|
|
294
324
|
readonly type: "String";
|
|
@@ -297,6 +327,7 @@ declare const _SITEPING_MODELS: {
|
|
|
297
327
|
readonly clientId: {
|
|
298
328
|
readonly type: "String";
|
|
299
329
|
readonly isUnique: true;
|
|
330
|
+
readonly maxLength: 191;
|
|
300
331
|
};
|
|
301
332
|
readonly createdAt: {
|
|
302
333
|
readonly type: "DateTime";
|
package/dist/schema.d.ts
CHANGED
|
@@ -52,6 +52,13 @@ export interface FieldDef {
|
|
|
52
52
|
isUnique?: boolean;
|
|
53
53
|
/** Prisma native type attribute (e.g. "Text" for @db.Text) — used for MySQL compatibility on long strings */
|
|
54
54
|
nativeType?: PrismaNativeType;
|
|
55
|
+
/**
|
|
56
|
+
* Longest value the HTTP handler accepts for this column, in characters —
|
|
57
|
+
* set on every `String` column filled from a request body, and checked
|
|
58
|
+
* against the handler's own caps. The CLI sizes the column from it where a
|
|
59
|
+
* plain `String` is shorter: on MySQL, a `VARCHAR(191)`.
|
|
60
|
+
*/
|
|
61
|
+
maxLength?: number;
|
|
55
62
|
/** Prisma @updatedAt attribute */
|
|
56
63
|
isUpdatedAt?: boolean;
|
|
57
64
|
}
|
|
@@ -82,6 +89,7 @@ declare const _SITEPING_MODELS: {
|
|
|
82
89
|
};
|
|
83
90
|
readonly projectName: {
|
|
84
91
|
readonly type: "String";
|
|
92
|
+
readonly maxLength: 191;
|
|
85
93
|
};
|
|
86
94
|
readonly type: {
|
|
87
95
|
readonly type: "String";
|
|
@@ -89,6 +97,7 @@ declare const _SITEPING_MODELS: {
|
|
|
89
97
|
readonly message: {
|
|
90
98
|
readonly type: "String";
|
|
91
99
|
readonly nativeType: "Text";
|
|
100
|
+
readonly maxLength: 5000;
|
|
92
101
|
};
|
|
93
102
|
readonly status: {
|
|
94
103
|
readonly type: "String";
|
|
@@ -96,15 +105,18 @@ declare const _SITEPING_MODELS: {
|
|
|
96
105
|
};
|
|
97
106
|
readonly url: {
|
|
98
107
|
readonly type: "String";
|
|
108
|
+
readonly maxLength: 2000;
|
|
99
109
|
};
|
|
100
110
|
readonly urlPattern: {
|
|
101
111
|
readonly type: "String";
|
|
102
112
|
readonly optional: true;
|
|
113
|
+
readonly maxLength: 2000;
|
|
103
114
|
};
|
|
104
115
|
readonly screenshotUrl: {
|
|
105
116
|
readonly type: "String";
|
|
106
117
|
readonly optional: true;
|
|
107
118
|
readonly nativeType: "Text";
|
|
119
|
+
readonly maxLength: 1500000;
|
|
108
120
|
};
|
|
109
121
|
readonly screenshotRegion: {
|
|
110
122
|
readonly type: "Json";
|
|
@@ -116,19 +128,24 @@ declare const _SITEPING_MODELS: {
|
|
|
116
128
|
};
|
|
117
129
|
readonly viewport: {
|
|
118
130
|
readonly type: "String";
|
|
131
|
+
readonly maxLength: 50;
|
|
119
132
|
};
|
|
120
133
|
readonly userAgent: {
|
|
121
134
|
readonly type: "String";
|
|
135
|
+
readonly maxLength: 500;
|
|
122
136
|
};
|
|
123
137
|
readonly authorName: {
|
|
124
138
|
readonly type: "String";
|
|
139
|
+
readonly maxLength: 200;
|
|
125
140
|
};
|
|
126
141
|
readonly authorEmail: {
|
|
127
142
|
readonly type: "String";
|
|
143
|
+
readonly maxLength: 200;
|
|
128
144
|
};
|
|
129
145
|
readonly clientId: {
|
|
130
146
|
readonly type: "String";
|
|
131
147
|
readonly isUnique: true;
|
|
148
|
+
readonly maxLength: 191;
|
|
132
149
|
};
|
|
133
150
|
readonly resolvedAt: {
|
|
134
151
|
readonly type: "DateTime";
|
|
@@ -188,40 +205,50 @@ declare const _SITEPING_MODELS: {
|
|
|
188
205
|
readonly cssSelector: {
|
|
189
206
|
readonly type: "String";
|
|
190
207
|
readonly nativeType: "Text";
|
|
208
|
+
readonly maxLength: 2000;
|
|
191
209
|
};
|
|
192
210
|
readonly xpath: {
|
|
193
211
|
readonly type: "String";
|
|
194
212
|
readonly nativeType: "Text";
|
|
213
|
+
readonly maxLength: 2000;
|
|
195
214
|
};
|
|
196
215
|
readonly textSnippet: {
|
|
197
216
|
readonly type: "String";
|
|
198
217
|
readonly nativeType: "Text";
|
|
218
|
+
readonly maxLength: 500;
|
|
199
219
|
};
|
|
200
220
|
readonly elementTag: {
|
|
201
221
|
readonly type: "String";
|
|
222
|
+
readonly maxLength: 191;
|
|
202
223
|
};
|
|
203
224
|
readonly elementId: {
|
|
204
225
|
readonly type: "String";
|
|
205
226
|
readonly optional: true;
|
|
227
|
+
readonly maxLength: 191;
|
|
206
228
|
};
|
|
207
229
|
readonly textPrefix: {
|
|
208
230
|
readonly type: "String";
|
|
209
231
|
readonly nativeType: "Text";
|
|
232
|
+
readonly maxLength: 200;
|
|
210
233
|
};
|
|
211
234
|
readonly textSuffix: {
|
|
212
235
|
readonly type: "String";
|
|
213
236
|
readonly nativeType: "Text";
|
|
237
|
+
readonly maxLength: 200;
|
|
214
238
|
};
|
|
215
239
|
readonly fingerprint: {
|
|
216
240
|
readonly type: "String";
|
|
241
|
+
readonly maxLength: 200;
|
|
217
242
|
};
|
|
218
243
|
readonly neighborText: {
|
|
219
244
|
readonly type: "String";
|
|
220
245
|
readonly nativeType: "Text";
|
|
246
|
+
readonly maxLength: 500;
|
|
221
247
|
};
|
|
222
248
|
readonly anchorKey: {
|
|
223
249
|
readonly type: "String";
|
|
224
250
|
readonly optional: true;
|
|
251
|
+
readonly maxLength: 200;
|
|
225
252
|
};
|
|
226
253
|
readonly xPct: {
|
|
227
254
|
readonly type: "Float";
|
|
@@ -283,12 +310,15 @@ declare const _SITEPING_MODELS: {
|
|
|
283
310
|
readonly body: {
|
|
284
311
|
readonly type: "String";
|
|
285
312
|
readonly nativeType: "Text";
|
|
313
|
+
readonly maxLength: 5000;
|
|
286
314
|
};
|
|
287
315
|
readonly authorName: {
|
|
288
316
|
readonly type: "String";
|
|
317
|
+
readonly maxLength: 200;
|
|
289
318
|
};
|
|
290
319
|
readonly authorEmail: {
|
|
291
320
|
readonly type: "String";
|
|
321
|
+
readonly maxLength: 200;
|
|
292
322
|
};
|
|
293
323
|
readonly authorRole: {
|
|
294
324
|
readonly type: "String";
|
|
@@ -297,6 +327,7 @@ declare const _SITEPING_MODELS: {
|
|
|
297
327
|
readonly clientId: {
|
|
298
328
|
readonly type: "String";
|
|
299
329
|
readonly isUnique: true;
|
|
330
|
+
readonly maxLength: 191;
|
|
300
331
|
};
|
|
301
332
|
readonly createdAt: {
|
|
302
333
|
readonly type: "DateTime";
|
|
@@ -42,26 +42,24 @@ export interface ScreenshotStorage {
|
|
|
42
42
|
* Adapters call this synchronously inside `createFeedback` — keep it
|
|
43
43
|
* fast or move to a queue if needed.
|
|
44
44
|
*
|
|
45
|
-
* `ctx.feedbackId`
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
45
|
+
* `ctx.feedbackId` is the id the feedback is stored under. The record does
|
|
46
|
+
* not exist yet, so the store draws it before the upload — fresh for each
|
|
47
|
+
* create attempt, never client-supplied — and inserts the row under it.
|
|
48
|
+
* Two submissions of one `clientId` therefore upload under two ids, and
|
|
49
|
+
* only one of them is stored.
|
|
49
50
|
*
|
|
50
51
|
* **URL ownership:** every call must return a URL no other call returns —
|
|
51
52
|
* key the object by a fresh random value (`crypto.randomUUID()`, as the
|
|
52
53
|
* example does), never by `ctx.feedbackId` alone nor by a content hash.
|
|
53
54
|
* Adapters treat each URL as the property of the one record that stores it
|
|
54
55
|
* and may pass it to `delete` once that record is deleted, or once its
|
|
55
|
-
* create lost a race:
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
* by it is rewritten by every replay — a retry, or anyone who learns the
|
|
59
|
-
* id — and a URL shared by several records lets deleting one remove the
|
|
60
|
-
* object another still points at.
|
|
56
|
+
* create lost a race: the object of the submission that is not stored is
|
|
57
|
+
* deleted, and a URL shared by several records would let deleting one
|
|
58
|
+
* remove the object another still points at.
|
|
61
59
|
*
|
|
62
|
-
* **Security note:**
|
|
63
|
-
*
|
|
64
|
-
*
|
|
60
|
+
* **Security note:** the bundled stores generate `ctx.feedbackId`, but a
|
|
61
|
+
* third-party one may not honour the contract: sanitize it before using it
|
|
62
|
+
* in a filesystem path or an object key.
|
|
65
63
|
*/
|
|
66
64
|
upload(dataUrl: string, ctx: {
|
|
67
65
|
feedbackId: string;
|
|
@@ -72,10 +70,9 @@ export interface ScreenshotStorage {
|
|
|
72
70
|
/**
|
|
73
71
|
* Optional cleanup hook called when the feedback is deleted, and for an
|
|
74
72
|
* object uploaded by a create whose record was not stored: one rejected
|
|
75
|
-
* because its `clientId` is already stored, or
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
* is kept. Receives only URLs returned by {@link ScreenshotStorage.upload}.
|
|
73
|
+
* because its `clientId` is already stored, or one whose insert failed. An
|
|
74
|
+
* object a stored row still references (a storage that broke URL
|
|
75
|
+
* ownership) is kept. Receives only URLs returned by {@link ScreenshotStorage.upload}.
|
|
79
76
|
* Adapters call this best-effort and swallow errors — orphaned objects are
|
|
80
77
|
* preferred over failed deletes.
|
|
81
78
|
*/
|
|
@@ -42,26 +42,24 @@ export interface ScreenshotStorage {
|
|
|
42
42
|
* Adapters call this synchronously inside `createFeedback` — keep it
|
|
43
43
|
* fast or move to a queue if needed.
|
|
44
44
|
*
|
|
45
|
-
* `ctx.feedbackId`
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
45
|
+
* `ctx.feedbackId` is the id the feedback is stored under. The record does
|
|
46
|
+
* not exist yet, so the store draws it before the upload — fresh for each
|
|
47
|
+
* create attempt, never client-supplied — and inserts the row under it.
|
|
48
|
+
* Two submissions of one `clientId` therefore upload under two ids, and
|
|
49
|
+
* only one of them is stored.
|
|
49
50
|
*
|
|
50
51
|
* **URL ownership:** every call must return a URL no other call returns —
|
|
51
52
|
* key the object by a fresh random value (`crypto.randomUUID()`, as the
|
|
52
53
|
* example does), never by `ctx.feedbackId` alone nor by a content hash.
|
|
53
54
|
* Adapters treat each URL as the property of the one record that stores it
|
|
54
55
|
* and may pass it to `delete` once that record is deleted, or once its
|
|
55
|
-
* create lost a race:
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
* by it is rewritten by every replay — a retry, or anyone who learns the
|
|
59
|
-
* id — and a URL shared by several records lets deleting one remove the
|
|
60
|
-
* object another still points at.
|
|
56
|
+
* create lost a race: the object of the submission that is not stored is
|
|
57
|
+
* deleted, and a URL shared by several records would let deleting one
|
|
58
|
+
* remove the object another still points at.
|
|
61
59
|
*
|
|
62
|
-
* **Security note:**
|
|
63
|
-
*
|
|
64
|
-
*
|
|
60
|
+
* **Security note:** the bundled stores generate `ctx.feedbackId`, but a
|
|
61
|
+
* third-party one may not honour the contract: sanitize it before using it
|
|
62
|
+
* in a filesystem path or an object key.
|
|
65
63
|
*/
|
|
66
64
|
upload(dataUrl: string, ctx: {
|
|
67
65
|
feedbackId: string;
|
|
@@ -72,10 +70,9 @@ export interface ScreenshotStorage {
|
|
|
72
70
|
/**
|
|
73
71
|
* Optional cleanup hook called when the feedback is deleted, and for an
|
|
74
72
|
* object uploaded by a create whose record was not stored: one rejected
|
|
75
|
-
* because its `clientId` is already stored, or
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
* is kept. Receives only URLs returned by {@link ScreenshotStorage.upload}.
|
|
73
|
+
* because its `clientId` is already stored, or one whose insert failed. An
|
|
74
|
+
* object a stored row still references (a storage that broke URL
|
|
75
|
+
* ownership) is kept. Receives only URLs returned by {@link ScreenshotStorage.upload}.
|
|
79
76
|
* Adapters call this best-effort and swallow errors — orphaned objects are
|
|
80
77
|
* preferred over failed deletes.
|
|
81
78
|
*/
|