target-supabase-sdk 0.2.22 → 0.2.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/browser.d.ts +2 -0
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +1 -1
- package/dist/chunks/{task-queue-DWFe0rCS.js → task-queue-C_nUaM2S.js} +260 -37
- package/dist/chunks/task-queue-C_nUaM2S.js.map +1 -0
- package/dist/link/index.d.ts +2 -0
- package/dist/link/index.d.ts.map +1 -1
- package/dist/link/link.query.d.ts +9 -0
- package/dist/link/link.query.d.ts.map +1 -0
- package/dist/node.d.ts +2 -0
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +113 -6
- package/dist/node.js.map +1 -1
- package/dist/shared/feed/feed.query.d.ts.map +1 -1
- package/dist/shared/idea/idea.build.d.ts +35 -0
- package/dist/shared/idea/idea.build.d.ts.map +1 -0
- package/dist/shared/idea/idea.interface.d.ts +23 -0
- package/dist/shared/idea/idea.interface.d.ts.map +1 -0
- package/dist/shared/idea/idea.normalize.d.ts +3 -0
- package/dist/shared/idea/idea.normalize.d.ts.map +1 -0
- package/dist/shared/idea/idea.parse.d.ts +3 -0
- package/dist/shared/idea/idea.parse.d.ts.map +1 -0
- package/dist/shared/idea/idea.query.d.ts +6 -0
- package/dist/shared/idea/idea.query.d.ts.map +1 -0
- package/dist/shared/idea/idea.schema.d.ts +60 -0
- package/dist/shared/idea/idea.schema.d.ts.map +1 -0
- package/dist/shared/idea/index.d.ts +15 -0
- package/dist/shared/idea/index.d.ts.map +1 -0
- package/dist/shared/idea/node/idea.hash.d.ts +3 -0
- package/dist/shared/idea/node/idea.hash.d.ts.map +1 -0
- package/dist/shared/idea/node/idea.locator.d.ts +9 -0
- package/dist/shared/idea/node/idea.locator.d.ts.map +1 -0
- package/dist/shared/idea/node/index.d.ts +3 -0
- package/dist/shared/idea/node/index.d.ts.map +1 -0
- package/dist/shared/media/media.query.d.ts.map +1 -1
- package/dist/shared/review/review.query.d.ts.map +1 -1
- package/dist/shared/utils/config-path.utils.d.ts +1 -1
- package/dist/shared/utils/postgrest.utils.d.ts +1 -1
- package/dist/task/index.d.ts +1 -1
- package/dist/task/local-task-registry.d.ts +1 -1
- package/dist/task/task-node.d.ts +3 -0
- package/dist/task/task-node.d.ts.map +1 -1
- package/dist/task/task.api.d.ts +14 -1
- package/dist/task/task.api.d.ts.map +1 -1
- package/dist/trigger/trigger.api.d.ts +6 -1
- package/dist/trigger/trigger.api.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/browser.ts +21 -0
- package/src/link/index.ts +2 -0
- package/src/link/link.query.ts +18 -0
- package/src/node.ts +8 -0
- package/src/shared/feed/README.md +1 -1
- package/src/shared/feed/feed.query.ts +2 -7
- package/src/shared/idea/README.md +82 -0
- package/src/shared/idea/VALIDATION-MATRIX.md +69 -0
- package/src/shared/idea/idea.build.ts +160 -0
- package/src/shared/idea/idea.interface.ts +34 -0
- package/src/shared/idea/idea.normalize.ts +17 -0
- package/src/shared/idea/idea.parse.ts +16 -0
- package/src/shared/idea/idea.query.ts +16 -0
- package/src/shared/idea/idea.schema.ts +32 -0
- package/src/shared/idea/index.ts +21 -0
- package/src/shared/idea/node/idea.hash.ts +6 -0
- package/src/shared/idea/node/idea.locator.ts +61 -0
- package/src/shared/idea/node/index.ts +7 -0
- package/src/shared/media/README.md +1 -1
- package/src/shared/media/media.query.ts +2 -7
- package/src/shared/review/review.query.ts +2 -7
- package/src/shared/utils/config-path.utils.ts +1 -1
- package/src/shared/utils/postgrest.utils.ts +1 -1
- package/src/task/index.ts +1 -1
- package/src/task/local-task-registry.ts +1 -1
- package/src/task/task-node.ts +7 -1
- package/src/task/task.api.ts +62 -15
- package/src/trigger/trigger.api.ts +6 -1
- package/dist/chunks/task-queue-DWFe0rCS.js.map +0 -1
- package/dist/resource/resource.interface.d.ts +0 -20
- package/dist/resource/resource.interface.d.ts.map +0 -1
- package/dist/task/task.utils.d.ts +0 -1
- package/dist/task/task.utils.d.ts.map +0 -1
- package/src/resource/resource.interface.ts +0 -37
- package/src/task/task.utils.ts +0 -0
package/src/task/task.api.ts
CHANGED
|
@@ -5,12 +5,15 @@ import {
|
|
|
5
5
|
OPTIMISTIC_LOCK_ERROR_CODE,
|
|
6
6
|
OPTIMISTIC_LOCK_FAILED_MESSAGE,
|
|
7
7
|
type QueryFilter,
|
|
8
|
+
type TargetFilterBuilder,
|
|
8
9
|
updateTargetDetails,
|
|
9
10
|
validateWithSchema,
|
|
10
11
|
} from "../core.api";
|
|
11
12
|
import { generateResponse, type SupabaseResponse } from "../core.interface";
|
|
12
13
|
import { createLogger, type LoggerWithScope } from "../shared/log";
|
|
13
14
|
import { getErrorMessage } from "../shared/utils/error.utils";
|
|
15
|
+
import { escapeIlikePattern } from "../shared/utils/postgrest.utils";
|
|
16
|
+
import { supabase } from "../supabase";
|
|
14
17
|
import { LOG_TOPIC_TASK } from "./task.constant";
|
|
15
18
|
import { CategoryTask, ResultCode, type Task, type TaskDetails, TaskStatus, TaskStatusAction } from "./task.interface";
|
|
16
19
|
|
|
@@ -112,12 +115,28 @@ export type PatchTaskProgressPayload = z.infer<typeof patchTaskProgressSchema>;
|
|
|
112
115
|
|
|
113
116
|
export const patchClaimTaskSchema = z.object({
|
|
114
117
|
nodeId: nodeIdSchema,
|
|
118
|
+
/** Task `value` must be in this list (always applied). */
|
|
115
119
|
availableTaskList: z.array(z.string()).min(1),
|
|
116
120
|
traceId: traceIdSchema,
|
|
121
|
+
/**
|
|
122
|
+
* Optional second axis on `name` (AND with `availableTaskList`, not coupled to `value`).
|
|
123
|
+
* When non-empty: `name` must equal a prefix or start with `prefix:`; query uses PostgREST `ilike`.
|
|
124
|
+
*/
|
|
125
|
+
namePrefixList: z.array(z.string().trim().min(1)).optional(),
|
|
117
126
|
});
|
|
118
127
|
|
|
119
128
|
export type PatchClaimTaskPayload = z.infer<typeof patchClaimTaskSchema>;
|
|
120
129
|
|
|
130
|
+
/** `name === prefix` or `name.startsWith(prefix + ':')` via `name ilike 'prefix%'`. */
|
|
131
|
+
function buildClaimTaskNamePrefixFilterBuilder(namePrefixes: string[]): TargetFilterBuilder {
|
|
132
|
+
const base = supabase.client.from("target").select("*");
|
|
133
|
+
if (namePrefixes.length === 1) {
|
|
134
|
+
return base.ilike("name", `${escapeIlikePattern(namePrefixes[0])}%`);
|
|
135
|
+
}
|
|
136
|
+
const nameOrParts = namePrefixes.map((prefix) => `name.ilike.${escapeIlikePattern(prefix)}%`);
|
|
137
|
+
return base.or(nameOrParts.join(","));
|
|
138
|
+
}
|
|
139
|
+
|
|
121
140
|
// ─── Optimistic lock helpers ─────────────────────────────────────────────────
|
|
122
141
|
|
|
123
142
|
function lockOnStatus(...allowed: TaskStatus[]): QueryFilter[] {
|
|
@@ -338,7 +357,11 @@ export const patchTaskProgress = validateWithSchema(
|
|
|
338
357
|
/**
|
|
339
358
|
* Discover and claim one TODO task for this node.
|
|
340
359
|
*
|
|
341
|
-
*
|
|
360
|
+
* Match criteria (all required): `details.status = TODO`, `value ∈ availableTaskList`, and when
|
|
361
|
+
* `namePrefixList` is non-empty, `name` matches any listed prefix. Those filters are independent
|
|
362
|
+
* (AND); omitting `namePrefixList` does not relax the `value` constraint.
|
|
363
|
+
*
|
|
364
|
+
* Flow: oldest matching row (FIFO by `created_at`) → {@link claimTaskById}.
|
|
342
365
|
* Equivalent to `patchChangeTaskStatus({ id, action: CLAIM, nodeId })` when `id` is already known.
|
|
343
366
|
*
|
|
344
367
|
* Returns `data: null` when no TODO task matches.
|
|
@@ -347,25 +370,49 @@ export const patchTaskProgress = validateWithSchema(
|
|
|
347
370
|
export const patchClaimTask = validateWithSchema(
|
|
348
371
|
patchClaimTaskSchema,
|
|
349
372
|
"patchClaimTaskSchema",
|
|
350
|
-
)(async ({ nodeId, availableTaskList, traceId }): Promise<SupabaseResponse<Task | null>> => {
|
|
373
|
+
)(async ({ nodeId, availableTaskList, traceId, namePrefixList }): Promise<SupabaseResponse<Task | null>> => {
|
|
351
374
|
const logger = createLogger({ module: "patchClaimTask", traceId, labels: { nodeId } });
|
|
352
375
|
|
|
376
|
+
const namePrefixes = (namePrefixList ?? []).map((prefix) => prefix.trim()).filter((prefix) => prefix !== "");
|
|
377
|
+
|
|
378
|
+
function taskNameMatchesPrefixList(name: string): boolean {
|
|
379
|
+
return namePrefixes.some((prefix) => name === prefix || name.startsWith(`${prefix}:`));
|
|
380
|
+
}
|
|
381
|
+
|
|
353
382
|
let todoTask: Task;
|
|
354
383
|
try {
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
384
|
+
if (namePrefixes.length === 0) {
|
|
385
|
+
const { data: taskCandidates } = await getTargetList<Task>({
|
|
386
|
+
category: CategoryTask.TASK,
|
|
387
|
+
filterList: [
|
|
388
|
+
{ field: TASK_STATUS_FIELD, operator: "eq", value: TaskStatus.TODO },
|
|
389
|
+
{ field: "value", operator: "in", value: availableTaskList },
|
|
390
|
+
],
|
|
391
|
+
limit: 1,
|
|
392
|
+
orderBy: { field: "created_at", ascending: true },
|
|
393
|
+
});
|
|
394
|
+
const possibleTask = taskCandidates?.[0] ?? null;
|
|
395
|
+
if (possibleTask == null) {
|
|
396
|
+
return generateResponse.success(null);
|
|
397
|
+
}
|
|
398
|
+
todoTask = possibleTask;
|
|
399
|
+
} else {
|
|
400
|
+
const { data: taskCandidates } = await getTargetList<Task>({
|
|
401
|
+
category: CategoryTask.TASK,
|
|
402
|
+
filterList: [
|
|
403
|
+
{ field: TASK_STATUS_FIELD, operator: "eq", value: TaskStatus.TODO },
|
|
404
|
+
{ field: "value", operator: "in", value: availableTaskList },
|
|
405
|
+
],
|
|
406
|
+
limit: 1,
|
|
407
|
+
orderBy: { field: "created_at", ascending: true },
|
|
408
|
+
filterBuilder: buildClaimTaskNamePrefixFilterBuilder(namePrefixes),
|
|
409
|
+
});
|
|
410
|
+
const possibleTask = taskCandidates?.find((task) => taskNameMatchesPrefixList(task.name)) ?? null;
|
|
411
|
+
if (possibleTask == null) {
|
|
412
|
+
return generateResponse.success(null);
|
|
413
|
+
}
|
|
414
|
+
todoTask = possibleTask;
|
|
367
415
|
}
|
|
368
|
-
todoTask = possibleTask;
|
|
369
416
|
} catch (error) {
|
|
370
417
|
const message = getErrorMessage(error);
|
|
371
418
|
logger.error("獲取 TODO 任務失敗", { topic: LOG_TOPIC_TASK, data: { error: message, availableTaskList } });
|
|
@@ -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
|
-
/**
|
|
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",
|