target-supabase-sdk 0.2.23 → 0.2.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/README.md +3 -3
  2. package/dist/browser.d.ts +3 -1
  3. package/dist/browser.d.ts.map +1 -1
  4. package/dist/browser.js +3 -2
  5. package/dist/browser.js.map +1 -1
  6. package/dist/chunks/{enable-C7lFooTG.js → enable-DkiKPHDl.js} +20 -121
  7. package/dist/chunks/enable-DkiKPHDl.js.map +1 -0
  8. package/dist/chunks/{list.api-BzbTJBsM.js → list.api-B-aM4vzv.js} +46 -10
  9. package/dist/chunks/list.api-B-aM4vzv.js.map +1 -0
  10. package/dist/chunks/{task-queue-Ba8S21I6.js → task-queue-BYfO0Nzm.js} +212 -28
  11. package/dist/chunks/task-queue-BYfO0Nzm.js.map +1 -0
  12. package/dist/link/index.d.ts +2 -0
  13. package/dist/link/index.d.ts.map +1 -1
  14. package/dist/link/link.query.d.ts +9 -0
  15. package/dist/link/link.query.d.ts.map +1 -0
  16. package/dist/node.d.ts +2 -0
  17. package/dist/node.d.ts.map +1 -1
  18. package/dist/node.js +157 -27
  19. package/dist/node.js.map +1 -1
  20. package/dist/preload.js +3 -3
  21. package/dist/shared/feed/feed.query.d.ts.map +1 -1
  22. package/dist/shared/idea/idea.build.d.ts +35 -0
  23. package/dist/shared/idea/idea.build.d.ts.map +1 -0
  24. package/dist/shared/idea/idea.interface.d.ts +23 -0
  25. package/dist/shared/idea/idea.interface.d.ts.map +1 -0
  26. package/dist/shared/idea/idea.normalize.d.ts +3 -0
  27. package/dist/shared/idea/idea.normalize.d.ts.map +1 -0
  28. package/dist/shared/idea/idea.parse.d.ts +3 -0
  29. package/dist/shared/idea/idea.parse.d.ts.map +1 -0
  30. package/dist/shared/idea/idea.query.d.ts +6 -0
  31. package/dist/shared/idea/idea.query.d.ts.map +1 -0
  32. package/dist/shared/idea/idea.schema.d.ts +60 -0
  33. package/dist/shared/idea/idea.schema.d.ts.map +1 -0
  34. package/dist/shared/idea/index.d.ts +15 -0
  35. package/dist/shared/idea/index.d.ts.map +1 -0
  36. package/dist/shared/idea/node/idea.hash.d.ts +3 -0
  37. package/dist/shared/idea/node/idea.hash.d.ts.map +1 -0
  38. package/dist/shared/idea/node/idea.locator.d.ts +9 -0
  39. package/dist/shared/idea/node/idea.locator.d.ts.map +1 -0
  40. package/dist/shared/idea/node/index.d.ts +3 -0
  41. package/dist/shared/idea/node/index.d.ts.map +1 -0
  42. package/dist/shared/log/core/log-batch.d.ts +4 -4
  43. package/dist/shared/log/core/log-batch.d.ts.map +1 -1
  44. package/dist/shared/log/index.d.ts +2 -1
  45. package/dist/shared/log/index.d.ts.map +1 -1
  46. package/dist/shared/log/spool/collector.d.ts +1 -1
  47. package/dist/shared/log/spool/collector.d.ts.map +1 -1
  48. package/dist/shared/log/spool/file.d.ts +2 -8
  49. package/dist/shared/log/spool/file.d.ts.map +1 -1
  50. package/dist/shared/log/spool/writer.d.ts.map +1 -1
  51. package/dist/shared/log/upload/flush.d.ts.map +1 -1
  52. package/dist/shared/log/upload/interface.d.ts +3 -11
  53. package/dist/shared/log/upload/interface.d.ts.map +1 -1
  54. package/dist/shared/media/media.query.d.ts.map +1 -1
  55. package/dist/shared/review/review.query.d.ts.map +1 -1
  56. package/dist/shared/utils/config-path.utils.d.ts +1 -1
  57. package/dist/shared/utils/postgrest.utils.d.ts +1 -1
  58. package/dist/task/index.d.ts +1 -1
  59. package/dist/task/local-task-registry.d.ts +1 -1
  60. package/dist/trigger/trigger.api.d.ts +6 -1
  61. package/dist/trigger/trigger.api.d.ts.map +1 -1
  62. package/package.json +1 -1
  63. package/src/browser.ts +23 -2
  64. package/src/link/index.ts +2 -0
  65. package/src/link/link.query.ts +18 -0
  66. package/src/node.ts +8 -0
  67. package/src/shared/feed/README.md +1 -1
  68. package/src/shared/feed/feed.query.ts +2 -7
  69. package/src/shared/idea/README.md +82 -0
  70. package/src/shared/idea/VALIDATION-MATRIX.md +69 -0
  71. package/src/shared/idea/idea.build.ts +160 -0
  72. package/src/shared/idea/idea.interface.ts +34 -0
  73. package/src/shared/idea/idea.normalize.ts +17 -0
  74. package/src/shared/idea/idea.parse.ts +16 -0
  75. package/src/shared/idea/idea.query.ts +16 -0
  76. package/src/shared/idea/idea.schema.ts +32 -0
  77. package/src/shared/idea/index.ts +21 -0
  78. package/src/shared/idea/node/idea.hash.ts +6 -0
  79. package/src/shared/idea/node/idea.locator.ts +61 -0
  80. package/src/shared/idea/node/index.ts +7 -0
  81. package/src/shared/log/README.md +2 -3
  82. package/src/shared/log/core/log-batch.ts +12 -10
  83. package/src/shared/log/index.ts +2 -1
  84. package/src/shared/log/spool/collector.ts +46 -20
  85. package/src/shared/log/spool/file.ts +12 -25
  86. package/src/shared/log/spool/writer.ts +2 -7
  87. package/src/shared/log/upload/flush.ts +23 -38
  88. package/src/shared/log/upload/interface.ts +3 -12
  89. package/src/shared/media/README.md +1 -1
  90. package/src/shared/media/media.query.ts +2 -7
  91. package/src/shared/review/review.query.ts +2 -7
  92. package/src/shared/utils/config-path.utils.ts +1 -1
  93. package/src/shared/utils/postgrest.utils.ts +1 -1
  94. package/src/task/index.ts +1 -1
  95. package/src/task/local-task-registry.ts +1 -1
  96. package/src/trigger/trigger.api.ts +6 -1
  97. package/dist/chunks/enable-C7lFooTG.js.map +0 -1
  98. package/dist/chunks/list.api-BzbTJBsM.js.map +0 -1
  99. package/dist/chunks/task-queue-Ba8S21I6.js.map +0 -1
  100. package/dist/resource/resource.interface.d.ts +0 -20
  101. package/dist/resource/resource.interface.d.ts.map +0 -1
  102. package/dist/shared/log/upload/batch-id.d.ts +0 -13
  103. package/dist/shared/log/upload/batch-id.d.ts.map +0 -1
  104. package/dist/task/task.utils.d.ts +0 -1
  105. package/dist/task/task.utils.d.ts.map +0 -1
  106. package/src/resource/resource.interface.ts +0 -37
  107. package/src/shared/log/upload/batch-id.ts +0 -43
  108. package/src/task/task.utils.ts +0 -0
