@dyrected/core 2.15.0 → 2.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/server.cjs CHANGED
@@ -6466,6 +6466,134 @@ function isPlainObject2(value) {
6466
6466
  return typeof value === "object" && value !== null && !Array.isArray(value);
6467
6467
  }
6468
6468
 
6469
+ // src/utils/id.ts
6470
+ var import_node_crypto2 = require("crypto");
6471
+ var NANOID_ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-";
6472
+ var BASE62_ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
6473
+ var CROCKFORD_BASE32 = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
6474
+ var COMMON_SLUG_PREFIXES = {
6475
+ users: "usr",
6476
+ members: "mem",
6477
+ accounts: "acc",
6478
+ applications: "app",
6479
+ customers: "cus",
6480
+ products: "prd",
6481
+ orders: "ord",
6482
+ reports: "rpt",
6483
+ sessions: "ses",
6484
+ transactions: "txn",
6485
+ wallets: "wal",
6486
+ media: "med",
6487
+ files: "fil",
6488
+ posts: "post",
6489
+ pages: "page",
6490
+ tags: "tag",
6491
+ categories: "cat",
6492
+ comments: "com"
6493
+ };
6494
+ function nanoid(size = 21, alphabet = NANOID_ALPHABET) {
6495
+ const mask = (2 << 31 - Math.clz32(alphabet.length - 1 | 1)) - 1;
6496
+ const step = Math.ceil(1.6 * mask * size / alphabet.length);
6497
+ let id = "";
6498
+ while (true) {
6499
+ const bytes = (0, import_node_crypto2.randomBytes)(step);
6500
+ for (let i = 0; i < step; i++) {
6501
+ const byte = bytes[i] & mask;
6502
+ if (alphabet[byte]) {
6503
+ id += alphabet[byte];
6504
+ if (id.length === size) return id;
6505
+ }
6506
+ }
6507
+ }
6508
+ }
6509
+ function encodeUlidTime(now2, len = 10) {
6510
+ let str = "";
6511
+ let time = Math.floor(now2);
6512
+ for (let i = len - 1; i >= 0; i--) {
6513
+ const mod = time % 32;
6514
+ str = CROCKFORD_BASE32[mod] + str;
6515
+ time = (time - mod) / 32;
6516
+ }
6517
+ return str;
6518
+ }
6519
+ function encodeUlidRandom(bytes) {
6520
+ let str = "";
6521
+ let bitBuffer = 0;
6522
+ let bitsInBuffer = 0;
6523
+ for (let i = 0; i < bytes.length; i++) {
6524
+ bitBuffer = bitBuffer << 8 | bytes[i];
6525
+ bitsInBuffer += 8;
6526
+ while (bitsInBuffer >= 5) {
6527
+ bitsInBuffer -= 5;
6528
+ const index = bitBuffer >> bitsInBuffer & 31;
6529
+ str += CROCKFORD_BASE32[index];
6530
+ }
6531
+ }
6532
+ return str;
6533
+ }
6534
+ function ulid(seedTime = Date.now()) {
6535
+ const timePart = encodeUlidTime(seedTime);
6536
+ const randPart = encodeUlidRandom((0, import_node_crypto2.randomBytes)(10));
6537
+ return timePart + randPart;
6538
+ }
6539
+ function uuid() {
6540
+ return (0, import_node_crypto2.randomUUID)();
6541
+ }
6542
+ function resolvePrefix(slug, explicitPrefix) {
6543
+ if (explicitPrefix && typeof explicitPrefix === "string" && explicitPrefix.trim().length > 0) {
6544
+ const cleaned = explicitPrefix.trim().toLowerCase().replace(/[^a-z0-9_]/g, "");
6545
+ if (cleaned.length > 0) return cleaned;
6546
+ }
6547
+ if (!slug || typeof slug !== "string") return "col";
6548
+ const s = slug.trim().toLowerCase();
6549
+ if (COMMON_SLUG_PREFIXES[s]) return COMMON_SLUG_PREFIXES[s];
6550
+ const pieces = s.split(/[-_]/).filter(Boolean);
6551
+ if (pieces.length > 1 && pieces[0].length >= 2) {
6552
+ const first2 = pieces[0].replace(/[^a-z0-9]/g, "");
6553
+ if (first2.length >= 2) return first2.slice(0, 4);
6554
+ }
6555
+ const cleanWord = s.replace(/[^a-z0-9]/g, "");
6556
+ if (cleanWord.length <= 4) return cleanWord || "col";
6557
+ return cleanWord.slice(0, 3) || "col";
6558
+ }
6559
+ function prefixedId(prefix = "col", size = 16) {
6560
+ const cleanPrefix = (prefix || "col").toLowerCase().replace(/[^a-z0-9_]/g, "") || "col";
6561
+ return `${cleanPrefix}_${nanoid(size, BASE62_ALPHABET)}`;
6562
+ }
6563
+ function generateId(options) {
6564
+ if (typeof options === "string") {
6565
+ return prefixedId(resolvePrefix(options));
6566
+ }
6567
+ const opts = options || {};
6568
+ const type = opts.type || "prefixed-nanoid";
6569
+ switch (type) {
6570
+ case "uuid":
6571
+ return uuid();
6572
+ case "ulid":
6573
+ return ulid();
6574
+ case "nanoid":
6575
+ return nanoid(opts.size || 21);
6576
+ case "prefixed-nanoid":
6577
+ default:
6578
+ return prefixedId(opts.prefix || "col", opts.size || 16);
6579
+ }
6580
+ }
6581
+ function generateDocumentId(collectionOrSlug) {
6582
+ if (!collectionOrSlug) {
6583
+ return prefixedId("col");
6584
+ }
6585
+ if (typeof collectionOrSlug === "string") {
6586
+ const prefix2 = resolvePrefix(collectionOrSlug);
6587
+ return prefixedId(prefix2);
6588
+ }
6589
+ if (typeof collectionOrSlug.idGenerator === "function") {
6590
+ return collectionOrSlug.idGenerator(collectionOrSlug.slug);
6591
+ }
6592
+ const type = collectionOrSlug.idType || "prefixed-nanoid";
6593
+ const prefix = resolvePrefix(collectionOrSlug.slug, collectionOrSlug.idPrefix);
6594
+ return generateId({ prefix, type });
6595
+ }
6596
+
6469
6597
  // src/workflows.ts
