@dyrected/core 2.12.0 → 2.13.1

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/index.cjs CHANGED
@@ -5,9 +5,6 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __getProtoOf = Object.getPrototypeOf;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __esm = (fn, res) => function __init() {
9
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
10
- };
11
8
  var __commonJS = (cb, mod) => function __require() {
12
9
  return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
13
10
  };
@@ -33,517 +30,6 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
33
30
  ));
34
31
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
35
32
 
36
- // src/observability.ts
37
- var observability_exports = {};
38
- __export(observability_exports, {
39
- attachRequestMetrics: () => attachRequestMetrics,
40
- bindObservabilityRuntime: () => bindObservabilityRuntime,
41
- buildRequestLogger: () => buildRequestLogger,
42
- captureRequestBody: () => captureRequestBody,
43
- createObservabilityRuntime: () => createObservabilityRuntime,
44
- createRequestSpan: () => createRequestSpan,
45
- endRequestSpan: () => endRequestSpan,
46
- getConfigLogger: () => getConfigLogger,
47
- getObservabilityRuntime: () => getObservabilityRuntime,
48
- getRequestLogger: () => getRequestLogger,
49
- redactBody: () => redactBody,
50
- redactHeaders: () => redactHeaders,
51
- renderPrometheusMetrics: () => renderPrometheusMetrics,
52
- resolveObservabilityConfig: () => resolveObservabilityConfig,
53
- shouldSampleRequest: () => shouldSampleRequest
54
- });
55
- function resolveObservabilityConfig(config) {
56
- const requestLogging = config?.requestLogging;
57
- const sampling = config?.sampling;
58
- const tracing = config?.tracing;
59
- const metrics = config?.metrics;
60
- return {
61
- requestLogging: {
62
- enabled: requestLogging?.enabled ?? true,
63
- logBodies: requestLogging?.logBodies ?? false,
64
- maxBodyBytes: requestLogging?.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES,
65
- redactPaths: uniqueValues([
66
- ...DEFAULT_REDACT_PATHS,
67
- ...requestLogging?.redactPaths ?? []
68
- ]),
69
- includeHeaders: normalizeHeaderNames(requestLogging?.includeHeaders ?? []),
70
- redactHeaders: uniqueValues(
71
- normalizeHeaderNames([
72
- ...DEFAULT_REDACT_HEADERS,
73
- ...requestLogging?.redactHeaders ?? []
74
- ])
75
- )
76
- },
77
- sampling: {
78
- successRate: clampRate(sampling?.successRate ?? DEFAULT_SUCCESS_SAMPLE_RATE),
79
- traceSuccessRate: clampRate(
80
- sampling?.traceSuccessRate ?? sampling?.successRate ?? DEFAULT_SUCCESS_SAMPLE_RATE
81
- ),
82
- bodySuccessRate: clampRate(
83
- sampling?.bodySuccessRate ?? Math.min(DEFAULT_BODY_SAMPLE_RATE, sampling?.successRate ?? DEFAULT_SUCCESS_SAMPLE_RATE)
84
- ),
85
- alwaysKeep4xx: sampling?.alwaysKeep4xx ?? true,
86
- alwaysKeep5xx: sampling?.alwaysKeep5xx ?? true
87
- },
88
- tracing: {
89
- enabled: tracing?.enabled ?? false,
90
- serviceName: tracing?.serviceName ?? "dyrected",
91
- exporter: tracing?.exporter ?? "none",
92
- headers: tracing?.headers ?? {},
93
- endpoint: tracing?.endpoint
94
- },
95
- metrics: {
96
- enabled: metrics?.enabled ?? false,
97
- exporter: metrics?.exporter ?? "none",
98
- endpoint: metrics?.endpoint,
99
- path: metrics?.path ?? "/metrics"
100
- },
101
- transports: {
102
- targets: config?.transports?.targets && config.transports.targets.length > 0 ? config.transports.targets : [{ type: "stdout" }]
103
- }
104
- };
105
- }
106
- function createObservabilityRuntime(config) {
107
- const resolved = resolveObservabilityConfig(config.observability);
108
- const logger = createRootLogger(config.logger, resolved);
109
- const shutdownTasks = [];
110
- const meterRuntime = resolved.metrics.enabled || resolved.metrics.exporter === "prometheus" ? createMeterProvider(resolved) : void 0;
111
- const tracerProvider = resolved.tracing.enabled ? createTracerProvider(resolved) : void 0;
112
- const tracer = tracerProvider?.getTracer("dyrected");
113
- const meter = meterRuntime?.provider.getMeter("dyrected");
114
- const metrics = meter ? {
115
- requestCount: meter.createCounter("dyrected_http_requests_total", {
116
- description: "Total HTTP requests served by Dyrected"
117
- }),
118
- authFailureCount: meter.createCounter("dyrected_auth_failures_total", {
119
- description: "Authentication failures"
120
- }),
121
- uncaughtErrorCount: meter.createCounter("dyrected_uncaught_errors_total", {
122
- description: "Uncaught application errors"
123
- }),
124
- auditWriteFailureCount: meter.createCounter("dyrected_audit_write_failures_total", {
125
- description: "Audit log persistence failures"
126
- }),
127
- emailSendFailureCount: meter.createCounter("dyrected_email_send_failures_total", {
128
- description: "Transactional email delivery failures"
129
- }),
130
- workflowHookFailureCount: meter.createCounter("dyrected_workflow_hook_failures_total", {
131
- description: "Workflow hook failures isolated after successful writes"
132
- }),
133
- requestDuration: meter.createHistogram("dyrected_http_request_duration_ms", {
134
- description: "HTTP request duration in milliseconds",
135
- unit: "ms"
136
- })
137
- } : void 0;
138
- if (meterRuntime) {
139
- shutdownTasks.push(() => meterRuntime.provider.shutdown());
140
- }
141
- if (tracerProvider) {
142
- shutdownTasks.push(() => tracerProvider.shutdown());
143
- }
144
- return {
145
- logger,
146
- config: resolved,
147
- tracer,
148
- metrics,
149
- prometheusExporter: meterRuntime?.prometheusExporter,
150
- shutdown: async () => {
151
- for (const task of shutdownTasks) {
152
- await task();
153
- }
154
- },
155
- recordAuthFailure: (attributes) => metrics?.authFailureCount.add(1, attributes),
156
- recordUncaughtError: (attributes) => metrics?.uncaughtErrorCount.add(1, attributes),
157
- recordAuditWriteFailure: (attributes) => metrics?.auditWriteFailureCount.add(1, attributes),
158
- recordEmailSendFailure: (attributes) => metrics?.emailSendFailureCount.add(1, attributes),
159
- recordWorkflowHookFailure: (attributes) => metrics?.workflowHookFailureCount.add(1, attributes)
160
- };
161
- }
162
- function bindObservabilityRuntime(config, runtime) {
163
- runtimeByConfig.set(config, runtime);
164
- }
165
- function getObservabilityRuntime(config) {
166
- if (!config || typeof config !== "object") return void 0;
167
- return runtimeByConfig.get(config);
168
- }
169
- function createRootLogger(loggerConfig, observability) {
170
- if (loggerConfig && !("options" in loggerConfig)) {
171
- return loggerConfig;
172
- }
173
- const options = loggerConfig?.options ?? {};
174
- const enabled = options.enabled ?? process.env.DISABLE_LOGGING !== "true";
175
- const name = options.name ?? "dyrected";
176
- if (loggerConfig?.destination) {
177
- return (0, import_pino.default)(
178
- {
179
- ...options,
180
- enabled,
181
- name,
182
- timestamp: options.timestamp ?? import_pino.stdTimeFunctions.isoTime
183
- },
184
- loggerConfig.destination
185
- );
186
- }
187
- const streams = buildTransportStreams(observability);
188
- const destination = streams.length === 1 ? streams[0] : (0, import_pino.multistream)(streams.map((stream) => ({ stream })));
189
- return (0, import_pino.default)(
190
- {
191
- ...options,
192
- enabled,
193
- name,
194
- timestamp: options.timestamp ?? import_pino.stdTimeFunctions.isoTime
195
- },
196
- destination
197
- );
198
- }
199
- function buildTransportStreams(observability) {
200
- const targets = observability.transports.targets;
201
- if (targets.length === 0) {
202
- return [defaultLoggerDestination()];
203
- }
204
- return targets.map((target) => {
205
- switch (target.type) {
206
- case "stdout":
207
- return defaultLoggerDestination();
208
- case "stderr":
209
- return (0, import_pino.destination)(2);
210
- case "file":
211
- return (0, import_pino.destination)(target.path);
212
- case "otlp":
213
- return new OtlpLogWritable(target.endpoint, target.headers);
214
- }
215
- });
216
- }
217
- function defaultLoggerDestination() {
218
- if (process.env.NODE_ENV !== "production" && typeof window === "undefined") {
219
- try {
220
- const req = globalThis.require;
221
- if (req) {
222
- const pinoPretty = req("pino-pretty");
223
- return pinoPretty({
224
- colorize: true,
225
- ignore: "pid,hostname",
226
- translateTime: "SYS:HH:MM:ss",
227
- destination: 1,
228
- sync: true
229
- });
230
- }
231
- } catch {
232
- }
233
- }
234
- return (0, import_pino.destination)(1);
235
- }
236
- function createTracerProvider(observability) {
237
- const exporter = createTraceExporter(observability);
238
- const provider = new import_sdk_trace_base.BasicTracerProvider({
239
- resource: (0, import_resources.resourceFromAttributes)({
240
- "service.name": observability.tracing.serviceName
241
- }),
242
- spanProcessors: exporter ? [new import_sdk_trace_base.BatchSpanProcessor(exporter)] : []
243
- });
244
- import_api.trace.setGlobalTracerProvider(provider);
245
- return provider;
246
- }
247
- function createTraceExporter(observability) {
248
- switch (observability.tracing.exporter) {
249
- case "console":
250
- return new import_sdk_trace_base.ConsoleSpanExporter();
251
- case "otlp":
252
- return new import_exporter_trace_otlp_http.OTLPTraceExporter({
253
- url: observability.tracing.endpoint,
254
- headers: observability.tracing.headers
255
- });
256
- default:
257
- return void 0;
258
- }
259
- }
260
- function createMeterProvider(observability) {
261
- const readers = [];
262
- let prometheusExporter;
263
- switch (observability.metrics.exporter) {
264
- case "otlp":
265
- readers.push(
266
- new import_sdk_metrics.PeriodicExportingMetricReader({
267
- exporter: new import_exporter_metrics_otlp_http.OTLPMetricExporter({
268
- url: observability.metrics.endpoint
269
- })
270
- })
271
- );
272
- break;
273
- case "prometheus":
274
- prometheusExporter = new import_exporter_prometheus.PrometheusExporter({
275
- endpoint: observability.metrics.path,
276
- preventServerStart: true
277
- });
278
- readers.push(prometheusExporter);
279
- break;
280
- }
281
- const provider = new import_sdk_metrics.MeterProvider({
282
- resource: (0, import_resources.resourceFromAttributes)({
283
- "service.name": observability.tracing.serviceName
284
- }),
285
- readers
286
- });
287
- return { provider, prometheusExporter };
288
- }
289
- function getConfigLogger(config, component) {
290
- const base = config?.logger && !("options" in config.logger) ? config.logger : fallbackLogger;
291
- return base.child({ component });
292
- }
293
- function getRequestLogger(c, component) {
294
- const base = c.get("logger") ?? fallbackLogger;
295
- return component ? base.child({ component }) : base;
296
- }
297
- function buildRequestLogger(runtime, vars, requestId) {
298
- const trace2 = vars.requestTrace;
299
- return runtime.logger.child({
300
- requestId,
301
- siteId: vars.siteId,
302
- workspaceId: vars.workspaceId,
303
- traceId: trace2?.traceId,
304
- spanId: trace2?.spanId
305
- });
306
- }
307
- function shouldSampleRequest(requestId, statusCode, sampling, kind) {
308
- if (statusCode >= 500 && sampling.alwaysKeep5xx) return true;
309
- if (statusCode >= 400 && sampling.alwaysKeep4xx) return true;
310
- const rate = kind === "trace" ? sampling.traceSuccessRate : kind === "body" ? sampling.bodySuccessRate : sampling.successRate;
311
- return deterministicSample(requestId ?? "no-request-id", rate);
312
- }
313
- function redactHeaders(headers, observability) {
314
- const includeHeaders = new Set(observability.requestLogging.includeHeaders);
315
- const redactHeaders2 = new Set(observability.requestLogging.redactHeaders);
316
- const output = {};
317
- for (const [key, value] of Object.entries(headers)) {
318
- const normalizedKey = key.toLowerCase();
319
- if (includeHeaders.size > 0 && !includeHeaders.has(normalizedKey)) continue;
320
- output[normalizedKey] = redactHeaders2.has(normalizedKey) ? REDACTED_VALUE : value;
321
- }
322
- return output;
323
- }
324
- async function captureRequestBody(request, observability) {
325
- const contentType = request.headers.get("content-type") ?? void 0;
326
- const contentLength = toNumber(request.headers.get("content-length"));
327
- const jsonBody = contentType?.includes("application/json");
328
- if (!jsonBody) {
329
- return {
330
- attempted: false,
331
- contentType,
332
- contentLength
333
- };
334
- }
335
- try {
336
- const text = await request.clone().text();
337
- if (!text) {
338
- return { attempted: true, contentType, contentLength };
339
- }
340
- const truncated = text.length > observability.requestLogging.maxBodyBytes;
341
- const raw = truncated ? text.slice(0, observability.requestLogging.maxBodyBytes) : text;
342
- try {
343
- const parsed = JSON.parse(raw);
344
- return {
345
- attempted: true,
346
- contentType,
347
- contentLength: contentLength ?? text.length,
348
- truncated,
349
- body: redactBody(parsed, observability.requestLogging.redactPaths)
350
- };
351
- } catch {
352
- return {
353
- attempted: true,
354
- contentType,
355
- contentLength: contentLength ?? text.length,
356
- truncated,
357
- parseFailed: true
358
- };
359
- }
360
- } catch {
361
- return {
362
- attempted: true,
363
- contentType,
364
- contentLength,
365
- parseFailed: true
366
- };
367
- }
368
- }
369
- function redactBody(value, redactPaths) {
370
- const cloned = deepClone(value);
371
- for (const path of redactPaths) {
372
- applyRedaction(cloned, path.split("."));
373
- }
374
- return cloned;
375
- }
376
- function applyRedaction(target, segments) {
377
- if (!target || segments.length === 0) return;
378
- const [segment, ...rest] = segments;
379
- if (Array.isArray(target)) {
380
- for (const item of target) {
381
- if (segment === "*") {
382
- applyRedaction(item, rest);
383
- } else {
384
- applyRedaction(item, segments);
385
- }
386
- }
387
- return;
388
- }
389
- if (typeof target !== "object") return;
390
- const record = target;
391
- if (segment === "*") {
392
- for (const value of Object.values(record)) {
393
- applyRedaction(value, rest);
394
- }
395
- return;
396
- }
397
- if (!(segment in record)) return;
398
- if (rest.length === 0) {
399
- record[segment] = REDACTED_VALUE;
400
- return;
401
- }
402
- applyRedaction(record[segment], rest);
403
- }
404
- function deepClone(value) {
405
- if (typeof structuredClone === "function") {
406
- return structuredClone(value);
407
- }
408
- return JSON.parse(JSON.stringify(value));
409
- }
410
- function createRequestSpan(runtime, name, attributes) {
411
- if (!runtime.tracer) return void 0;
412
- const span = runtime.tracer.startSpan(name, { attributes }, import_api.context.active());
413
- const spanContext = span.spanContext();
414
- return {
415
- span,
416
- traceId: spanContext.traceId,
417
- spanId: spanContext.spanId,
418
- sampled: spanContext.traceFlags === import_api.TraceFlags.SAMPLED
419
- };
420
- }
421
- function endRequestSpan(requestTrace, statusCode, error) {
422
- if (!requestTrace) return;
423
- requestTrace.span.setAttribute("http.status_code", statusCode);
424
- if (error) {
425
- requestTrace.span.recordException(error instanceof Error ? error : new Error(String(error)));
426
- }
427
- requestTrace.span.end();
428
- }
429
- function attachRequestMetrics(runtime, args) {
430
- runtime.metrics?.requestCount.add(1, {
431
- method: args.method,
432
- route: args.route,
433
- status_class: `${Math.floor(args.statusCode / 100)}xx`
434
- });
435
- runtime.metrics?.requestDuration.record(args.durationMs, {
436
- method: args.method,
437
- route: args.route,
438
- status_class: `${Math.floor(args.statusCode / 100)}xx`
439
- });
440
- }
441
- async function renderPrometheusMetrics(exporter) {
442
- let statusCode = 200;
443
- const headers = {};
444
- let body = "";
445
- await new Promise((resolve) => {
446
- const response = {
447
- statusCode,
448
- setHeader(name, value) {
449
- headers[name.toLowerCase()] = value;
450
- },
451
- end(chunk) {
452
- if (chunk) body += chunk;
453
- statusCode = this.statusCode;
454
- resolve();
455
- }
456
- };
457
- exporter.getMetricsRequestHandler({}, response);
458
- });
459
- return { body, headers, statusCode };
460
- }
461
- function normalizeHeaderNames(headers) {
462
- return headers.map((value) => value.toLowerCase());
463
- }
464
- function uniqueValues(values) {
465
- return [...new Set(values)];
466
- }
467
- function clampRate(value) {
468
- if (Number.isNaN(value)) return 0;
469
- return Math.max(0, Math.min(1, value));
470
- }
471
- function deterministicSample(seed, rate) {
472
- if (rate >= 1) return true;
473
- if (rate <= 0) return false;
474
- let hash = 0;
475
- for (let index = 0; index < seed.length; index += 1) {
476
- hash = hash * 31 + seed.charCodeAt(index) >>> 0;
477
- }
478
- return hash / 4294967295 <= rate;
479
- }
480
- function toNumber(value) {
481
- if (!value) return void 0;
482
- const parsed = Number(value);
483
- return Number.isFinite(parsed) ? parsed : void 0;
484
- }
485
- var import_api, import_exporter_metrics_otlp_http, import_exporter_prometheus, import_exporter_trace_otlp_http, import_resources, import_sdk_metrics, import_sdk_trace_base, import_pino, import_node_stream, DEFAULT_REDACT_HEADERS, DEFAULT_REDACT_PATHS, DEFAULT_MAX_BODY_BYTES, DEFAULT_SUCCESS_SAMPLE_RATE, DEFAULT_BODY_SAMPLE_RATE, REDACTED_VALUE, runtimeByConfig, fallbackLogger, OtlpLogWritable;
486
- var init_observability = __esm({
487
- "src/observability.ts"() {
488
- "use strict";
489
- import_api = require("@opentelemetry/api");
490
- import_exporter_metrics_otlp_http = require("@opentelemetry/exporter-metrics-otlp-http");
491
- import_exporter_prometheus = require("@opentelemetry/exporter-prometheus");
492
- import_exporter_trace_otlp_http = require("@opentelemetry/exporter-trace-otlp-http");
493
- import_resources = require("@opentelemetry/resources");
494
- import_sdk_metrics = require("@opentelemetry/sdk-metrics");
495
- import_sdk_trace_base = require("@opentelemetry/sdk-trace-base");
496
- import_pino = __toESM(require("pino"), 1);
497
- import_node_stream = require("stream");
498
- DEFAULT_REDACT_HEADERS = ["authorization", "cookie", "set-cookie", "x-api-key"];
499
- DEFAULT_REDACT_PATHS = [
500
- "password",
501
- "currentPassword",
502
- "newPassword",
503
- "confirmPassword",
504
- "token",
505
- "refreshToken",
506
- "accessToken",
507
- "secret",
508
- "apiKey",
509
- "inviteToken",
510
- "resetToken"
511
- ];
512
- DEFAULT_MAX_BODY_BYTES = 8192;
513
- DEFAULT_SUCCESS_SAMPLE_RATE = 0.1;
514
- DEFAULT_BODY_SAMPLE_RATE = 0.02;
515
- REDACTED_VALUE = "[REDACTED]";
516
- runtimeByConfig = /* @__PURE__ */ new WeakMap();
517
- fallbackLogger = (0, import_pino.default)({
518
- name: "dyrected",
519
- enabled: process.env.DISABLE_LOGGING !== "true",
520
- timestamp: import_pino.stdTimeFunctions.isoTime
521
- });
522
- OtlpLogWritable = class extends import_node_stream.Writable {
523
- constructor(endpoint, headers = {}) {
524
- super();
525
- this.endpoint = endpoint;
526
- this.headers = headers;
527
- }
528
- endpoint;
529
- headers;
530
- _write(chunk, _encoding, callback) {
531
- const payload = chunk.toString();
532
- void fetch(this.endpoint, {
533
- method: "POST",
534
- headers: {
535
- "content-type": "application/json",
536
- ...this.headers
537
- },
538
- body: JSON.stringify({ logs: payload.trim().split("\n").filter(Boolean) })
539
- }).then(() => callback()).catch(
540
- (error) => callback(error instanceof Error ? error : new Error(String(error)))
541
- );
542
- }
543
- };
544
- }
545
- });
546
-
547
33
  // ../../node_modules/.pnpm/@babel+runtime@7.29.7/node_modules/@babel/runtime/helpers/interopRequireDefault.js