@@ -1,7 +1,6 @@
1
- import { access, constants, readdir, readFile, rename, stat, unlink } from "node:fs/promises";
1
+ import { access, constants, readdir, readFile, rename, stat, unlink, writeFile } from "node:fs/promises";
2
2
  import { join } from "node:path";
3
3
  import { isLogEntry } from "../core/log-batch";
4
- import { computeLogBatchIdempotencyKey } from "../upload/batch-id";
5
4
  import { buildLogListDraft, postLogBatch } from "../upload/flush";
6
5
  import { spoolLogger } from "../upload/logger";
7
6
  import { type DEFAULT_LOG_SPOOL_CONFIG, resolveLogSpoolConfig } from "./config";
@@ -11,6 +10,7 @@ import { logSpoolProcessDir, resolveLogSpoolRoot } from "./paths";
11
10
  import { resolveAllLogSpoolProcessRoles } from "./process-roles";
12
11
 
13
12
  const UPLOAD_LANE = "slow";
13
+ const MAX_UPLOADS_PER_COLLECT_TICK = 20;
14
14
 
15
15
  /** Per serviceId — guard collect-log tick runs GC at most once per `gc.intervalMs`. */
16
16
  const lastGcAtMsByServiceId = new Map<string, number>();
@@ -61,6 +61,10 @@ function processMetaKey(serviceId: string, processRole: LogSpoolProcessRole): st
61
61
  return `${serviceId}:${processRole}`;