6470
6598
  var WORKFLOW_HISTORY_COLLECTION = "__workflow_history";
6471
6599
  var LIFECYCLE_EVENTS_COLLECTION = "__lifecycle_events";
@@ -7434,6 +7562,9 @@ var CollectionController = class {
7434
7562
  surface: "collection.beforeChange",
7435
7563
  path: `collection:${this.collection.slug}.hooks.beforeChange`
7436
7564
  });
7565
+ if (!data.id) {
7566
+ data = { ...data, id: generateDocumentId(this.collection) };
7567
+ }
7437
7568
  const doc = this.collection.workflow ? (await createWorkflowDocument({
7438
7569
  config,
7439
7570
  collection: this.collection,
@@ -7584,6 +7715,9 @@ var CollectionController = class {
7584
7715
  surface: "collection.beforeChange",
7585
7716
  path: `collection:${this.collection.slug}.hooks.beforeChange`
7586
7717
  });
7718
+ if (!data.id) {
7719
+ data = { ...data, id: generateDocumentId(this.collection) };
7720
+ }
7587
7721
  const doc = await db.create({
7588
7722
  collection: this.collection.slug,
7589
7723
  data
@@ -7849,7 +7983,8 @@ var CollectionController = class {
7849
7983
  }
7850
7984
  const body = await c.req.json().catch(() => ({}));
7851
7985
  const requestedIds = Array.isArray(body?.ids) ? body.ids.filter((value) => typeof value === "string") : typeof body?.id === "string" ? [body.id] : [];
7852
- if (!requestedIds.length) {
7986
+ const isHeaderAction = action.type === "header";
7987
+ if (!requestedIds.length && !isHeaderAction) {
7853
7988
  return c.json(
7854
7989
  { error: true, message: "Provide an `id` or an `ids` array of documents to act on." },
7855
7990
  400
@@ -7877,6 +8012,30 @@ var CollectionController = class {
7877
8012
  return c.json({ error: true, message: `Access denied: action "${action.name}"` }, 403);
7878
8013
  }
7879
8014
  }
8015
+ if (isHeaderAction && !requestedIds.length) {
8016
+ if (typeof action.handler === "function") {
8017
+ try {
8018
+ const handlerResult = await action.handler({
8019
+ doc: null,
8020
+ docs: [],
8021
+ user: user ?? null,
8022
+ input,
8023
+ collection: {
8024
+ slug: this.collection.slug,
8025
+ label: this.collection.labels?.singular ?? this.collection.slug
8026
+ }
8027
+ });
8028
+ return c.json(handlerResult ?? { success: true });
8029
+ } catch (error) {
8030
+ const status = typeof error.statusCode === "number" ? error.statusCode : 500;
8031
+ return c.json(
8032
+ { error: true, message: error instanceof Error ? error.message : String(error) },
8033
+ status
8034
+ );
8035
+ }
8036
+ }
8037
+ return c.json({ success: true });
8038
+ }
7880
8039
  const results = [];
7881
8040
  for (const id of requestedIds) {
7882
8041
  const targetDoc = await db.findOne({ collection: this.collection.slug, id });
@@ -9285,7 +9444,7 @@ var MediaFolderController = class {
9285
9444
  };
9286
9445
 
9287
9446
  // src/controllers/auth.controller.ts
9288
- var import_node_crypto2 = require("crypto");
9447
+ var import_node_crypto3 = require("crypto");
9289
9448
 
9290
9449
  // src/auth/lockout.ts
9291
9450
  var DEFAULT_MAX_LOGIN_ATTEMPTS = 5;
@@ -9596,7 +9755,7 @@ var AuthController = class {
9596
9755
  const data = {
9597
9756
  ...safeExtraFields,
9598
9757
  email,
9599
- password: await hashPassword((0, import_node_crypto2.randomBytes)(32).toString("hex"))
9758
+ password: await hashPassword((0, import_node_crypto3.randomBytes)(32).toString("hex"))
9600
9759
  };
9601
9760
  if (this.hasField("status")) {
9602
9761
  data.status = "pending";
@@ -10199,7 +10358,7 @@ var AuthController = class {
10199
10358
  };
10200
10359
 
10201
10360
  // src/controllers/admin-auth.controller.ts
10202
- var import_node_crypto3 = require("crypto");
10361
+ var import_node_crypto4 = require("crypto");
10203
10362
  var import_node_util2 = require("util");
10204
10363
  var import_jose2 = require("jose");
10205
10364
  var AdminAuthController = class {
@@ -10409,7 +10568,7 @@ var AdminAuthController = class {
10409
10568
  access.roles,
10410
10569
  access.data
10411
10570
  ),
10412
- password: await hashPassword((0, import_node_crypto3.randomBytes)(32).toString("hex"))
10571
+ password: await hashPassword((0, import_node_crypto4.randomBytes)(32).toString("hex"))
10413
10572
  }
10414
10573
  });
10415
10574
  } else if (user) {
@@ -10496,7 +10655,7 @@ var AdminAuthController = class {
10496
10655
  }
10497
10656
  async buildOidcStartUrl(provider, returnTo, siteId, origin) {
10498
10657
  const discovery = await this.getOidcDiscovery(provider);
10499
- const nonce = (0, import_node_crypto3.randomBytes)(16).toString("hex");
10658
+ const nonce = (0, import_node_crypto4.randomBytes)(16).toString("hex");
10500
10659
  const state = await this.signState({
10501
10660
  providerId: provider.id,
10502
10661
  returnTo,
@@ -11057,7 +11216,7 @@ function formatAIErrorMessage(error) {
11057
11216
  }
11058
11217
  return msg;
11059
11218
  }
11060
- function generateId() {
11219
+ function generateId2() {
11061
11220
  return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
11062
11221
  }
11063
11222
  function buildDyrectedSystemPrompt(context) {
@@ -11203,7 +11362,7 @@ var AIAgent = class {
11203
11362
  }
11204
11363
  async createThread(title, id) {
11205
11364
  const thread = {
11206
- id: id || generateId(),
11365
+ id: id || generateId2(),
11207
11366
  projectId: this.projectId,
11208
11367
  userId: this.userId,
11209
11368
  title: title || "New Conversation",
@@ -11270,7 +11429,7 @@ var AIAgent = class {
11270
11429
  }
11271
11430
  async persistUserMessage(threadId, content) {
11272
11431
  const message = {
11273
- id: generateId(),
11432
+ id: generateId2(),
11274
11433
  threadId,
11275
11434
  role: "user",
11276
11435
  content,
@@ -11286,7 +11445,7 @@ var AIAgent = class {
11286
11445
  }
11287
11446
  async persistAssistantMessage(threadId, content, metadata, parts) {
11288
11447
  const message = {
11289
- id: generateId(),
11448
+ id: generateId2(),
11290
11449
  threadId,
11291
11450
  role: "assistant",
11292
11451
  content,
@@ -14848,11 +15007,31 @@ function normalizeConfig(config) {
14848
15007
  }
14849
15008
  const updatedFieldNames = new Set(fields.map((f) => f.name));
14850
15009
  const fieldsToInject = SYSTEM_FIELDS.filter((f) => !updatedFieldNames.has(f.name));
15010
+ const indexedFieldNames = /* @__PURE__ */ new Set();
15011
+ if (col.indexes) {
15012
+ for (const idx of col.indexes) {
15013
+ if (Array.isArray(idx.fields)) {
15014
+ for (const f of idx.fields) {
15015
+ indexedFieldNames.add(f);
15016
+ }
15017
+ }
15018
+ }
15019
+ }
15020
+ const allFields = [...fields, ...fieldsToInject].map((field) => {
15021
+ if (field.unique || field.name && indexedFieldNames.has(field.name)) {
15022
+ return { ...field, promoted: true };
15023
+ }
15024
+ return field;
15025
+ });
14851
15026
  const workflow = col.workflow || (col.drafts ? simplePublishingWorkflow() : void 0);
15027
+ const idPrefix = col.idPrefix || resolvePrefix(col.slug);
15028
+ const idType = col.idType || "prefixed-nanoid";
14852
15029
  return {
14853
15030
  ...col,
15031
+ idPrefix,
15032
+ idType,
14854
15033
  workflow,
14855
- fields: [...fields, ...fieldsToInject]
15034
+ fields: allFields
14856
15035
  };
14857
15036
  });
14858
15037
  const hasAuditCollection = normalizedCollections.some((col) => col.slug === AUDIT_COLLECTION_SLUG);
package/dist/server.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { M as DyrectedContext, D as DyrectedConfig, C as CollectionConfig, G as GlobalConfig, bl as DatabaseAdapter, b as AuthenticatedUser, bG as DyrectedAIContext, an as AIThread, aj as AIMessage, cw as RAGSearchResult, am as AIRateLimitStore, al as AIRateLimitResult } from './app-Cmp5lFLJ.cjs';
2
- export { da as CollectionTokenPayload, db as DEFAULT_SESSION_EXPIRY, dc as DyrectedObservabilityRuntime, dd as RequestBodyCapture, de as RequestTraceContext, df as ResolvedObservabilityConfig, dg as attachRequestMetrics, dh as bindObservabilityRuntime, di as buildRequestLogger, dj as captureRequestBody, dk as createDyrectedApp, dl as createObservabilityRuntime, dm as createRequestSpan, dn as decodeCollectionToken, dp as endRequestSpan, dq as getConfigLogger, dr as getObservabilityRuntime, ds as getRequestLogger, dt as redactBody, du as redactHeaders, dv as renderPrometheusMetrics, dw as resolveObservabilityConfig, dx as resolveSessionTokenExpiry, dy as shouldSampleRequest, dz as signCollectionToken, dA as verifyCollectionToken } from './app-Cmp5lFLJ.cjs';
1
+ import { M as DyrectedContext, D as DyrectedConfig, C as CollectionConfig, G as GlobalConfig, bn as DatabaseAdapter, b as AuthenticatedUser, bI as DyrectedAIContext, ao as AIThread, ak as AIMessage, cz as RAGSearchResult, an as AIRateLimitStore, am as AIRateLimitResult } from './app-KSDgfXsw.cjs';
2
+ export { dd as CollectionTokenPayload, de as DEFAULT_SESSION_EXPIRY, df as DyrectedObservabilityRuntime, dg as RequestBodyCapture, dh as RequestTraceContext, di as ResolvedObservabilityConfig, dj as attachRequestMetrics, dk as bindObservabilityRuntime, dl as buildRequestLogger, dm as captureRequestBody, dn as createDyrectedApp, dp as createObservabilityRuntime, dq as createRequestSpan, dr as decodeCollectionToken, ds as endRequestSpan, dt as getConfigLogger, du as getObservabilityRuntime, dv as getRequestLogger, dw as redactBody, dx as redactHeaders, dy as renderPrometheusMetrics, dz as resolveObservabilityConfig, dA as resolveSessionTokenExpiry, dB as shouldSampleRequest, dC as signCollectionToken, dD as verifyCollectionToken } from './app-KSDgfXsw.cjs';
3
3
  import * as hono from 'hono';
4
4
  import { Hono, Context, Next } from 'hono';
5
5
  import * as hono_utils_http_status from 'hono/utils/http-status';
package/dist/server.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { M as DyrectedContext, D as DyrectedConfig, C as CollectionConfig, G as GlobalConfig, bl as DatabaseAdapter, b as AuthenticatedUser, bG as DyrectedAIContext, an as AIThread, aj as AIMessage, cw as RAGSearchResult, am as AIRateLimitStore, al as AIRateLimitResult } from './app-Cmp5lFLJ.js';
2
- export { da as CollectionTokenPayload, db as DEFAULT_SESSION_EXPIRY, dc as DyrectedObservabilityRuntime, dd as RequestBodyCapture, de as RequestTraceContext, df as ResolvedObservabilityConfig, dg as attachRequestMetrics, dh as bindObservabilityRuntime, di as buildRequestLogger, dj as captureRequestBody, dk as createDyrectedApp, dl as createObservabilityRuntime, dm as createRequestSpan, dn as decodeCollectionToken, dp as endRequestSpan, dq as getConfigLogger, dr as getObservabilityRuntime, ds as getRequestLogger, dt as redactBody, du as redactHeaders, dv as renderPrometheusMetrics, dw as resolveObservabilityConfig, dx as resolveSessionTokenExpiry, dy as shouldSampleRequest, dz as signCollectionToken, dA as verifyCollectionToken } from './app-Cmp5lFLJ.js';
1
+ import { M as DyrectedContext, D as DyrectedConfig, C as CollectionConfig, G as GlobalConfig, bn as DatabaseAdapter, b as AuthenticatedUser, bI as DyrectedAIContext, ao as AIThread, ak as AIMessage, cz as RAGSearchResult, an as AIRateLimitStore, am as AIRateLimitResult } from './app-KSDgfXsw.js';
2
+ export { dd as CollectionTokenPayload, de as DEFAULT_SESSION_EXPIRY, df as DyrectedObservabilityRuntime, dg as RequestBodyCapture, dh as RequestTraceContext, di as ResolvedObservabilityConfig, dj as attachRequestMetrics, dk as bindObservabilityRuntime, dl as buildRequestLogger, dm as captureRequestBody, dn as createDyrectedApp, dp as createObservabilityRuntime, dq as createRequestSpan, dr as decodeCollectionToken, ds as endRequestSpan, dt as getConfigLogger, du as getObservabilityRuntime, dv as getRequestLogger, dw as redactBody, dx as redactHeaders, dy as renderPrometheusMetrics, dz as resolveObservabilityConfig, dA as resolveSessionTokenExpiry, dB as shouldSampleRequest, dC as signCollectionToken, dD as verifyCollectionToken } from './app-KSDgfXsw.js';
3
3
  import * as hono from 'hono';
4
4
  import { Hono, Context, Next } from 'hono';
5
5
  import * as hono_utils_http_status from 'hono/utils/http-status';
package/dist/server.js CHANGED
@@ -6406,6 +6406,134 @@ function isPlainObject2(value) {
6406
6406
  return typeof value === "object" && value !== null && !Array.isArray(value);
6407
6407
  }
6408
6408
 
6409
+ // src/utils/id.ts
6410
+ import { randomBytes as randomBytes2, randomUUID } from "crypto";
6411
+ var NANOID_ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-";
6412
+ var BASE62_ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
6413
+ var CROCKFORD_BASE32 = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
6414
+ var COMMON_SLUG_PREFIXES = {
6415
+ users: "usr",
6416
+ members: "mem",
6417
+ accounts: "acc",
6418
+ applications: "app",
6419
+ customers: "cus",
6420
+ products: "prd",
6421
+ orders: "ord",
6422
+ reports: "rpt",
6423
+ sessions: "ses",
6424
+ transactions: "txn",
6425
+ wallets: "wal",
6426
+ media: "med",
6427
+ files: "fil",
6428
+ posts: "post",
6429
+ pages: "page",
6430
+ tags: "tag",
6431
+ categories: "cat",
6432
+ comments: "com"
6433
+ };
6434
+ function nanoid(size = 21, alphabet = NANOID_ALPHABET) {
6435
+ const mask = (2 << 31 - Math.clz32(alphabet.length - 1 | 1)) - 1;
6436
+ const step = Math.ceil(1.6 * mask * size / alphabet.length);
6437
+ let id = "";
6438
+ while (true) {
6439
+ const bytes = randomBytes2(step);
6440
+ for (let i = 0; i < step; i++) {
6441
+ const byte = bytes[i] & mask;
6442
+ if (alphabet[byte]) {
6443
+ id += alphabet[byte];
6444
+ if (id.length === size) return id;
6445
+ }
6446
+ }
6447
+ }
6448
+ }
6449
+ function encodeUlidTime(now2, len = 10) {
6450
+ let str = "";
6451
+ let time = Math.floor(now2);
6452
+ for (let i = len - 1; i >= 0; i--) {
6453
+ const mod = time % 32;
6454
+ str = CROCKFORD_BASE32[mod] + str;
6455
+ time = (time - mod) / 32;
6456
+ }
6457
+ return str;
6458
+ }
6459
+ function encodeUlidRandom(bytes) {
6460
+ let str = "";
6461
+ let bitBuffer = 0;
6462
+ let bitsInBuffer = 0;
6463
+ for (let i = 0; i < bytes.length; i++) {
6464
+ bitBuffer = bitBuffer << 8 | bytes[i];
6465
+ bitsInBuffer += 8;
6466
+ while (bitsInBuffer >= 5) {
6467
+ bitsInBuffer -= 5;
6468
+ const index = bitBuffer >> bitsInBuffer & 31;
6469
+ str += CROCKFORD_BASE32[index];
6470
+ }
6471
+ }
6472
+ return str;
6473
+ }
6474
+ function ulid(seedTime = Date.now()) {
6475
+ const timePart = encodeUlidTime(seedTime);
6476
+ const randPart = encodeUlidRandom(randomBytes2(10));
6477
+ return timePart + randPart;
6478
+ }
6479
+ function uuid() {
6480
+ return randomUUID();
6481
+ }
6482
+ function resolvePrefix(slug, explicitPrefix) {
6483
+ if (explicitPrefix && typeof explicitPrefix === "string" && explicitPrefix.trim().length > 0) {
6484
+ const cleaned = explicitPrefix.trim().toLowerCase().replace(/[^a-z0-9_]/g, "");
6485
+ if (cleaned.length > 0) return cleaned;
6486
+ }
6487
+ if (!slug || typeof slug !== "string") return "col";
6488
+ const s = slug.trim().toLowerCase();
6489
+ if (COMMON_SLUG_PREFIXES[s]) return COMMON_SLUG_PREFIXES[s];
6490
+ const pieces = s.split(/[-_]/).filter(Boolean);
6491
+ if (pieces.length > 1 && pieces[0].length >= 2) {
6492
+ const first2 = pieces[0].replace(/[^a-z0-9]/g, "");
6493
+ if (first2.length >= 2) return first2.slice(0, 4);
6494
+ }
6495
+ const cleanWord = s.replace(/[^a-z0-9]/g, "");
6496
+ if (cleanWord.length <= 4) return cleanWord || "col";
6497
+ return cleanWord.slice(0, 3) || "col";
6498
+ }
6499
+ function prefixedId(prefix = "col", size = 16) {
6500
+ const cleanPrefix = (prefix || "col").toLowerCase().replace(/[^a-z0-9_]/g, "") || "col";
6501
+ return `${cleanPrefix}_${nanoid(size, BASE62_ALPHABET)}`;
6502
+ }
6503
+ function generateId(options) {
6504
+ if (typeof options === "string") {
6505
+ return prefixedId(resolvePrefix(options));
6506
+ }
6507
+ const opts = options || {};
6508
+ const type = opts.type || "prefixed-nanoid";
6509
+ switch (type) {
6510
+ case "uuid":
6511
+ return uuid();
6512
+ case "ulid":
6513
+ return ulid();
6514
+ case "nanoid":
6515
+ return nanoid(opts.size || 21);
6516
+ case "prefixed-nanoid":
6517
+ default:
6518
+ return prefixedId(opts.prefix || "col", opts.size || 16);
6519
+ }
6520
+ }
6521
+ function generateDocumentId(collectionOrSlug) {
6522
+ if (!collectionOrSlug) {
6523
+ return prefixedId("col");
6524
+ }
6525
+ if (typeof collectionOrSlug === "string") {
6526
+ const prefix2 = resolvePrefix(collectionOrSlug);
6527
+ return prefixedId(prefix2);
6528
+ }
6529
+ if (typeof collectionOrSlug.idGenerator === "function") {
6530
+ return collectionOrSlug.idGenerator(collectionOrSlug.slug);
6531
+ }
6532
+ const type = collectionOrSlug.idType || "prefixed-nanoid";
6533
+ const prefix = resolvePrefix(collectionOrSlug.slug, collectionOrSlug.idPrefix);
6534
+ return generateId({ prefix, type });
6535
+ }
6536
+
6409
6537
  // src/workflows.ts
6410
6538
  var WORKFLOW_HISTORY_COLLECTION = "__workflow_history";
6411
6539
  var LIFECYCLE_EVENTS_COLLECTION = "__lifecycle_events";
@@ -7374,6 +7502,9 @@ var CollectionController = class {
7374
7502
  surface: "collection.beforeChange",
7375
7503
  path: `collection:${this.collection.slug}.hooks.beforeChange`
7376
7504
  });
7505
+ if (!data.id) {
7506
+ data = { ...data, id: generateDocumentId(this.collection) };
7507
+ }
7377
7508
  const doc = this.collection.workflow ? (await createWorkflowDocument({
7378
7509
  config,
7379
7510
  collection: this.collection,
@@ -7524,6 +7655,9 @@ var CollectionController = class {
7524
7655
  surface: "collection.beforeChange",
7525
7656
  path: `collection:${this.collection.slug}.hooks.beforeChange`
7526
7657
  });
7658
+ if (!data.id) {
7659
+ data = { ...data, id: generateDocumentId(this.collection) };
7660
+ }
7527
7661
  const doc = await db.create({
7528
7662
  collection: this.collection.slug,
7529
7663
  data
@@ -7789,7 +7923,8 @@ var CollectionController = class {
7789
7923
  }
7790
7924
  const body = await c.req.json().catch(() => ({}));
7791
7925
  const requestedIds = Array.isArray(body?.ids) ? body.ids.filter((value) => typeof value === "string") : typeof body?.id === "string" ? [body.id] : [];
7792
- if (!requestedIds.length) {
7926
+ const isHeaderAction = action.type === "header";
7927
+ if (!requestedIds.length && !isHeaderAction) {
7793
7928
  return c.json(
7794
7929
  { error: true, message: "Provide an `id` or an `ids` array of documents to act on." },
7795
7930
  400
@@ -7817,6 +7952,30 @@ var CollectionController = class {
7817
7952
  return c.json({ error: true, message: `Access denied: action "${action.name}"` }, 403);
7818
7953
  }
7819
7954
  }
7955
+ if (isHeaderAction && !requestedIds.length) {
7956
+ if (typeof action.handler === "function") {
7957
+ try {
7958
+ const handlerResult = await action.handler({
7959
+ doc: null,
7960
+ docs: [],
7961
+ user: user ?? null,
7962
+ input,
7963
+ collection: {
7964
+ slug: this.collection.slug,
7965
+ label: this.collection.labels?.singular ?? this.collection.slug
7966
+ }
7967
+ });
7968
+ return c.json(handlerResult ?? { success: true });
7969
+ } catch (error) {
7970
+ const status = typeof error.statusCode === "number" ? error.statusCode : 500;
7971
+ return c.json(
7972
+ { error: true, message: error instanceof Error ? error.message : String(error) },
7973
+ status
7974
+ );
7975
+ }
7976
+ }
7977
+ return c.json({ success: true });
7978
+ }
7820
7979
  const results = [];
7821
7980
  for (const id of requestedIds) {
7822
7981
  const targetDoc = await db.findOne({ collection: this.collection.slug, id });
@@ -9225,7 +9384,7 @@ var MediaFolderController = class {
9225
9384
  };
9226
9385
 
9227
9386
  // src/controllers/auth.controller.ts
9228
- import { randomBytes as randomBytes2 } from "crypto";
9387
+ import { randomBytes as randomBytes3 } from "crypto";
9229
9388
 
9230
9389
  // src/auth/lockout.ts
9231
9390
  var DEFAULT_MAX_LOGIN_ATTEMPTS = 5;
@@ -9536,7 +9695,7 @@ var AuthController = class {
9536
9695
  const data = {
9537
9696
  ...safeExtraFields,
9538
9697
  email,
9539
- password: await hashPassword(randomBytes2(32).toString("hex"))
9698
+ password: await hashPassword(randomBytes3(32).toString("hex"))
9540
9699
  };
9541
9700
  if (this.hasField("status")) {
9542
9701
  data.status = "pending";
@@ -10139,7 +10298,7 @@ var AuthController = class {
10139
10298
  };
10140
10299
 
10141
10300
  // src/controllers/admin-auth.controller.ts
10142
- import { randomBytes as randomBytes3 } from "crypto";
10301
+ import { randomBytes as randomBytes4 } from "crypto";
10143
10302
  import { TextEncoder as TextEncoder2 } from "util";
10144
10303
  import {
10145
10304
  SignJWT as SignJWT2,
@@ -10354,7 +10513,7 @@ var AdminAuthController = class {
10354
10513
  access.roles,
10355
10514
  access.data
10356
10515
  ),
10357
- password: await hashPassword(randomBytes3(32).toString("hex"))
10516
+ password: await hashPassword(randomBytes4(32).toString("hex"))
10358
10517
  }
10359
10518
  });
10360
10519
  } else if (user) {
@@ -10441,7 +10600,7 @@ var AdminAuthController = class {
10441
10600
  }
10442
10601
  async buildOidcStartUrl(provider, returnTo, siteId, origin) {
10443
10602
  const discovery = await this.getOidcDiscovery(provider);
10444
- const nonce = randomBytes3(16).toString("hex");
10603
+ const nonce = randomBytes4(16).toString("hex");
10445
10604
  const state = await this.signState({
10446
10605
  providerId: provider.id,
10447
10606
  returnTo,
@@ -11002,7 +11161,7 @@ function formatAIErrorMessage(error) {
11002
11161
  }
11003
11162
  return msg;
11004
11163
  }
11005
- function generateId() {
11164
+ function generateId2() {
11006
11165
  return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
11007
11166
  }
11008
11167
  function buildDyrectedSystemPrompt(context) {
@@ -11148,7 +11307,7 @@ var AIAgent = class {
11148
11307
  }
11149
11308
  async createThread(title, id) {
11150
11309
  const thread = {
11151
- id: id || generateId(),
11310
+ id: id || generateId2(),
11152
11311
  projectId: this.projectId,
11153
11312
  userId: this.userId,
11154
11313
  title: title || "New Conversation",
@@ -11215,7 +11374,7 @@ var AIAgent = class {
11215
11374
  }
11216
11375
  async persistUserMessage(threadId, content) {
11217
11376
  const message = {
11218
- id: generateId(),
11377
+ id: generateId2(),
11219
11378
  threadId,
11220
11379
  role: "user",
11221
11380
  content,
@@ -11231,7 +11390,7 @@ var AIAgent = class {
11231
11390
  }
11232
11391
  async persistAssistantMessage(threadId, content, metadata, parts) {
11233
11392
  const message = {
11234
- id: generateId(),
11393
+ id: generateId2(),
11235
11394
  threadId,
11236
11395
  role: "assistant",
11237
11396
  content,
@@ -14793,11 +14952,31 @@ function normalizeConfig(config) {
14793
14952
  }
14794
14953
  const updatedFieldNames = new Set(fields.map((f) => f.name));
14795
14954
  const fieldsToInject = SYSTEM_FIELDS.filter((f) => !updatedFieldNames.has(f.name));
14955
+ const indexedFieldNames = /* @__PURE__ */ new Set();
14956
+ if (col.indexes) {
14957
+ for (const idx of col.indexes) {
14958
+ if (Array.isArray(idx.fields)) {
14959
+ for (const f of idx.fields) {
14960
+ indexedFieldNames.add(f);
14961
+ }
14962
+ }
14963
+ }
14964
+ }
14965
+ const allFields = [...fields, ...fieldsToInject].map((field) => {
14966
+ if (field.unique || field.name && indexedFieldNames.has(field.name)) {
14967
+ return { ...field, promoted: true };
14968
+ }
14969
+ return field;
14970
+ });
14796
14971
  const workflow = col.workflow || (col.drafts ? simplePublishingWorkflow() : void 0);
14972
+ const idPrefix = col.idPrefix || resolvePrefix(col.slug);
14973
+ const idType = col.idType || "prefixed-nanoid";
14797
14974
  return {
14798
14975
  ...col,
14976
+ idPrefix,
14977
+ idType,
14799
14978
  workflow,
14800
- fields: [...fields, ...fieldsToInject]
14979
+ fields: allFields
14801
14980
  };
14802
14981
  });
14803
14982
  const hasAuditCollection = normalizedCollections.some((col) => col.slug === AUDIT_COLLECTION_SLUG);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dyrected/core",
3
- "version": "2.15.0",
3
+ "version": "2.16.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",