548
34
  var require_interopRequireDefault = __commonJS({
549
35
  "../../node_modules/.pnpm/@babel+runtime@7.29.7/node_modules/@babel/runtime/helpers/interopRequireDefault.js"(exports2, module2) {
@@ -2298,12 +1784,20 @@ var require_Jexl = __commonJS({
2298
1784
  // src/index.ts
2299
1785
  var index_exports = {};
2300
1786
  __export(index_exports, {
1787
+ AI_ACTIONS_COLLECTION: () => AI_ACTIONS_COLLECTION,
1788
+ AI_AUDIT_COLLECTION: () => AI_AUDIT_COLLECTION,
1789
+ AI_CHUNKS_COLLECTION: () => AI_CHUNKS_COLLECTION,
1790
+ AI_MESSAGES_COLLECTION: () => AI_MESSAGES_COLLECTION,
1791
+ AI_THREADS_COLLECTION: () => AI_THREADS_COLLECTION,
2301
1792
  BUILTIN_JEXL_HELPERS: () => BUILTIN_JEXL_HELPERS,
2302
1793
  ConfigValidationError: () => ConfigValidationError,
1794
+ DyrectedAIError: () => DyrectedAIError,
2303
1795
  FieldConditionBuilder: () => FieldConditionBuilder,
2304
1796
  LIFECYCLE_EVENTS_COLLECTION: () => LIFECYCLE_EVENTS_COLLECTION,
2305
1797
  LIFECYCLE_EVENT_NAMES: () => LIFECYCLE_EVENT_NAMES,
2306
1798
  MatchBuilder: () => MatchBuilder,
1799
+ NON_NEGOTIABLE_CREDENTIAL_FIELDS: () => NON_NEGOTIABLE_CREDENTIAL_FIELDS,
1800
+ PII_REGEX_PATTERNS: () => PII_REGEX_PATTERNS,
2307
1801
  WORKFLOW_HISTORY_COLLECTION: () => WORKFLOW_HISTORY_COLLECTION,
2308
1802
  abs: () => abs,
2309
1803
  addDays: () => addDays,
@@ -2391,18 +1885,26 @@ __export(index_exports, {
2391
1885
  generateOpenApi: () => generateOpenApi,
2392
1886
  get: () => get,
2393
1887
  getAdminAuthCollection: () => getAdminAuthCollection,
1888
+ getAdminRoleForCollection: () => getAdminRoleForCollection,
1889
+ getAllowedSitesForUser: () => getAllowedSitesForUser,
2394
1890
  getPublicAdminAuthConfig: () => getPublicAdminAuthConfig,
2395
1891
  includes: () => includes,
2396
1892
  initializeWorkflowDocument: () => initializeWorkflowDocument,
1893
+ isAICollection: () => isAICollection,
2397
1894
  isConfigValidationError: () => isConfigValidationError,
2398
1895
  isDetailItemVisible: () => isDetailItemVisible,
2399
1896
  isEmpty: () => isEmpty,
2400
1897
  isFuture: () => isFuture,
2401
1898
  isPast: () => isPast,
1899
+ isSiteAuthorized: () => isSiteAuthorized,
1900
+ isUserAdmin: () => isUserAdmin,
2402
1901
  join: () => join,
2403
1902
  last: () => last,
2404
1903
  length: () => length,
2405
1904
  lower: () => lower,
1905
+ maskEmail: () => maskEmail,
1906
+ maskPhone: () => maskPhone,
1907
+ maskTextPII: () => maskTextPII,
2406
1908
  materializeWorkflowDocument: () => materializeWorkflowDocument,
2407
1909
  max: () => max,
2408
1910
  min: () => min,
@@ -2420,8 +1922,10 @@ __export(index_exports, {
2420
1922
  registerJexlHelpers: () => registerJexlHelpers,
2421
1923
  replace: () => replace,
2422
1924
  resolveAdminAuthCollection: () => resolveAdminAuthCollection,
1925
+ resolveAuthorizedSiteId: () => resolveAuthorizedSiteId,
2423
1926
  round: () => round,
2424
1927
  runCollectionHooks: () => runCollectionHooks,
1928
+ sanitizeDocForAI: () => sanitizeDocForAI,
2425
1929
  saveWorkflowDraft: () => saveWorkflowDraft,
2426
1930
  simplePublishingWorkflow: () => simplePublishingWorkflow,
2427
1931
  slugify: () => slugify,
@@ -2440,7 +1944,8 @@ __export(index_exports, {
2440
1944
  validatePreviewUrlExpression: () => validatePreviewUrlExpression,
2441
1945
  when: () => when,
2442
1946
  wordCount: () => wordCount,
2443
- workflowCapabilities: () => workflowCapabilities
1947
+ workflowCapabilities: () => workflowCapabilities,
1948
+ z: () => import_zod.z
2444
1949
  });
2445
1950
  module.exports = __toCommonJS(index_exports);
2446
1951
 
@@ -2460,6 +1965,40 @@ function defineAction(config) {
2460
1965
  return config;
2461
1966
  }
2462
1967
 
1968
+ // src/types/ai.ts
1969
+ var AI_THREADS_COLLECTION = "_dyrected_ai_threads";
1970
+ var AI_MESSAGES_COLLECTION = "_dyrected_ai_messages";
1971
+ var AI_CHUNKS_COLLECTION = "_dyrected_ai_chunks";
1972
+ var AI_ACTIONS_COLLECTION = "_dyrected_ai_actions";
1973
+ var AI_AUDIT_COLLECTION = "_dyrected_ai_audit";
1974
+ function isAICollection(slug) {
1975
+ return slug === AI_THREADS_COLLECTION || slug === AI_MESSAGES_COLLECTION || slug === AI_CHUNKS_COLLECTION || slug === AI_ACTIONS_COLLECTION || slug === AI_AUDIT_COLLECTION;
1976
+ }
1977
+
1978
+ // src/types/ai-errors.ts
1979
+ var DyrectedAIError = class _DyrectedAIError extends Error {
1980
+ code;
1981
+ status;
1982
+ details;
1983
+ constructor(code, message, status = 500, details) {
1984
+ super(message);
1985
+ this.name = "DyrectedAIError";
1986
+ this.code = code;
1987
+ this.status = status;
1988
+ this.details = details;
1989
+ Object.setPrototypeOf(this, _DyrectedAIError.prototype);
1990
+ }
1991
+ toJSON() {
1992
+ return {
1993
+ error: true,
1994
+ code: this.code,
1995
+ message: this.message,
1996
+ status: this.status,
1997
+ details: this.details
1998
+ };
1999
+ }
2000
+ };
2001
+
2463
2002
  // src/auth/token.ts
2464
2003
  var import_jose = require("jose");
2465
2004
 
@@ -2862,18 +2401,31 @@ async function transitionWorkflow(args) {
2862
2401
  await hook({ ...hookContext, doc: updated, event: events[0] });
2863
2402
  } catch (error) {
2864
2403
  try {
2865
- const { getConfigLogger: getConfigLogger2, getObservabilityRuntime: getObservabilityRuntime2 } = await Promise.resolve().then(() => (init_observability(), observability_exports));
2866
- getObservabilityRuntime2(config)?.recordWorkflowHookFailure({
2404
+ const obs = config?.__observability;
2405
+ obs?.recordWorkflowHookFailure?.({
2867
2406
  collection: collection.slug,
2868
2407
  transition: transition.name
2869
2408
  });
2870
- getConfigLogger2(config, "workflow").error({
2871
- err: error,
2872
- msg: "afterTransition hook failed",
2873
- collection: collection.slug,
2874
- transition: transition.name,
2875
- documentId: id
2876
- });
2409
+ const logger = config?.logger;
2410
+ if (logger && typeof logger.child === "function") {
2411
+ logger.child({ component: "workflow" }).error({
2412
+ err: error,
2413
+ msg: "afterTransition hook failed",
2414
+ collection: collection.slug,
2415
+ transition: transition.name,
2416
+ documentId: id
2417
+ });
2418
+ } else if (logger && typeof logger.error === "function") {
2419
+ logger.error({
2420
+ err: error,
2421
+ msg: "afterTransition hook failed",
2422
+ collection: collection.slug,
2423
+ transition: transition.name,
2424
+ documentId: id
2425
+ });
2426
+ } else {
2427
+ console.error("afterTransition hook failed:", error);
2428
+ }
2877
2429
  } catch {
2878
2430
  console.error("afterTransition hook failed:", error);
2879
2431
  }
@@ -2916,6 +2468,19 @@ function humanizeProviderName(id, type) {
2916
2468
  if (!cleaned) return type.toUpperCase();
2917
2469
  return cleaned.replace(/\b\w/g, (char) => char.toUpperCase());
2918
2470
  }
2471
+ function getAdminRoleForCollection(collection) {
2472
+ if (!collection?.auth) return "admin";
2473
+ if (typeof collection.auth === "object" && collection.auth.adminRole) {
2474
+ return collection.auth.adminRole;
2475
+ }
2476
+ return "admin";
2477
+ }
2478
+ function isUserAdmin(user, collection) {
2479
+ if (!user || !Array.isArray(user.roles)) return false;
2480
+ const configuredRole = getAdminRoleForCollection(collection);
2481
+ if (user.roles.includes(configuredRole)) return true;
2482
+ return user.roles.includes("admin") || user.roles.includes("super_admin");
2483
+ }
2919
2484
 
2920
2485
  // src/utils/block-references.ts
2921
2486
  function mergeUniqueBlocks(blocks) {
@@ -5023,6 +4588,182 @@ function generateOpenApi(config) {
5023
4588
  }
5024
4589
  }
5025
4590
  };
4591
+ spec.paths["/api/ai/chat"] = {
4592
+ post: {
4593
+ tags: ["AI"],
4594
+ summary: "Send prompt and stream AI assistant response",
4595
+ requestBody: {
4596
+ required: true,
4597
+ content: {
4598
+ "application/json": {
4599
+ schema: {
4600
+ type: "object",
4601
+ properties: {
4602
+ content: { type: "string", description: "User prompt message" },
4603
+ prompt: { type: "string", description: "Alias for content" },
4604
+ threadId: { type: "string", description: "Existing conversation thread ID" },
4605
+ messages: { type: "array", items: { type: "object" }, description: "Conversation history" }
4606
+ },
4607
+ required: []
4608
+ }
4609
+ }
4610
+ }
4611
+ },
4612
+ responses: {
4613
+ 200: {
4614
+ description: "Server-Sent Events (SSE) AI message stream",
4615
+ content: { "text/event-stream": { schema: { type: "string" } } }
4616
+ }
4617
+ }
4618
+ }
4619
+ };
4620
+ spec.paths["/api/ai/threads"] = {
4621
+ get: {
4622
+ tags: ["AI"],
4623
+ summary: "List conversation threads",
4624
+ parameters: [
4625
+ { name: "limit", in: "query", schema: { type: "integer", default: 20 } }
4626
+ ],
4627
+ responses: { 200: { description: "Array of conversation threads" } }
4628
+ },
4629
+ post: {
4630
+ tags: ["AI"],
4631
+ summary: "Create a new conversation thread",
4632
+ requestBody: {
4633
+ content: {
4634
+ "application/json": {
4635
+ schema: {
4636
+ type: "object",
4637
+ properties: { title: { type: "string" } }
4638
+ }
4639
+ }
4640
+ }
4641
+ },
4642
+ responses: { 201: { description: "Created conversation thread" } }
4643
+ },
4644
+ delete: {
4645
+ tags: ["AI"],
4646
+ summary: "Clear all conversation threads",
4647
+ responses: { 200: { description: "Count of deleted threads" } }
4648
+ }
4649
+ };
4650
+ spec.paths["/api/ai/threads/{threadId}"] = {
4651
+ get: {
4652
+ tags: ["AI"],
4653
+ summary: "Get conversation thread and historical messages",
4654
+ parameters: [
4655
+ { name: "threadId", in: "path", required: true, schema: { type: "string" } }
4656
+ ],
4657
+ responses: { 200: { description: "Thread details and messages" } }
4658
+ },
4659
+ delete: {
4660
+ tags: ["AI"],
4661
+ summary: "Delete a specific conversation thread",
4662
+ parameters: [
4663
+ { name: "threadId", in: "path", required: true, schema: { type: "string" } }
4664
+ ],
4665
+ responses: { 200: { description: "Deletion success status" } }
4666
+ }
4667
+ };
4668
+ spec.paths["/api/ai/threads/{threadId}/messages"] = {
4669
+ post: {
4670
+ tags: ["AI"],
4671
+ summary: "Post a message to an existing thread and stream reply",
4672
+ parameters: [
4673
+ { name: "threadId", in: "path", required: true, schema: { type: "string" } }
4674
+ ],
4675
+ requestBody: {
4676
+ required: true,
4677
+ content: {
4678
+ "application/json": {
4679
+ schema: {
4680
+ type: "object",
4681
+ properties: { content: { type: "string" } },
4682
+ required: ["content"]
4683
+ }
4684
+ }
4685
+ }
4686
+ },
4687
+ responses: {
4688
+ 200: {
4689
+ description: "Server-Sent Events (SSE) AI message stream",
4690
+ content: { "text/event-stream": { schema: { type: "string" } } }
4691
+ }
4692
+ }
4693
+ }
4694
+ };
4695
+ spec.paths["/api/ai/actions/{actionId}"] = {
4696
+ get: {
4697
+ tags: ["AI"],
4698
+ summary: "Get an AI mutation action proposal",
4699
+ parameters: [
4700
+ { name: "actionId", in: "path", required: true, schema: { type: "string" } }
4701
+ ],
4702
+ responses: { 200: { description: "AI Action details and diff payload" } }
4703
+ }
4704
+ };
4705
+ spec.paths["/api/ai/actions/{actionId}/execute"] = {
4706
+ post: {
4707
+ tags: ["AI"],
4708
+ summary: "Approve and execute an AI mutation proposal",
4709
+ parameters: [
4710
+ { name: "actionId", in: "path", required: true, schema: { type: "string" } }
4711
+ ],
4712
+ responses: { 200: { description: "Execution result" } }
4713
+ }
4714
+ };
4715
+ spec.paths["/api/ai/actions/{actionId}/reject"] = {
4716
+ post: {
4717
+ tags: ["AI"],
4718
+ summary: "Reject an AI mutation proposal",
4719
+ parameters: [
4720
+ { name: "actionId", in: "path", required: true, schema: { type: "string" } }
4721
+ ],
4722
+ responses: { 200: { description: "Rejection result" } }
4723
+ }
4724
+ };
4725
+ spec.paths["/api/ai/rag/reindex"] = {
4726
+ post: {
4727
+ tags: ["AI"],
4728
+ summary: "Trigger vector reindexing for semantic search",
4729
+ requestBody: {
4730
+ content: {
4731
+ "application/json": {
4732
+ schema: {
4733
+ type: "object",
4734
+ properties: {
4735
+ collection: { type: "string" },
4736
+ force: { type: "boolean" }
4737
+ }
4738
+ }
4739
+ }
4740
+ }
4741
+ },
4742
+ responses: { 200: { description: "Reindexing results and chunk counts" } }
4743
+ }
4744
+ };
4745
+ spec.paths["/api/ai/rag/search"] = {
4746
+ post: {
4747
+ tags: ["AI"],
4748
+ summary: "Perform semantic vector search against indexed content",
4749
+ requestBody: {
4750
+ required: true,
4751
+ content: {
4752
+ "application/json": {
4753
+ schema: {
4754
+ type: "object",
4755
+ properties: {
4756
+ query: { type: "string" },
4757
+ limit: { type: "integer", default: 5 }
4758
+ },
4759
+ required: ["query"]
4760
+ }
4761
+ }
4762
+ }
4763
+ },
4764
+ responses: { 200: { description: "Semantic search results" } }
4765
+ }
4766
+ };
5026
4767
  for (const collection of config.collections) {
5027
4768
  spec.components.schemas[collection.slug] = collectionToSchema(collection);
5028
4769
  }
@@ -6496,7 +6237,240 @@ function createExpr() {
6496
6237
  var expr = createExpr();
6497
6238
  var when = expr;
6498
6239
 
6240
+ // src/utils/tenant.ts
6241
+ var import_http_exception = require("hono/http-exception");
6242
+ function getAllowedSitesForUser(user, payload) {
6243
+ const raw = user?.allowedSites ?? user?.siteIds ?? user?.siteAccess ?? (user?.siteId ? [user.siteId] : void 0) ?? payload?.allowedSites ?? payload?.siteIds ?? payload?.siteAccess ?? (payload?.siteId ? [payload.siteId] : void 0);
6244
+ if (Array.isArray(raw)) {
6245
+ return raw.filter((s) => typeof s === "string" && s.trim().length > 0);
6246
+ }
6247
+ return void 0;
6248
+ }
6249
+ function isSiteAuthorized(user, payload, requestedSiteId) {
6250
+ const allowedSites = getAllowedSitesForUser(user, payload);
6251
+ if (!allowedSites || allowedSites.length === 0) {
6252
+ return true;
6253
+ }
6254
+ if (!requestedSiteId) {
6255
+ return false;
6256
+ }
6257
+ return allowedSites.includes(requestedSiteId);
6258
+ }
6259
+ function resolveAuthorizedSiteId(c) {
6260
+ const user = typeof c.get === "function" ? c.get("user") : void 0;
6261
+ const tokenPayload = typeof c.get === "function" ? c.get("authTokenPayload") : void 0;
6262
+ const requestedSiteId = (typeof c.req?.header === "function" ? c.req.header("X-Site-Id") : void 0) || (typeof c.get === "function" ? c.get("siteId") : void 0);
6263
+ const allowedSites = getAllowedSitesForUser(user, tokenPayload);
6264
+ if (Array.isArray(allowedSites) && allowedSites.length > 0) {
6265
+ if (!requestedSiteId) {
6266
+ return allowedSites[0];
6267
+ }
6268
+ if (!allowedSites.includes(requestedSiteId)) {
6269
+ throw new import_http_exception.HTTPException(403, {
6270
+ message: `Forbidden: User is not authorized to access site "${requestedSiteId}".`
6271
+ });
6272
+ }
6273
+ return requestedSiteId;
6274
+ }
6275
+ return requestedSiteId || "default";
6276
+ }
6277
+
6278
+ // src/utils/ai-pii.ts
6279
+ var NON_NEGOTIABLE_CREDENTIAL_FIELDS = /* @__PURE__ */ new Set([
6280
+ "password",
6281
+ "salt",
6282
+ "hash",
6283
+ "resetPasswordToken",
6284
+ "apiKey",
6285
+ "token",
6286
+ "secret",
6287
+ "accessToken",
6288
+ "refreshToken",
6289
+ "privateKey",
6290
+ "private_key",
6291
+ "webhookSecret",
6292
+ "webhook_secret",
6293
+ "authSecret",
6294
+ "auth_secret",
6295
+ "cvv",
6296
+ "cvc",
6297
+ "pin"
6298
+ ]);
6299
+ function maskEmail(email, strategy = "mask") {
6300
+ if (!email || typeof email !== "string") return email;
6301
+ if (strategy === "token") return "[REDACTED_EMAIL]";
6302
+ const atIdx = email.indexOf("@");
6303
+ if (atIdx <= 1) {
6304
+ return "***@" + (email.slice(atIdx + 1) || "example.com");
6305
+ }
6306
+ const prefix = email[0];
6307
+ const domain = email.slice(atIdx);
6308
+ return `${prefix}***${domain}`;
6309
+ }
6310
+ function maskPhone(phone, strategy = "mask") {
6311
+ if (!phone || typeof phone !== "string") return phone;
6312
+ if (strategy === "token") return "[REDACTED_PHONE]";
6313
+ const digits = phone.replace(/\D/g, "");
6314
+ if (digits.length < 4) return "***-***-****";
6315
+ const lastFour = digits.slice(-4);
6316
+ return `***-***-${lastFour}`;
6317
+ }
6318
+ var PII_REGEX_PATTERNS = {
6319
+ email: /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g,
6320
+ phone_number: /(?:\+?\d{1,3}[-\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-\s]?\d{4}\b/g,
6321
+ phoneNumber: /(?:\+?\d{1,3}[-\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-\s]?\d{4}\b/g,
6322
+ phone: /(?:\+?\d{1,3}[-\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-\s]?\d{4}\b/g,
6323
+ credit_card: /\b(?:\d{4}[-\s]?){3}\d{4}\b/g,
6324
+ ssn: /\b\d{3}-\d{2}-\d{4}\b/g,
6325
+ ipv4: /\b(?:\d{1,3}\.){3}\d{1,3}\b/g,
6326
+ ipv6: /(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,7}:|(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}/g,
6327
+ iban: /\b[A-Z]{2}\d{2}[A-Z0-9]{4}\d{7}([A-Z0-9]?){0,16}\b/g,
6328
+ api_key: /\b(?:sk_(?:live|test)_[0-9a-zA-Z_]{16,}|ghp_[0-9a-zA-Z_]{20,}|AKIA[0-9A-Z]{16})\b/g,
6329
+ jwt: /\beyJ[a-zA-Z0-9_-]+\.eyJ[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+\b/g,
6330
+ passport: /\b[A-Z]{1,2}[0-9]{6,9}\b/g
6331
+ };
6332
+ var PII_PATTERN_PRIORITY = {
6333
+ api_key: 1,
6334
+ jwt: 2,
6335
+ iban: 3,
6336
+ credit_card: 4,
6337
+ ipv6: 5,
6338
+ ipv4: 6,
6339
+ email: 7,
6340
+ passport: 8,
6341
+ ssn: 9,
6342
+ phone: 10,
6343
+ phone_number: 10,
6344
+ phoneNumber: 10
6345
+ };
6346
+ function maskTextPII(text, piiConfig) {
6347
+ if (!text || typeof text !== "string") return text;
6348
+ if (!piiConfig || piiConfig.enabled === false) return text;
6349
+ let result = text;
6350
+ const strategy = piiConfig.strategy || "mask";
6351
+ const rawPatterns = piiConfig.patterns || [
6352
+ "email",
6353
+ "phone",
6354
+ "phone_number",
6355
+ "phoneNumber",
6356
+ "ssn",
6357
+ "credit_card",
6358
+ "ipv4"
6359
+ ];
6360
+ const patterns = [...rawPatterns].sort(
6361
+ (a, b) => (PII_PATTERN_PRIORITY[a] ?? 99) - (PII_PATTERN_PRIORITY[b] ?? 99)
6362
+ );
6363
+ for (const patternName of patterns) {
6364
+ const regex = PII_REGEX_PATTERNS[patternName];
6365
+ if (!regex) continue;
6366
+ result = result.replace(regex, (match) => {
6367
+ switch (patternName) {
6368
+ case "email":
6369
+ return maskEmail(match, strategy);
6370
+ case "phone":
6371
+ case "phone_number":
6372
+ case "phoneNumber":
6373
+ return maskPhone(match, strategy);
6374
+ case "credit_card":
6375
+ return strategy === "token" ? "[REDACTED_CREDIT_CARD]" : "****-****-****-" + match.replace(/\D/g, "").slice(-4);
6376
+ case "ssn":
6377
+ return strategy === "token" ? "[REDACTED_SSN]" : "***-**-" + match.replace(/\D/g, "").slice(-4);
6378
+ case "ipv4":
6379
+ return "[REDACTED_IP]";
6380
+ case "ipv6":
6381
+ return "[REDACTED_IPV6]";
6382
+ case "iban":
6383
+ return strategy === "token" ? "[REDACTED_IBAN]" : "****-****-" + match.replace(/\s/g, "").slice(-4);
6384
+ case "api_key":
6385
+ return "[REDACTED_API_KEY]";
6386
+ case "jwt":
6387
+ return "[REDACTED_JWT]";
6388
+ case "passport":
6389
+ return strategy === "token" ? "[REDACTED_PASSPORT]" : "***" + match.slice(-3);
6390
+ default:
6391
+ return "[REDACTED]";
6392
+ }
6393
+ });
6394
+ }
6395
+ if (typeof piiConfig.customScrubber === "function") {
6396
+ result = piiConfig.customScrubber(result);
6397
+ }
6398
+ return result;
6399
+ }
6400
+ function sanitizeDocForAI(options) {
6401
+ const { doc, collectionConfig, globalAIConfig } = options;
6402
+ if (!doc || typeof doc !== "object") return doc;
6403
+ const copy = { ...doc };
6404
+ for (const cred of NON_NEGOTIABLE_CREDENTIAL_FIELDS) {
6405
+ delete copy[cred];
6406
+ }
6407
+ if (collectionConfig?.ai?.excludeFields) {
6408
+ for (const field of collectionConfig.ai.excludeFields) {
6409
+ delete copy[field];
6410
+ }
6411
+ }
6412
+ const fields = collectionConfig?.fields || [];
6413
+ for (const field of fields) {
6414
+ const fieldName = field.name;
6415
+ if (!fieldName || copy[fieldName] === void 0) continue;
6416
+ if (field.ai?.exclude === true) {
6417
+ delete copy[fieldName];
6418
+ continue;
6419
+ }
6420
+ const val = copy[fieldName];
6421
+ const normalizedName = fieldName.toLowerCase().replace(/[_\s-]/g, "");
6422
+ const isEmail = field.type === "email" || normalizedName.includes("email");
6423
+ const isPhone = field.type === "phone" || normalizedName.includes("phone") || normalizedName === "tel" || normalizedName === "telephone" || normalizedName === "mobile" || normalizedName === "cell";
6424
+ if (isEmail) {
6425
+ if (globalAIConfig?.pii?.enabled !== false && field.ai?.allowRaw !== true && typeof val === "string") {
6426
+ copy[fieldName] = maskEmail(val, globalAIConfig?.pii?.strategy);
6427
+ }
6428
+ continue;
6429
+ }
6430
+ if (isPhone) {
6431
+ if (globalAIConfig?.pii?.enabled !== false && field.ai?.allowRaw !== true && typeof val === "string") {
6432
+ copy[fieldName] = maskPhone(val, globalAIConfig?.pii?.strategy);
6433
+ }
6434
+ continue;
6435
+ }
6436
+ if (field.ai?.redact === true || field.ai?.redact === "mask") {
6437
+ if (typeof val === "string") {
6438
+ copy[fieldName] = "[REDACTED]";
6439
+ }
6440
+ }
6441
+ }
6442
+ if (collectionConfig?.ai?.redactFields) {
6443
+ for (const field of collectionConfig.ai.redactFields) {
6444
+ if (copy[field] !== void 0) {
6445
+ const val = copy[field];
6446
+ if (typeof val === "string") {
6447
+ if (field.toLowerCase().includes("email")) {
6448
+ copy[field] = maskEmail(val, globalAIConfig?.pii?.strategy);
6449
+ } else if (field.toLowerCase().includes("phone")) {
6450
+ copy[field] = maskPhone(val, globalAIConfig?.pii?.strategy);
6451
+ } else {
6452
+ copy[field] = "[REDACTED]";
6453
+ }
6454
+ }
6455
+ }
6456
+ }
6457
+ }
6458
+ let result = copy;
6459
+ if (typeof collectionConfig?.ai?.sanitizeDoc === "function") {
6460
+ result = collectionConfig.ai.sanitizeDoc(copy);
6461
+ }
6462
+ if (globalAIConfig?.pii?.enabled === true) {
6463
+ for (const [k, v] of Object.entries(result)) {
6464
+ if (typeof v === "string") {
6465
+ result[k] = maskTextPII(v, globalAIConfig.pii);
6466
+ }
6467
+ }
6468
+ }
6469
+ return result;
6470
+ }
6471
+
6499
6472
  // src/index.ts
6473
+ var import_zod = require("zod");
6500
6474
  function defineCollection(config) {
6501
6475
  return config;
6502
6476
  }
@@ -6545,12 +6519,20 @@ function defineTab(args) {
6545
6519
  }
6546
6520
  // Annotate the CommonJS export names for ESM import in node:
6547
6521
  0 && (module.exports = {
6522
+ AI_ACTIONS_COLLECTION,
6523
+ AI_AUDIT_COLLECTION,
6524
+ AI_CHUNKS_COLLECTION,
6525
+ AI_MESSAGES_COLLECTION,
6526
+ AI_THREADS_COLLECTION,
6548
6527
  BUILTIN_JEXL_HELPERS,
6549
6528
  ConfigValidationError,
6529
+ DyrectedAIError,
6550
6530
  FieldConditionBuilder,
6551
6531
  LIFECYCLE_EVENTS_COLLECTION,
6552
6532
  LIFECYCLE_EVENT_NAMES,
6553
6533
  MatchBuilder,
6534
+ NON_NEGOTIABLE_CREDENTIAL_FIELDS,
6535
+ PII_REGEX_PATTERNS,
6554
6536
  WORKFLOW_HISTORY_COLLECTION,
6555
6537
  abs,
6556
6538
  addDays,
@@ -6638,18 +6620,26 @@ function defineTab(args) {
6638
6620
  generateOpenApi,
6639
6621
  get,
6640
6622
  getAdminAuthCollection,
6623
+ getAdminRoleForCollection,
6624
+ getAllowedSitesForUser,
6641
6625
  getPublicAdminAuthConfig,
6642
6626
  includes,
6643
6627
  initializeWorkflowDocument,
6628
+ isAICollection,
6644
6629
  isConfigValidationError,
6645
6630
  isDetailItemVisible,
6646
6631
  isEmpty,
6647
6632
  isFuture,
6648
6633
  isPast,
6634
+ isSiteAuthorized,
6635
+ isUserAdmin,
6649
6636
  join,
6650
6637
  last,
6651
6638
  length,
6652
6639
  lower,
6640
+ maskEmail,
6641
+ maskPhone,
6642
+ maskTextPII,
6653
6643
  materializeWorkflowDocument,
6654
6644
  max,
6655
6645
  min,
@@ -6667,8 +6657,10 @@ function defineTab(args) {
6667
6657
  registerJexlHelpers,
6668
6658
  replace,
6669
6659
  resolveAdminAuthCollection,
6660
+ resolveAuthorizedSiteId,
6670
6661
  round,
6671
6662
  runCollectionHooks,
6663
+ sanitizeDocForAI,
6672
6664
  saveWorkflowDraft,
6673
6665
  simplePublishingWorkflow,
6674
6666
  slugify,
@@ -6687,5 +6679,6 @@ function defineTab(args) {
6687
6679
  validatePreviewUrlExpression,
6688
6680
  when,
6689
6681
  wordCount,
6690
- workflowCapabilities
6682
+ workflowCapabilities,
6683
+ z
6691
6684
  });