62
62
  }
63
63
 
64
+ function syncedMarkerPath(jsonPath: string): string {
65
+ return jsonPath.replace(/\.json$/, ".synced");
66
+ }
67
+
64
68
  async function uploadTmpBatch(input: {
65
69
  serviceId: string;
66
70
  serviceValue: string;
@@ -78,6 +82,7 @@ async function uploadTmpBatch(input: {
78
82
 
79
83
  const jsonFileName = input.fileName.replace(/\.tmp$/, ".json");
80
84
  const jsonPath = join(logSpoolProcessDir(input.spoolRoot, input.serviceId, input.processRole), jsonFileName);
85
+ const syncedPath = syncedMarkerPath(jsonPath);
81
86
 
82
87
  if (await pathExists(jsonPath)) {
83
88
  try {
@@ -92,6 +97,19 @@ async function uploadTmpBatch(input: {
92
97
  return "skipped";
93
98
  }
94
99
 
100
+ if (await pathExists(syncedPath)) {
101
+ try {
102
+ await rename(input.tmpPath, jsonPath);
103
+ } catch {
104
+ try {
105
+ await unlink(input.tmpPath);
106
+ } catch {
107
+ // best-effort
108
+ }
109
+ }
110
+ return "skipped";
111
+ }
112
+
95
113
  const batch = await readBatchTmpFile(input.tmpPath);
96
114
  if (batch == null) {
97
115
  spoolLogger.warn("collect-log skipped — tmp failed validation", {
@@ -108,19 +126,10 @@ async function uploadTmpBatch(input: {
108
126
  }
109
127
 
110
128
  const processKey = processMetaKey(input.serviceId, input.processRole);
111
- // TODO: `lane` is legacy transport metadata — scheduled for removal; fixed to "slow" for downstream decode compatibility.
112
- const idempotencyKey = computeLogBatchIdempotencyKey({
113
- service: input.serviceValue,
114
- process: processKey,
115
- lane: UPLOAD_LANE,
116
- entries: batch.entries,
117
- });
118
-
119
129
  const draft = buildLogListDraft({
120
130
  service: input.serviceValue,
121
131
  process: processKey,
122
132
  lane: UPLOAD_LANE,
123
- idempotencyKey,
124
133
  entries: batch.entries,
125
134
  });
126
135
 
@@ -136,8 +145,20 @@ async function uploadTmpBatch(input: {
136
145
  return "failed";
137
146
  }
138
147
 
148
+ try {
149
+ await writeFile(syncedPath, `${Date.now()}\n`, "utf8");
150
+ } catch (error) {
151
+ const message = error instanceof Error ? error.message : String(error);
152
+ spoolLogger.warn("collect-log synced marker write failed", { syncedPath, error: message });
153
+ }
154
+
139
155
  try {
140
156
  await rename(input.tmpPath, jsonPath);
157
+ try {
158
+ await unlink(syncedPath);
159
+ } catch {
160
+ // best-effort
161
+ }
141
162
  } catch (error) {
142
163
  const message = error instanceof Error ? error.message : String(error);
143
164
  spoolLogger.error("collect-log rename tmp→json failed after upload", {
@@ -145,14 +166,13 @@ async function uploadTmpBatch(input: {
145
166
  jsonPath,
146
167
  error: message,
147
168
  });
148
- return "failed";
169
+ return "uploaded";
149
170
  }
150
171
 
151
172
  spoolLogger.info("collect-log uploaded batch", {
152
173
  processRole: input.processRole,
153
174
  fileName: jsonFileName,
154
175
  entryCount: batch.entries.length,
155
- idempotencyKey,
156
176
  });
157
177
 
158
178
  return "uploaded";
@@ -217,22 +237,22 @@ async function gcSyncedJsonFiles(input: {
217
237
  }
218
238
 
219
239
  for (const name of names) {
220
- if (!name.endsWith(".json")) {
240
+ if (!name.endsWith(".json") && !name.endsWith(".synced")) {
221
241
  continue;
222
242
  }
223
- if (parseLogSpoolBatchFilename(name) == null) {
243
+ if (parseLogSpoolBatchFilename(name) == null && !name.endsWith(".synced")) {
224
244
  continue;
225
245
  }
226
246
 
227
- const jsonPath = join(processDir, name);
247
+ const filePath = join(processDir, name);
228
248
  try {
229
- const fileStat = await stat(jsonPath);
249
+ const fileStat = await stat(filePath);
230
250
  if (input.nowMs - fileStat.mtimeMs <= input.syncedRetentionMs) {
231
251
  continue;
232
252
  }
233
- await unlink(jsonPath);
253
+ await unlink(filePath);
234
254
  removed += 1;
235
- spoolLogger.info("collect-log gc removed synced json", {
255
+ spoolLogger.info("collect-log gc removed synced file", {
236
256
  fileName: name,
237
257
  processRole,
238
258
  });
@@ -256,7 +276,7 @@ function shouldRunGc(serviceId: string, nowMs: number, intervalMs: number): bool
256
276
 
257
277
  /**
258
278
  * Guard-only: upload pending `.tmp` batches, rename to `.json`, then periodic GC.
259
- * Upload completion is implied by `.json` presence; no sync-state file.
279
+ * Post success is tracked via `.json` or `.synced` marker (prevents duplicate DB inserts).
260
280
  */
261
281
  export async function runCollectLogTick(input: RunCollectLogTickInput): Promise<RunCollectLogTickResult> {
262
282
  const config = resolveLogSpoolConfig(input.config);
@@ -271,6 +291,8 @@ export async function runCollectLogTick(input: RunCollectLogTickInput): Promise<
271
291
  gcSyncedRemoved: 0,
272
292
  };
273
293
 
294
+ let uploadsThisTick = 0;
295
+
274
296
  for (const processRole of resolveAllLogSpoolProcessRoles()) {
275
297
  const processDir = logSpoolProcessDir(spoolRoot, input.serviceId, processRole);
276
298
  let names: string[] = [];
@@ -284,6 +306,9 @@ export async function runCollectLogTick(input: RunCollectLogTickInput): Promise<
284
306
  if (!name.endsWith(".tmp")) {
285
307
  continue;
286
308
  }
309
+ if (uploadsThisTick >= MAX_UPLOADS_PER_COLLECT_TICK) {
310
+ break;
311
+ }
287
312
  const status = await uploadTmpBatch({
288
313
  serviceId: input.serviceId,
289
314
  serviceValue: input.serviceValue,
@@ -295,6 +320,7 @@ export async function runCollectLogTick(input: RunCollectLogTickInput): Promise<
295
320
  });
296
321
  if (status === "uploaded") {
297
322
  result.uploaded += 1;
323
+ uploadsThisTick += 1;
298
324
  } else if (status === "skipped") {
299
325
  result.skipped += 1;
300
326
  } else {
@@ -1,37 +1,24 @@
1
- import type { LogEntry } from "../core/log-manager";
2
- import { computeLogBatchIdempotencyKey } from "../upload/batch-id";
1
+ import { randomBytes } from "node:crypto";
3
2
  import type { LogSpoolProcessRole } from "./interface";
4
3
 
5
4
  /** `role.timestamp.hash.tmp|json` — role is lowercase alphanumeric + hyphen. */
6
- const BATCH_FILE_PATTERN = /^([a-z][a-z0-9-]*)\.(\d{14})\.([a-f0-9]{8})\.(tmp|json)$/;
5
+ const BATCH_FILE_PATTERN = /^([a-z][a-z0-9-]*)\.(\d{14})\.([a-f0-9]{8})\.(tmp|json|synced)$/;
7
6
 
8
7
  export function formatLogSpoolBatchTimestamp(ms: number): string {
9
8
  const d = new Date(ms);
10
9
  const pad = (n: number) => String(n).padStart(2, "0");
11
10
  return (
12
- `${d.getFullYear()}` +
13
- `${pad(d.getMonth() + 1)}` +
14
- `${pad(d.getDate())}` +
15
- `${pad(d.getHours())}` +
16
- `${pad(d.getMinutes())}` +
17
- `${pad(d.getSeconds())}`
11
+ `${d.getUTCFullYear()}` +
12
+ `${pad(d.getUTCMonth() + 1)}` +
13
+ `${pad(d.getUTCDate())}` +
14
+ `${pad(d.getUTCHours())}` +
15
+ `${pad(d.getUTCMinutes())}` +
16
+ `${pad(d.getUTCSeconds())}`
18
17
  );
19
18
  }
20
19
 
21
- export function computeLogSpoolShortHash(input: {
22
- serviceValue: string;
23
- serviceId: string;
24
- processRole: LogSpoolProcessRole;
25
- entries: LogEntry[];
26
- }): string {
27
- const processKey = `${input.serviceId}:${input.processRole}`;
28
- const idempotencyKey = computeLogBatchIdempotencyKey({
29
- service: input.serviceValue,
30
- process: processKey,
31
- lane: "slow",
32
- entries: input.entries,
33
- });
34
- return idempotencyKey.slice(0, 8);
20
+ export function randomLogSpoolShortId(): string {
21
+ return randomBytes(4).toString("hex");
35
22
  }
36
23
 
37
24
  export function buildLogSpoolBatchBaseName(input: {
@@ -47,7 +34,7 @@ export function parseLogSpoolBatchFilename(filename: string): {
47
34
  processRole: LogSpoolProcessRole;
48
35
  timestampMs: number;
49
36
  shortHash: string;
50
- extension: "tmp" | "json";
37
+ extension: "tmp" | "json" | "synced";
51
38
  } | null {
52
39
  const match = BATCH_FILE_PATTERN.exec(filename);
53
40
  if (match == null) {
@@ -57,7 +44,7 @@ export function parseLogSpoolBatchFilename(filename: string): {
57
44
  const processRole = match[1] as LogSpoolProcessRole;
58
45
  const tsRaw = match[2];
59
46
  const shortHash = match[3];
60
- const extension = match[4] as "tmp" | "json";
47
+ const extension = match[4] as "tmp" | "json" | "synced";
61
48
 
62
49
  const year = Number(tsRaw.slice(0, 4));
63
50
  const month = Number(tsRaw.slice(4, 6));
@@ -5,7 +5,7 @@ import { estimateEntriesBytes, takeEntriesWithinBytes } from "../upload/flush";
5
5
  import { registerLogPersistOffer } from "../upload/hook";
6
6
  import { isLogPersistInternalTopic, spoolLogger } from "../upload/logger";
7
7
  import { DEFAULT_LOG_SPOOL_CONFIG, resolveLogSpoolConfig } from "./config";
8
- import { buildLogSpoolBatchBaseName, computeLogSpoolShortHash } from "./file";
8
+ import { buildLogSpoolBatchBaseName, randomLogSpoolShortId } from "./file";
9
9
  import type { EnableLogSpoolOptions, LogSpoolBatchFile, LogSpoolProcessRole, LogSpoolWriterStats } from "./interface";
10
10
  import { logSpoolProcessDir, resolveLogSpoolRoot } from "./paths";
11
11
 
@@ -224,12 +224,7 @@ class LogSpoolWriter {
224
224
  spoolRoot: string;
225
225
  }): Promise<{ fileName: string; tmpPath: string }> {
226
226
  const first = input.entries[0];
227
- const shortHash = computeLogSpoolShortHash({
228
- serviceValue: input.serviceValue,
229
- serviceId: input.serviceId,
230
- processRole: input.processRole,
231
- entries: input.entries,
232
- });
227
+ const shortHash = randomLogSpoolShortId();
233
228
  const baseName = buildLogSpoolBatchBaseName({
234
229
  processRole: input.processRole,
235
230
  firstEntryTimestamp: first.timestamp,
@@ -1,9 +1,8 @@
1
- import { isCreateTargetAlreadyExistsError } from "../../../core.api";
2
- import { postLogListCreate } from "../../../list/list.api";
1
+ import { postListCreate } from "../../../list/list.api";
3
2
  import { CategoryList } from "../../../list/list.interface";
4
3
  import type { LogEntry } from "../core/log-manager";
5
4
  import type { BuildLogListDraftInput, LogListDraft } from "./interface";
6
- import { LOG_PERSIST_LOADER_KEY } from "./interface";
5
+ import { LOG_BATCH_LIST_VALUE } from "./interface";
7
6
  import { persistLogger } from "./logger";
8
7
 
9
8
  export function estimateEntriesBytes(entries: LogEntry[]): number {
@@ -18,19 +17,18 @@ export function estimateEntriesBytes(entries: LogEntry[]): number {
18
17
  }
19
18
 
20
19
  export function buildLogListDraft(input: BuildLogListDraftInput): LogListDraft {
21
- const { service, process, lane, idempotencyKey, entries } = input;
22
- // TODO: `lane` is legacy transport metadata — scheduled for removal; callers still pass a fixed value for decode compatibility.
20
+ const { service, process, lane, entries } = input;
23
21
  const first = entries[0];
24
22
  const last = entries[entries.length - 1];
25
23
 
26
24
  return {
27
- name: `log-batch:${service}:${process}:${lane}`,
28
- value: idempotencyKey,
25
+ name: service,
26
+ value: LOG_BATCH_LIST_VALUE,
29
27
  category: CategoryList.LIST,
30
28
  tagList: ["log", lane, service],
31
29
  details: {
32
30
  manifestVersion: 0,
33
- loaderKey: LOG_PERSIST_LOADER_KEY,
31
+ loaderKey: LOG_BATCH_LIST_VALUE,
34
32
  meta: {
35
33
  service,
36
34
  process,
@@ -40,7 +38,6 @@ export function buildLogListDraft(input: BuildLogListDraftInput): LogListDraft {
40
38
  from: first?.timestamp ?? null,
41
39
  to: last?.timestamp ?? null,
42
40
  flushedAt: Date.now(),
43
- idempotencyKey,
44
41
  },
45
42
  preview: first?.message ?? "",
46
43
  items: entries,
@@ -67,36 +64,24 @@ async function withTimeout<T>(promise: Promise<T>, timeoutMs: number, label: str
67
64
  }
68
65
 
69
66
  export async function postLogBatch(draft: LogListDraft, postTimeoutMs: number): Promise<void> {
70
- try {
71
- const result = await withTimeout(
72
- postLogListCreate({
73
- name: draft.name,
74
- value: draft.value,
75
- category: draft.category,
76
- details: draft.details,
77
- }),
78
- postTimeoutMs,
79
- "postLogListCreate",
80
- );
67
+ const result = await withTimeout(
68
+ postListCreate({
69
+ name: draft.name,
70
+ value: draft.value,
71
+ category: draft.category,
72
+ details: draft.details,
73
+ }),
74
+ postTimeoutMs,
75
+ "postListCreate",
76
+ );
81
77
 
82
- if (!result.success) {
83
- const message = result.error?.message ?? "postLogListCreate failed";
84
- persistLogger.error("post log batch failed", {
85
- idempotencyKey: draft.value,
86
- name: draft.name,
87
- error: message,
88
- });
89
- throw new Error(message);
90
- }
91
- } catch (error) {
92
- if (isCreateTargetAlreadyExistsError(error)) {
93
- persistLogger.error("post log batch idempotent duplicate — treating as success", {
94
- idempotencyKey: draft.value,
95
- name: draft.name,
96
- });
97
- return;
98
- }
99
- throw error;
78
+ if (!result.success) {
79
+ const message = result.error?.message ?? "postListCreate failed";
80
+ persistLogger.error("post log batch failed", {
81
+ name: draft.name,
82
+ error: message,
83
+ });
84
+ throw new Error(message);
100
85
  }
101
86
  }
102
87
 
@@ -2,17 +2,10 @@ import type { TargetDraft } from "../../../core.interface";
2
2
  import type { List } from "../../../list/list.interface";
3
3
  import type { LogEntry } from "../core/log-manager";
4
4
 
5
- /**
6
- * `ListDetails.loaderKey` stamped on every persisted log batch (write + read side).
7
- * Business type: **LogBatch** (transport flush). Pairs with log-service **LogTrace**
8
- * (`LogTrace.0`) after rekeying by `traceId`.
9
- */
10
- export const LOG_PERSIST_LOADER_KEY = "LogBatch.0";
5
+ /** Discriminator on `List.value` and `details.loaderKey` — query by `value` only, not JSON loaderKey. */
6
+ export const LOG_BATCH_LIST_VALUE = "log-batch";
11
7
 
12
- /** Alias for {@link LOG_PERSIST_LOADER_KEY} — preferred when contrasting with LogTrace. */
13
- export const LOG_BATCH_LOADER_KEY = LOG_PERSIST_LOADER_KEY;
14
-
15
- /** Legacy transport metadata — fixed `"slow"` for spool uploads; required for downstream decode today. */
8
+ /** Legacy transport metadata — fixed `"slow"` for spool uploads. */
16
9
  export type LogPersistLane = "fast" | "medium" | "slow";
17
10
 
18
11
  export interface LogBatchMeta {
@@ -24,14 +17,12 @@ export interface LogBatchMeta {
24
17
  from: number | null;
25
18
  to: number | null;
26
19
  flushedAt: number;
27
- idempotencyKey: string;
28
20
  }
29
21
 
30
22
  export interface BuildLogListDraftInput {
31
23
  service: string;
32
24
  process: string;
33
25
  lane: LogPersistLane;
34
- idempotencyKey: string;
35
26
  entries: LogEntry[];
36
27
  }
37
28
 
@@ -106,4 +106,4 @@ Internal SDK code:
106
106
 
107
107
  Cross-host `storageProvider` cannot be verified on the current process — availability returns `ok: false`.
108
108
 
109
- See `.cursor/skills/media-link/SKILL.md`.
109
+ See `.cursor/skills/link-conventions/SKILL.md`.
@@ -1,13 +1,8 @@
1
1
  import type { QueryFilter } from "../../core.interface";
2
- import { CategoryLink } from "../../link/link.interface";
2
+ import { linkDedupFilters } from "../../link/link.query";
3
3
  import type { MediaValue } from "./media.interface";
4
4
 
5
5
  /** Dedup key: `category=link` + `value` + `name` (matches register / find). */
6
6
  export function mediaLinkDedupFilters(input: { value: MediaValue; name: string }): QueryFilter[] {
7
- const name = input.name.trim();
8
- return [
9
- { field: "category", operator: "eq", value: CategoryLink.LINK },
10
- { field: "value", operator: "eq", value: input.value },
11
- { field: "name", operator: "eq", value: name },
12
- ];
7
+ return linkDedupFilters(input);
13
8
  }
@@ -1,5 +1,5 @@
1
1
  import type { QueryFilter } from "../../core.interface";
2
- import { CategoryLink } from "../../link/link.interface";
2
+ import { linkDedupFilters } from "../../link/link.query";
3
3
  import { REVIEW_VALUE } from "./review.interface";
4
4
 
5
5
  /**
@@ -7,10 +7,5 @@ import { REVIEW_VALUE } from "./review.interface";
7
7
  * Many rows may match — the SDK does not define dedup for reviews.
8
8
  */
9
9
  export function reviewLinksForNameFilters(name: string): QueryFilter[] {
10
- const trimmed = name.trim();
11
- return [
12
- { field: "category", operator: "eq", value: CategoryLink.LINK },
13
- { field: "value", operator: "eq", value: REVIEW_VALUE },
14
- { field: "name", operator: "eq", value: trimmed },
15
- ];
10
+ return linkDedupFilters({ value: REVIEW_VALUE, name });
16
11
  }
@@ -15,7 +15,7 @@ export function getConfigFileDir(configFilePath: string): string {
15
15
  * - Absolute `pathInConfig` → returned as-is
16
16
  * - Relative `pathInConfig` → `resolve(dirname(configFilePath), pathInConfig)`
17
17
  *
18
- * **Not** relative to `process.cwd()`. See `.cursor/skills/config-file-relative-paths/SKILL.md`.
18
+ * **Not** relative to `process.cwd()`. See `.cursor/skills/task-local-discovery/SKILL.md`.
19
19
  */
20
20
  export function resolvePathFromConfigFile(configFilePath: string, pathInConfig: string): string {
21
21
  const trimmed = pathInConfig.trim();
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * PostgREST / Supabase JS client helpers for `target` table filters.
3
3
  *
4
- * @see `.cursor/skills/postgrest-query-filters/SKILL.md`
4
+ * @see `.cursor/skills/target-list-query/SKILL.md`
5
5
  */
6
6
 
7
7
  /**
package/src/task/index.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Task domain public API — curated re-exports only.
3
- * Internal modules (local-task-registry, task-repo-context, task.utils) stay private.
3
+ * Internal modules (local-task-registry, task-repo-context) stay private.
4
4
  */
5
5
 
6
6
  export type {
@@ -244,7 +244,7 @@ async function runBootstrapScan(options: BootstrapLocalTasksOptions): Promise<Bo
244
244
  * ./tasks/my-task/index.mjs
245
245
  * ```
246
246
  *
247
- * `taskDir` / `entry` paths are relative to **their config file's directory** (see config-file-relative-paths skill).
247
+ * `taskDir` / `entry` paths are relative to **their config file's directory** (see task-local-discovery skill).
248
248
  *
249
249
  * Legacy fallback: `./task.config.js` at project root (`taskDir: "./tasks"`).
250
250
  */
@@ -60,7 +60,12 @@ function enabledTriggerFilter() {
60
60
  return [{ field: TRIGGER_STATUS_FIELD, operator: "eq" as const, value: TriggerStatus.ENABLED }];
61
61
  }
62
62
 
63
- /** List all ENABLED triggers (Phase 1: in-memory due check). */
63
+ /**
64
+ * List all ENABLED triggers. Due-check is in-memory on the caller:
65
+ * filter with `isTriggerDue`, then `patchTriggerFired` with `buildFireKey`
66
+ * (both from `trigger.utils`, exported on `/node`).
67
+ * Do not call this from TriggerNode (local interval runners only).
68
+ */
64
69
  export const scanEnabledTriggers = validateWithSchema(
65
70
  scanEnabledTriggersSchema,
66
71
  "scanEnabledTriggersSchema",