@ykurilov/agent-chat-images 0.56.0 → 0.58.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/CHANGELOG.md +32 -0
- package/dist/postgres.js +6 -11
- package/dist/postgres.js.map +1 -1
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
# @ykurilov/agent-chat-images
|
|
2
2
|
|
|
3
|
+
## 0.58.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 760962e: Хранилища пакетов на PostgreSQL можно открывать одновременно на совсем пустой базе. Раньше пакеты разом создавали
|
|
8
|
+
схему `agent_chat`, проигравший падал на `CREATE SCHEMA IF NOT EXISTS`, и его хранилище не работало до перезапуска.
|
|
9
|
+
Теперь схема создаётся под общей блокировкой всех пакетов, она держится только на создании схемы. Миграции идут под
|
|
10
|
+
своей блокировкой пакета: разные пакеты друг друга не ждут, взаимной блокировки нет. Столкновение «такое уже есть»
|
|
11
|
+
повторяется само. Новое в core: `migratePostgresSchema({ connect, lockKey, migrate })` — для своих пакетов на той же
|
|
12
|
+
схеме.
|
|
13
|
+
|
|
14
|
+
### Как обновить приложение
|
|
15
|
+
|
|
16
|
+
Ничего делать не нужно.
|
|
17
|
+
- Updated dependencies [332d90e]
|
|
18
|
+
- Updated dependencies [2ed600d]
|
|
19
|
+
- Updated dependencies [760962e]
|
|
20
|
+
- @ykurilov/agent-chat-core@0.58.0
|
|
21
|
+
- @ykurilov/agent-chat-attachments@0.58.0
|
|
22
|
+
- @ykurilov/agent-chat-react@0.58.0
|
|
23
|
+
|
|
24
|
+
## 0.57.0
|
|
25
|
+
|
|
26
|
+
### Patch Changes
|
|
27
|
+
|
|
28
|
+
- Updated dependencies [ade99cc]
|
|
29
|
+
- Updated dependencies [86afa20]
|
|
30
|
+
- Updated dependencies [624c10c]
|
|
31
|
+
- @ykurilov/agent-chat-core@0.57.0
|
|
32
|
+
- @ykurilov/agent-chat-attachments@0.57.0
|
|
33
|
+
- @ykurilov/agent-chat-react@0.57.0
|
|
34
|
+
|
|
3
35
|
## 0.56.0
|
|
4
36
|
|
|
5
37
|
### Patch Changes
|
package/dist/postgres.js
CHANGED
|
@@ -17,6 +17,7 @@ import { fileURLToPath } from "url";
|
|
|
17
17
|
import { and, asc, eq, isNotNull, isNull, lt, ne, or } from "drizzle-orm";
|
|
18
18
|
import { drizzle } from "drizzle-orm/node-postgres";
|
|
19
19
|
import { migrate } from "drizzle-orm/node-postgres/migrator";
|
|
20
|
+
import { migratePostgresSchema } from "@ykurilov/agent-chat-core";
|
|
20
21
|
|
|
21
22
|
// src/image-queue/postgres-schema.ts
|
|
22
23
|
import { sql } from "drizzle-orm";
|
|
@@ -115,17 +116,11 @@ function filterOf(filter) {
|
|
|
115
116
|
);
|
|
116
117
|
}
|
|
117
118
|
async function migrateImageJobsToLatest(pool, migrationsFolder) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
} finally {
|
|
124
|
-
await client.query("select pg_advisory_unlock($1)", [IMAGE_JOBS_MIGRATION_LOCK_KEY]);
|
|
125
|
-
}
|
|
126
|
-
} finally {
|
|
127
|
-
client.release();
|
|
128
|
-
}
|
|
119
|
+
await migratePostgresSchema({
|
|
120
|
+
connect: () => pool.connect(),
|
|
121
|
+
lockKey: IMAGE_JOBS_MIGRATION_LOCK_KEY,
|
|
122
|
+
migrate: (client) => migrate(drizzle(client), { migrationsFolder, migrationsSchema: "agent_chat", migrationsTable: IMAGE_JOBS_MIGRATIONS_TABLE })
|
|
123
|
+
});
|
|
129
124
|
}
|
|
130
125
|
function createPostgresImageJobStore(pool, migrationsFolder) {
|
|
131
126
|
const db = drizzle(pool);
|
package/dist/postgres.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/postgres.ts","../src/image-queue/postgres-image-job-store.ts","../src/image-queue/postgres-schema.ts"],"sourcesContent":["import { fileURLToPath } from \"node:url\";\nimport type { Pool } from \"pg\";\nimport type { ImageJobStore } from \"./image-queue/image-job-store\";\nimport { createPostgresImageJobStore } from \"./image-queue/postgres-image-job-store\";\n\nexport { IMAGE_JOBS_MIGRATION_LOCK_KEY, IMAGE_JOBS_MIGRATIONS_TABLE } from \"./image-queue/migration-settings\";\nexport { imageJobs } from \"./image-queue/postgres-schema\";\n\n/** Миграции лежат в пакете рядом с `dist`: `drizzle/postgres`. */\nconst MIGRATIONS_FOLDER = fileURLToPath(new URL(\"../drizzle/postgres\", import.meta.url));\n\n/**\n * Задачи картинок в PostgreSQL на пуле приложения (схема `agent_chat`, та же база, что у хранилища чатов).\n * Таблица приводится к последней миграции при первом обращении. Пул закрывает приложение.\n */\nexport function postgresImageJobStore(pool: Pool): ImageJobStore {\n return createPostgresImageJobStore(pool, MIGRATIONS_FOLDER);\n}\n","import { and, asc, eq, isNotNull, isNull, lt, ne, or } from \"drizzle-orm\";\nimport type { SQL } from \"drizzle-orm\";\nimport { drizzle } from \"drizzle-orm/node-postgres\";\nimport { migrate } from \"drizzle-orm/node-postgres/migrator\";\nimport type { Pool } from \"pg\";\nimport { applyImageJobPatch, findErrorCause, ImageJobKeyTakenError, imageJobBody, isOpenImageJob } from \"./image-job-store\";\nimport type { ImageJobFilter, ImageJobRecord, ImageJobStore } from \"./image-job-store\";\nimport { IMAGE_JOBS_MIGRATION_LOCK_KEY, IMAGE_JOBS_MIGRATIONS_TABLE } from \"./migration-settings\";\nimport { imageJobs } from \"./postgres-schema\";\n\ntype Row = typeof imageJobs.$inferSelect;\n\nconst toDate = (iso: string | undefined) => (iso === undefined ? null : new Date(iso));\nconst toIso = (date: Date | null) => (date === null ? undefined : date.toISOString());\n\n/** PostgreSQL не хранит символ NUL ни в тексте, ни в jsonb: убираем его до записи. */\nfunction withoutNul<T>(value: T): T {\n if (typeof value === \"string\") return value.replaceAll(\"\\u0000\", \"\") as T;\n if (Array.isArray(value)) return value.map(withoutNul) as T;\n if (value !== null && typeof value === \"object\") return Object.fromEntries(Object.entries(value).map(([key, item]) => [withoutNul(key), withoutNul(item)])) as T;\n return value;\n}\n\nfunction toRecord(row: Row): ImageJobRecord {\n const record: Record<string, unknown> = {\n ...row.body,\n id: row.id,\n key: row.key,\n ownerId: row.ownerId,\n chatId: row.chatId,\n group: row.group ?? undefined,\n status: row.status,\n active: row.active,\n leaseOwner: row.leaseOwner ?? undefined,\n leaseUntil: toIso(row.leaseUntil),\n version: row.version,\n createdAt: toIso(row.createdAt),\n updatedAt: toIso(row.updatedAt),\n finishedAt: toIso(row.finishedAt),\n };\n for (const [field, value] of Object.entries(record)) if (value === undefined) delete record[field];\n return record as unknown as ImageJobRecord;\n}\n\nfunction toRow(job: ImageJobRecord): Row {\n const clean = withoutNul(job);\n return {\n id: clean.id,\n key: clean.key,\n ownerId: clean.ownerId,\n chatId: clean.chatId,\n group: clean.group ?? null,\n status: clean.status,\n active: clean.active,\n open: isOpenImageJob(clean),\n leaseOwner: clean.leaseOwner ?? null,\n leaseUntil: toDate(clean.leaseUntil),\n version: clean.version,\n createdAt: toDate(clean.createdAt)!,\n updatedAt: toDate(clean.updatedAt)!,\n finishedAt: toDate(clean.finishedAt),\n body: imageJobBody(clean),\n };\n}\n\n/** Вторая открытая задача на место: её не пускает уникальный индекс `image_jobs_open_key_idx`. */\nfunction placeTaken(error: unknown, job: ImageJobRecord): never {\n if (findErrorCause(error, (cause) => cause.code === \"23505\" && cause.constraint === \"image_jobs_open_key_idx\")) throw new ImageJobKeyTakenError(job.ownerId, job.key);\n throw error;\n}\n\nfunction filterOf(filter: ImageJobFilter): SQL | undefined {\n return and(\n filter.ownerId !== undefined ? eq(imageJobs.ownerId, filter.ownerId) : undefined,\n filter.chatId !== undefined ? eq(imageJobs.chatId, filter.chatId) : undefined,\n filter.key !== undefined ? eq(imageJobs.key, filter.key) : undefined,\n filter.group !== undefined ? eq(imageJobs.group, filter.group) : undefined,\n );\n}\n\n/** Приводит таблицу задач к последней миграции под блокировкой базы: серверы, стартующие разом, не мешают друг другу. */\nasync function migrateImageJobsToLatest(pool: Pool, migrationsFolder: string): Promise<void> {\n const client = await pool.connect();\n try {\n await client.query(\"select pg_advisory_lock($1)\", [IMAGE_JOBS_MIGRATION_LOCK_KEY]);\n try {\n await migrate(drizzle(client), { migrationsFolder, migrationsSchema: \"agent_chat\", migrationsTable: IMAGE_JOBS_MIGRATIONS_TABLE });\n } finally {\n await client.query(\"select pg_advisory_unlock($1)\", [IMAGE_JOBS_MIGRATION_LOCK_KEY]);\n }\n } finally {\n client.release();\n }\n}\n\n/**\n * Задачи картинок в PostgreSQL (схема `agent_chat`) на пуле приложения. Миграция — при первом обращении, каждая операция\n * её дожидается. Правка — условное `update … where version = …`: из двух одновременных пройдёт одна. Пул закрывает\n * приложение.\n */\nexport function createPostgresImageJobStore(pool: Pool, migrationsFolder: string): ImageJobStore {\n const db = drizzle(pool);\n const ready = migrateImageJobsToLatest(pool, migrationsFolder);\n // Ошибка миграции придёт в каждую операцию; без этого она всплыла бы ещё и как необработанная.\n ready.catch(() => {});\n\n async function get(id: string): Promise<ImageJobRecord | null> {\n await ready;\n const [row] = await db.select().from(imageJobs).where(eq(imageJobs.id, id));\n return row ? toRecord(row) : null;\n }\n\n return {\n async insert(job) {\n await ready;\n await db\n .insert(imageJobs)\n .values(toRow(job))\n .catch((error: unknown) => placeTaken(error, job));\n },\n get,\n async list(filter) {\n await ready;\n const rows = await db.select().from(imageJobs).where(filterOf(filter)).orderBy(asc(imageJobs.createdAt), asc(imageJobs.id));\n return rows.map(toRecord);\n },\n async update(id, patch, expectedVersion) {\n const job = await get(id);\n if (!job || job.version !== expectedVersion) return null;\n const next = { ...applyImageJobPatch(job, patch), version: expectedVersion + 1 };\n const { id: _id, ...columns } = toRow(next);\n const [row] = await db\n .update(imageJobs)\n .set(columns)\n .where(and(eq(imageJobs.id, id), eq(imageJobs.version, expectedVersion)))\n .returning()\n .catch((error: unknown) => placeTaken(error, next));\n return row ? toRecord(row) : null;\n },\n async listResumable(now) {\n await ready;\n const rows = await db\n .select()\n .from(imageJobs)\n .where(and(eq(imageJobs.active, true), or(isNull(imageJobs.leaseUntil), lt(imageJobs.leaseUntil, new Date(now)))))\n .orderBy(asc(imageJobs.createdAt));\n return rows.map(toRecord);\n },\n async deleteFinishedBefore(before) {\n await ready;\n const deleted = await db\n .delete(imageJobs)\n .where(and(eq(imageJobs.active, false), ne(imageJobs.status, \"generating\"), isNotNull(imageJobs.finishedAt), lt(imageJobs.finishedAt, new Date(before))))\n .returning({ id: imageJobs.id });\n return deleted.length;\n },\n async close() {\n // Дожидаемся миграции: пул приложения можно закрывать сразу после.\n await ready.catch(() => {});\n },\n };\n}\n","import { sql } from \"drizzle-orm\";\nimport { boolean, index, integer, jsonb, pgSchema, text, timestamp, uniqueIndex } from \"drizzle-orm/pg-core\";\nimport type { ImageJobStatus } from \"./image-job-store\";\n\n/** Та же схема базы, что у хранилища чатов: у приложения одна база и одна схема `agent_chat`. */\nconst agentChatSchema = pgSchema(\"agent_chat\");\n\nconst moment = (name: string) => timestamp(name, { mode: \"date\", precision: 3, withTimezone: true });\n\nexport const imageJobs = agentChatSchema.table(\n \"image_jobs\",\n {\n id: text(\"id\").primaryKey(),\n key: text(\"key\").notNull(),\n ownerId: text(\"owner_id\").notNull(),\n chatId: text(\"chat_id\").notNull(),\n group: text(\"group_name\"),\n status: text(\"status\").$type<ImageJobStatus>().notNull(),\n /** У задачи есть работа: её подбирает старт сервера. */\n active: boolean(\"active\").notNull(),\n /** Задача открыта (генерация или позднее окно): открытая на место — одна, это держит индекс ниже. */\n open: boolean(\"open\").notNull(),\n leaseOwner: text(\"lease_owner\"),\n leaseUntil: moment(\"lease_until\"),\n version: integer(\"version\").notNull(),\n createdAt: moment(\"created_at\").notNull(),\n updatedAt: moment(\"updated_at\").notNull(),\n finishedAt: moment(\"finished_at\"),\n /** Остальное: описание, назначение, данные приложения, шаги с деньгами. */\n body: jsonb(\"body\").$type<Record<string, unknown>>().notNull(),\n },\n (table) => [\n index(\"image_jobs_owner_key_idx\").on(table.ownerId, table.key),\n uniqueIndex(\"image_jobs_open_key_idx\").on(table.ownerId, table.key).where(sql`open`),\n index(\"image_jobs_group_idx\").on(table.group),\n index(\"image_jobs_active_lease_idx\").on(table.active, table.leaseUntil),\n ],\n);\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/postgres.ts","../src/image-queue/postgres-image-job-store.ts","../src/image-queue/postgres-schema.ts"],"sourcesContent":["import { fileURLToPath } from \"node:url\";\nimport type { Pool } from \"pg\";\nimport type { ImageJobStore } from \"./image-queue/image-job-store\";\nimport { createPostgresImageJobStore } from \"./image-queue/postgres-image-job-store\";\n\nexport { IMAGE_JOBS_MIGRATION_LOCK_KEY, IMAGE_JOBS_MIGRATIONS_TABLE } from \"./image-queue/migration-settings\";\nexport { imageJobs } from \"./image-queue/postgres-schema\";\n\n/** Миграции лежат в пакете рядом с `dist`: `drizzle/postgres`. */\nconst MIGRATIONS_FOLDER = fileURLToPath(new URL(\"../drizzle/postgres\", import.meta.url));\n\n/**\n * Задачи картинок в PostgreSQL на пуле приложения (схема `agent_chat`, та же база, что у хранилища чатов).\n * Таблица приводится к последней миграции при первом обращении. Пул закрывает приложение.\n */\nexport function postgresImageJobStore(pool: Pool): ImageJobStore {\n return createPostgresImageJobStore(pool, MIGRATIONS_FOLDER);\n}\n","import { and, asc, eq, isNotNull, isNull, lt, ne, or } from \"drizzle-orm\";\nimport type { SQL } from \"drizzle-orm\";\nimport { drizzle } from \"drizzle-orm/node-postgres\";\nimport { migrate } from \"drizzle-orm/node-postgres/migrator\";\nimport { migratePostgresSchema } from \"@ykurilov/agent-chat-core\";\nimport type { Pool } from \"pg\";\nimport { applyImageJobPatch, findErrorCause, ImageJobKeyTakenError, imageJobBody, isOpenImageJob } from \"./image-job-store\";\nimport type { ImageJobFilter, ImageJobRecord, ImageJobStore } from \"./image-job-store\";\nimport { IMAGE_JOBS_MIGRATION_LOCK_KEY, IMAGE_JOBS_MIGRATIONS_TABLE } from \"./migration-settings\";\nimport { imageJobs } from \"./postgres-schema\";\n\ntype Row = typeof imageJobs.$inferSelect;\n\nconst toDate = (iso: string | undefined) => (iso === undefined ? null : new Date(iso));\nconst toIso = (date: Date | null) => (date === null ? undefined : date.toISOString());\n\n/** PostgreSQL не хранит символ NUL ни в тексте, ни в jsonb: убираем его до записи. */\nfunction withoutNul<T>(value: T): T {\n if (typeof value === \"string\") return value.replaceAll(\"\\u0000\", \"\") as T;\n if (Array.isArray(value)) return value.map(withoutNul) as T;\n if (value !== null && typeof value === \"object\") return Object.fromEntries(Object.entries(value).map(([key, item]) => [withoutNul(key), withoutNul(item)])) as T;\n return value;\n}\n\nfunction toRecord(row: Row): ImageJobRecord {\n const record: Record<string, unknown> = {\n ...row.body,\n id: row.id,\n key: row.key,\n ownerId: row.ownerId,\n chatId: row.chatId,\n group: row.group ?? undefined,\n status: row.status,\n active: row.active,\n leaseOwner: row.leaseOwner ?? undefined,\n leaseUntil: toIso(row.leaseUntil),\n version: row.version,\n createdAt: toIso(row.createdAt),\n updatedAt: toIso(row.updatedAt),\n finishedAt: toIso(row.finishedAt),\n };\n for (const [field, value] of Object.entries(record)) if (value === undefined) delete record[field];\n return record as unknown as ImageJobRecord;\n}\n\nfunction toRow(job: ImageJobRecord): Row {\n const clean = withoutNul(job);\n return {\n id: clean.id,\n key: clean.key,\n ownerId: clean.ownerId,\n chatId: clean.chatId,\n group: clean.group ?? null,\n status: clean.status,\n active: clean.active,\n open: isOpenImageJob(clean),\n leaseOwner: clean.leaseOwner ?? null,\n leaseUntil: toDate(clean.leaseUntil),\n version: clean.version,\n createdAt: toDate(clean.createdAt)!,\n updatedAt: toDate(clean.updatedAt)!,\n finishedAt: toDate(clean.finishedAt),\n body: imageJobBody(clean),\n };\n}\n\n/** Вторая открытая задача на место: её не пускает уникальный индекс `image_jobs_open_key_idx`. */\nfunction placeTaken(error: unknown, job: ImageJobRecord): never {\n if (findErrorCause(error, (cause) => cause.code === \"23505\" && cause.constraint === \"image_jobs_open_key_idx\")) throw new ImageJobKeyTakenError(job.ownerId, job.key);\n throw error;\n}\n\nfunction filterOf(filter: ImageJobFilter): SQL | undefined {\n return and(\n filter.ownerId !== undefined ? eq(imageJobs.ownerId, filter.ownerId) : undefined,\n filter.chatId !== undefined ? eq(imageJobs.chatId, filter.chatId) : undefined,\n filter.key !== undefined ? eq(imageJobs.key, filter.key) : undefined,\n filter.group !== undefined ? eq(imageJobs.group, filter.group) : undefined,\n );\n}\n\n/**\n * Приводит таблицу задач к последней миграции. Схема `agent_chat` создаётся под общей блокировкой всех пакетов, миграции — под своей:\n * копии пакета ждут друг друга, а разные пакеты стартуют разом на пустой базе без гонки за схему.\n */\nasync function migrateImageJobsToLatest(pool: Pool, migrationsFolder: string): Promise<void> {\n await migratePostgresSchema({\n connect: () => pool.connect(),\n lockKey: IMAGE_JOBS_MIGRATION_LOCK_KEY,\n migrate: (client) => migrate(drizzle(client), { migrationsFolder, migrationsSchema: \"agent_chat\", migrationsTable: IMAGE_JOBS_MIGRATIONS_TABLE }),\n });\n}\n\n/**\n * Задачи картинок в PostgreSQL (схема `agent_chat`) на пуле приложения. Миграция — при первом обращении, каждая операция\n * её дожидается. Правка — условное `update … where version = …`: из двух одновременных пройдёт одна. Пул закрывает\n * приложение.\n */\nexport function createPostgresImageJobStore(pool: Pool, migrationsFolder: string): ImageJobStore {\n const db = drizzle(pool);\n const ready = migrateImageJobsToLatest(pool, migrationsFolder);\n // Ошибка миграции придёт в каждую операцию; без этого она всплыла бы ещё и как необработанная.\n ready.catch(() => {});\n\n async function get(id: string): Promise<ImageJobRecord | null> {\n await ready;\n const [row] = await db.select().from(imageJobs).where(eq(imageJobs.id, id));\n return row ? toRecord(row) : null;\n }\n\n return {\n async insert(job) {\n await ready;\n await db\n .insert(imageJobs)\n .values(toRow(job))\n .catch((error: unknown) => placeTaken(error, job));\n },\n get,\n async list(filter) {\n await ready;\n const rows = await db.select().from(imageJobs).where(filterOf(filter)).orderBy(asc(imageJobs.createdAt), asc(imageJobs.id));\n return rows.map(toRecord);\n },\n async update(id, patch, expectedVersion) {\n const job = await get(id);\n if (!job || job.version !== expectedVersion) return null;\n const next = { ...applyImageJobPatch(job, patch), version: expectedVersion + 1 };\n const { id: _id, ...columns } = toRow(next);\n const [row] = await db\n .update(imageJobs)\n .set(columns)\n .where(and(eq(imageJobs.id, id), eq(imageJobs.version, expectedVersion)))\n .returning()\n .catch((error: unknown) => placeTaken(error, next));\n return row ? toRecord(row) : null;\n },\n async listResumable(now) {\n await ready;\n const rows = await db\n .select()\n .from(imageJobs)\n .where(and(eq(imageJobs.active, true), or(isNull(imageJobs.leaseUntil), lt(imageJobs.leaseUntil, new Date(now)))))\n .orderBy(asc(imageJobs.createdAt));\n return rows.map(toRecord);\n },\n async deleteFinishedBefore(before) {\n await ready;\n const deleted = await db\n .delete(imageJobs)\n .where(and(eq(imageJobs.active, false), ne(imageJobs.status, \"generating\"), isNotNull(imageJobs.finishedAt), lt(imageJobs.finishedAt, new Date(before))))\n .returning({ id: imageJobs.id });\n return deleted.length;\n },\n async close() {\n // Дожидаемся миграции: пул приложения можно закрывать сразу после.\n await ready.catch(() => {});\n },\n };\n}\n","import { sql } from \"drizzle-orm\";\nimport { boolean, index, integer, jsonb, pgSchema, text, timestamp, uniqueIndex } from \"drizzle-orm/pg-core\";\nimport type { ImageJobStatus } from \"./image-job-store\";\n\n/** Та же схема базы, что у хранилища чатов: у приложения одна база и одна схема `agent_chat`. */\nconst agentChatSchema = pgSchema(\"agent_chat\");\n\nconst moment = (name: string) => timestamp(name, { mode: \"date\", precision: 3, withTimezone: true });\n\nexport const imageJobs = agentChatSchema.table(\n \"image_jobs\",\n {\n id: text(\"id\").primaryKey(),\n key: text(\"key\").notNull(),\n ownerId: text(\"owner_id\").notNull(),\n chatId: text(\"chat_id\").notNull(),\n group: text(\"group_name\"),\n status: text(\"status\").$type<ImageJobStatus>().notNull(),\n /** У задачи есть работа: её подбирает старт сервера. */\n active: boolean(\"active\").notNull(),\n /** Задача открыта (генерация или позднее окно): открытая на место — одна, это держит индекс ниже. */\n open: boolean(\"open\").notNull(),\n leaseOwner: text(\"lease_owner\"),\n leaseUntil: moment(\"lease_until\"),\n version: integer(\"version\").notNull(),\n createdAt: moment(\"created_at\").notNull(),\n updatedAt: moment(\"updated_at\").notNull(),\n finishedAt: moment(\"finished_at\"),\n /** Остальное: описание, назначение, данные приложения, шаги с деньгами. */\n body: jsonb(\"body\").$type<Record<string, unknown>>().notNull(),\n },\n (table) => [\n index(\"image_jobs_owner_key_idx\").on(table.ownerId, table.key),\n uniqueIndex(\"image_jobs_open_key_idx\").on(table.ownerId, table.key).where(sql`open`),\n index(\"image_jobs_group_idx\").on(table.group),\n index(\"image_jobs_active_lease_idx\").on(table.active, table.leaseUntil),\n ],\n);\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ykurilov/agent-chat-images",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.58.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -49,14 +49,14 @@
|
|
|
49
49
|
"drizzle-orm": "^0.45.3",
|
|
50
50
|
"sharp": "^0.35.5",
|
|
51
51
|
"zod": "^4.6.5",
|
|
52
|
-
"@ykurilov/agent-chat-attachments": "0.
|
|
53
|
-
"@ykurilov/agent-chat-core": "0.
|
|
52
|
+
"@ykurilov/agent-chat-attachments": "0.58.0",
|
|
53
|
+
"@ykurilov/agent-chat-core": "0.58.0"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
56
|
"better-sqlite3": "^13.0.3",
|
|
57
57
|
"pg": "^8.23.0",
|
|
58
58
|
"react": "^19.3.0",
|
|
59
|
-
"@ykurilov/agent-chat-react": "^0.
|
|
59
|
+
"@ykurilov/agent-chat-react": "^0.58.0"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
62
|
"@electric-sql/pglite": "0.5.8",
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
"pg": "^8.23.0",
|
|
72
72
|
"react": "^19.3.0",
|
|
73
73
|
"tsx": "4.23.15",
|
|
74
|
-
"@ykurilov/agent-chat-react": "0.
|
|
74
|
+
"@ykurilov/agent-chat-react": "0.58.0"
|
|
75
75
|
},
|
|
76
76
|
"peerDependenciesMeta": {
|
|
77
77
|
"react": {
|