target-supabase-sdk 0.2.21 → 0.2.23
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/browser.js +1 -1
- package/dist/chunks/{task-queue-FygwujNS.js → task-queue-Ba8S21I6.js} +66 -20
- package/dist/chunks/task-queue-Ba8S21I6.js.map +1 -0
- package/dist/node.d.ts +2 -2
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +179 -19
- package/dist/node.js.map +1 -1
- package/dist/shared/feed/feed.build.d.ts +4 -2
- package/dist/shared/feed/feed.build.d.ts.map +1 -1
- package/dist/shared/feed/node/feed.access.d.ts +10 -2
- package/dist/shared/feed/node/feed.access.d.ts.map +1 -1
- package/dist/shared/feed/node/feed.service.d.ts +26 -1
- package/dist/shared/feed/node/feed.service.d.ts.map +1 -1
- package/dist/shared/feed/node/index.d.ts +3 -3
- package/dist/shared/feed/node/index.d.ts.map +1 -1
- package/dist/shared/http/fetch-retry.d.ts +2 -0
- package/dist/shared/http/fetch-retry.d.ts.map +1 -1
- package/dist/shared/media/node/media.service.d.ts +8 -2
- package/dist/shared/media/node/media.service.d.ts.map +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/package.json +1 -1
- package/src/node.ts +4 -0
- package/src/shared/feed/README.md +4 -3
- package/src/shared/feed/feed.build.ts +6 -3
- package/src/shared/feed/node/feed.access.ts +33 -6
- package/src/shared/feed/node/feed.service.ts +131 -4
- package/src/shared/feed/node/index.ts +7 -3
- package/src/shared/http/fetch-retry.ts +13 -2
- package/src/shared/media/README.md +4 -4
- package/src/shared/media/node/media.service.ts +77 -10
- package/src/task/task-node.ts +7 -1
- package/src/task/task.api.ts +62 -15
- package/dist/chunks/task-queue-FygwujNS.js.map +0 -1
|
@@ -3,9 +3,11 @@ import type { Link } from "../../link/link.interface";
|
|
|
3
3
|
import { type FeedSource, type FeedValue } from "./feed.interface";
|
|
4
4
|
export interface BuildFeedLinkDraftInput {
|
|
5
5
|
value: FeedValue;
|
|
6
|
+
/** When set, used as Link `name` (skips {@link buildFeedLinkName}). */
|
|
7
|
+
name?: string;
|
|
6
8
|
/** Dots are normalized to spaces in {@link buildFeedLinkName} before joining with `feedKey`. */
|
|
7
|
-
platformName
|
|
8
|
-
feedKey
|
|
9
|
+
platformName?: string;
|
|
10
|
+
feedKey?: string;
|
|
9
11
|
source: FeedSource;
|
|
10
12
|
locator: string;
|
|
11
13
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feed.build.d.ts","sourceRoot":"","sources":["../../../src/shared/feed/feed.build.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,2BAA2B,CAAC;AACtD,OAAO,EAGH,KAAK,UAAU,EACf,KAAK,SAAS,EAGjB,MAAM,kBAAkB,CAAC;AAG1B,MAAM,WAAW,uBAAuB;IACpC,KAAK,EAAE,SAAS,CAAC;IACjB,gGAAgG;IAChG,YAAY,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"feed.build.d.ts","sourceRoot":"","sources":["../../../src/shared/feed/feed.build.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,2BAA2B,CAAC;AACtD,OAAO,EAGH,KAAK,UAAU,EACf,KAAK,SAAS,EAGjB,MAAM,kBAAkB,CAAC;AAG1B,MAAM,WAAW,uBAAuB;IACpC,KAAK,EAAE,SAAS,CAAC;IACjB,uEAAuE;IACvE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gGAAgG;IAChG,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,UAAU,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sFAAsF;IACtF,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,qEAAqE;AACrE,wBAAgB,qBAAqB,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,GAAG,SAAS,CAK/E;AAED,iFAAiF;AACjF,wBAAgB,kCAAkC,CAAC,MAAM,EAAE,UAAU,EAAE,eAAe,CAAC,EAAE,MAAM,GAAG,MAAM,CAcvG;AAED,gGAAgG;AAChG,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAKjF;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,uBAAuB,GAAG,WAAW,CAAC,IAAI,CAAC,CAgCpF"}
|
|
@@ -23,12 +23,20 @@ export interface FeedAvailabilityResult {
|
|
|
23
23
|
* **Node-only**
|
|
24
24
|
*/
|
|
25
25
|
export declare function checkFeedLocalAvailability(input: CheckFeedLocalAvailabilityInput): Promise<FeedAvailabilityResult>;
|
|
26
|
+
export interface CheckFeedOssAvailabilityOptions {
|
|
27
|
+
/** Override global `fetch` (proxied outbound, etc.). Default: SDK `fetchWithRetry`. */
|
|
28
|
+
fetch?: (url: string, init?: RequestInit) => Promise<Response>;
|
|
29
|
+
/** When true, skip HTTP (co-located Files sandbox, etc.). */
|
|
30
|
+
tryLocalResolve?: (locator: string) => Promise<boolean>;
|
|
31
|
+
timeoutMs?: number;
|
|
32
|
+
}
|
|
26
33
|
/**
|
|
27
34
|
* Remote reachability for feed ingest (`.oss` source) via HTTP(S).
|
|
28
35
|
*
|
|
29
|
-
*
|
|
36
|
+
* Optional `tryLocalResolve` first (same-host Files). Then `HEAD`, then `GET` with
|
|
37
|
+
* `Range: bytes=0-0` when `HEAD` is not allowed. Pass `fetch` for proxied outbound.
|
|
30
38
|
*
|
|
31
39
|
* **Node-only**
|
|
32
40
|
*/
|
|
33
|
-
export declare function checkFeedOssAvailability(locator: string): Promise<FeedAvailabilityResult>;
|
|
41
|
+
export declare function checkFeedOssAvailability(locator: string, options?: CheckFeedOssAvailabilityOptions): Promise<FeedAvailabilityResult>;
|
|
34
42
|
//# sourceMappingURL=feed.access.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feed.access.d.ts","sourceRoot":"","sources":["../../../../src/shared/feed/node/feed.access.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAEnD,MAAM,WAAW,+BAA+B;IAC5C,KAAK,EAAE,SAAS,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,gEAAgE;IAChE,oBAAoB,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,sBAAsB;IACnC,EAAE,EAAE,OAAO,CAAC;IACZ,gEAAgE;IAChE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAUD;;;;;;;;;GASG;AACH,wBAAsB,0BAA0B,CAC5C,KAAK,EAAE,+BAA+B,GACvC,OAAO,CAAC,sBAAsB,CAAC,CA0CjC;
|
|
1
|
+
{"version":3,"file":"feed.access.d.ts","sourceRoot":"","sources":["../../../../src/shared/feed/node/feed.access.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAEnD,MAAM,WAAW,+BAA+B;IAC5C,KAAK,EAAE,SAAS,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,gEAAgE;IAChE,oBAAoB,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,sBAAsB;IACnC,EAAE,EAAE,OAAO,CAAC;IACZ,gEAAgE;IAChE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAUD;;;;;;;;;GASG;AACH,wBAAsB,0BAA0B,CAC5C,KAAK,EAAE,+BAA+B,GACvC,OAAO,CAAC,sBAAsB,CAAC,CA0CjC;AAKD,MAAM,WAAW,+BAA+B;IAC5C,uFAAuF;IACvF,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC/D,6DAA6D;IAC7D,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACxD,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAsBD;;;;;;;GAOG;AACH,wBAAsB,wBAAwB,CAC1C,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,+BAA+B,GAC1C,OAAO,CAAC,sBAAsB,CAAC,CAgCjC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { type FeedIngestSource } from "../feed.interface";
|
|
1
|
+
import { type FeedIngestSource, type FeedValue } from "../feed.interface";
|
|
2
|
+
import { type CheckFeedOssAvailabilityOptions } from "./feed.access";
|
|
2
3
|
export interface RegisterFeedInput {
|
|
3
4
|
/** Existing feed Link row id (must still be `.public` unless already ingested with same payload). */
|
|
4
5
|
id: string;
|
|
@@ -7,6 +8,8 @@ export interface RegisterFeedInput {
|
|
|
7
8
|
locator: string;
|
|
8
9
|
/** Required when `source` is `.local` (e.g. `process.env.LOCAL_STORAGE_PROVIDER`). Ignored for `.oss`. */
|
|
9
10
|
localStorageProvider?: string;
|
|
11
|
+
/** `.oss` HTTP check: inject `fetch` / same-host resolve. */
|
|
12
|
+
ossCheck?: CheckFeedOssAvailabilityOptions;
|
|
10
13
|
}
|
|
11
14
|
export interface RegisterFeedResult {
|
|
12
15
|
id: string;
|
|
@@ -20,4 +23,26 @@ export interface RegisterFeedResult {
|
|
|
20
23
|
* **Node-only** — under `shared/feed/node/`; do not import from the browser entry.
|
|
21
24
|
*/
|
|
22
25
|
export declare function registerFeed(input: RegisterFeedInput): Promise<RegisterFeedResult>;
|
|
26
|
+
export interface CreateFeedInput {
|
|
27
|
+
/** Full Link `name` (Filter itemName, etc.). */
|
|
28
|
+
name: string;
|
|
29
|
+
value: FeedValue;
|
|
30
|
+
source: FeedIngestSource;
|
|
31
|
+
storageProvider: string;
|
|
32
|
+
locator: string;
|
|
33
|
+
description?: string;
|
|
34
|
+
preview?: string;
|
|
35
|
+
tagList?: string[];
|
|
36
|
+
localStorageProvider?: string;
|
|
37
|
+
ossCheck?: CheckFeedOssAvailabilityOptions;
|
|
38
|
+
}
|
|
39
|
+
export interface CreateFeedResult {
|
|
40
|
+
id: string;
|
|
41
|
+
created: boolean;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Insert a new feed Link already on `.local` or `.oss` (not a `.public` promote).
|
|
45
|
+
* Dedup: `category` + `value` + `name`. Same original → `{ created: false }`.
|
|
46
|
+
*/
|
|
47
|
+
export declare function createFeed(input: CreateFeedInput): Promise<CreateFeedResult>;
|
|
23
48
|
//# sourceMappingURL=feed.service.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feed.service.d.ts","sourceRoot":"","sources":["../../../../src/shared/feed/node/feed.service.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"feed.service.d.ts","sourceRoot":"","sources":["../../../../src/shared/feed/node/feed.service.ts"],"names":[],"mappings":"AAWA,OAAO,EACH,KAAK,gBAAgB,EAErB,KAAK,SAAS,EAGjB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACH,KAAK,+BAA+B,EAGvC,MAAM,eAAe,CAAC;AAIvB,MAAM,WAAW,iBAAiB;IAC9B,qGAAqG;IACrG,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,gBAAgB,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,0GAA0G;IAC1G,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,+BAA+B,CAAC;CAC9C;AAED,MAAM,WAAW,kBAAkB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,gGAAgG;IAChG,OAAO,EAAE,OAAO,CAAC;CACpB;AAmDD;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CA+FxF;AAED,MAAM,WAAW,eAAe;IAC5B,gDAAgD;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,SAAS,CAAC;IACjB,MAAM,EAAE,gBAAgB,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,EAAE,+BAA+B,CAAC;CAC9C;AAED,MAAM,WAAW,gBAAgB;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,OAAO,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAsB,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAyFlF"}
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
*/
|
|
5
5
|
export type { FeedIngestSource } from "../feed.interface";
|
|
6
6
|
export { FEED_INGEST_SOURCES, isFeedIngestSource } from "../feed.interface";
|
|
7
|
-
export type { CheckFeedLocalAvailabilityInput, FeedAvailabilityResult } from "./feed.access";
|
|
7
|
+
export type { CheckFeedLocalAvailabilityInput, CheckFeedOssAvailabilityOptions, FeedAvailabilityResult, } from "./feed.access";
|
|
8
8
|
export { checkFeedLocalAvailability, checkFeedOssAvailability } from "./feed.access";
|
|
9
|
-
export type { RegisterFeedInput, RegisterFeedResult } from "./feed.service";
|
|
10
|
-
export { registerFeed } from "./feed.service";
|
|
9
|
+
export type { CreateFeedInput, CreateFeedResult, RegisterFeedInput, RegisterFeedResult } from "./feed.service";
|
|
10
|
+
export { createFeed, registerFeed } from "./feed.service";
|
|
11
11
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/shared/feed/node/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC5E,YAAY,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/shared/feed/node/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC5E,YAAY,EACR,+BAA+B,EAC/B,+BAA+B,EAC/B,sBAAsB,GACzB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,0BAA0B,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAC;AACrF,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAC/G,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC"}
|
|
@@ -15,6 +15,8 @@ export interface FetchRetryOptions {
|
|
|
15
15
|
/** When set, retry/success lines are logged via SDK logger. */
|
|
16
16
|
logger?: LoggerWithScope;
|
|
17
17
|
hint?: string;
|
|
18
|
+
/** Override global `fetch` (e.g. proxied outbound fetch). Ignores `dispatcher`. */
|
|
19
|
+
fetch?: (url: string, init?: RequestInit) => Promise<Response>;
|
|
18
20
|
}
|
|
19
21
|
export declare function fetchWithRetry(url: string, initOrFactory: RequestInit | FetchInitFactory, options: FetchRetryOptions): Promise<Response>;
|
|
20
22
|
export declare function fetchBinaryWithRetry(url: string, init: RequestInit, options: FetchRetryOptions): Promise<ArrayBuffer>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch-retry.d.ts","sourceRoot":"","sources":["../../../src/shared/http/fetch-retry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAOpD,2DAA2D;AAC3D,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAE7D;AAED,MAAM,MAAM,gBAAgB,GAAG,MAAM,WAAW,CAAC;AAEjD,wBAAgB,cAAc,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAEzD;AAED,MAAM,WAAW,iBAAiB;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,6FAA6F;IAC7F,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;IAChD,+DAA+D;IAC/D,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"fetch-retry.d.ts","sourceRoot":"","sources":["../../../src/shared/http/fetch-retry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAOpD,2DAA2D;AAC3D,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAE7D;AAED,MAAM,MAAM,gBAAgB,GAAG,MAAM,WAAW,CAAC;AAEjD,wBAAgB,cAAc,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAEzD;AAED,MAAM,WAAW,iBAAiB;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,6FAA6F;IAC7F,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;IAChD,+DAA+D;IAC/D,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mFAAmF;IACnF,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;CAClE;AAuCD,wBAAsB,cAAc,CAChC,GAAG,EAAE,MAAM,EACX,aAAa,EAAE,WAAW,GAAG,gBAAgB,EAC7C,OAAO,EAAE,iBAAiB,GAC3B,OAAO,CAAC,QAAQ,CAAC,CAoEnB;AAED,wBAAsB,oBAAoB,CACtC,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,WAAW,EACjB,OAAO,EAAE,iBAAiB,GAC3B,OAAO,CAAC,WAAW,CAAC,CAMtB"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type Link } from "../../../link/link.interface";
|
|
2
2
|
import { type MediaValue } from "../media.interface";
|
|
3
3
|
export interface FindMediaLinkInput {
|
|
4
4
|
value: MediaValue;
|
|
@@ -13,6 +13,11 @@ export interface RegisterMediaLinkInput {
|
|
|
13
13
|
locator: string;
|
|
14
14
|
/** This host id for availability (e.g. `process.env.LOCAL_STORAGE_PROVIDER`). */
|
|
15
15
|
localStorageProvider: string;
|
|
16
|
+
/**
|
|
17
|
+
* Local file to probe when `locator` is http(s) (OSS ingest).
|
|
18
|
+
* Availability uses this path + `localStorageProvider`.
|
|
19
|
+
*/
|
|
20
|
+
localLocator?: string;
|
|
16
21
|
description?: string;
|
|
17
22
|
tagList?: string[];
|
|
18
23
|
}
|
|
@@ -22,7 +27,8 @@ export interface RegisterMediaLinkResult {
|
|
|
22
27
|
}
|
|
23
28
|
/**
|
|
24
29
|
* Register a media Link: dedup → availability → probe → `postTarget`.
|
|
25
|
-
* Idempotent when `category`+`value`+`name` already exist.
|
|
30
|
+
* Idempotent when `category`+`value`+`name` already exist (local path).
|
|
31
|
+
* HTTP `locator` + `localLocator`: probe local file, persist remote URL; patch if name exists.
|
|
26
32
|
*
|
|
27
33
|
* **Node-only** — under `shared/media/node/`; do not import from the browser entry.
|
|
28
34
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"media.service.d.ts","sourceRoot":"","sources":["../../../../src/shared/media/node/media.service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"media.service.d.ts","sourceRoot":"","sources":["../../../../src/shared/media/node/media.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,KAAK,IAAI,EAAoB,MAAM,8BAA8B,CAAC;AAIzF,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAMnE,MAAM,WAAW,kBAAkB;IAC/B,KAAK,EAAE,UAAU,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CAChB;AAED,wDAAwD;AACxD,wBAAsB,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAYnF;AAED,MAAM,WAAW,sBAAsB;IACnC,KAAK,EAAE,UAAU,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,iFAAiF;IACjF,oBAAoB,EAAE,MAAM,CAAC;IAC7B;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,uBAAuB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,OAAO,CAAC;CACpB;AA6CD;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CAAC,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CA6FvG"}
|
package/dist/task/task-node.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { BaseNodeRuntime, type NodeLoopContext } from "../node/node-runtime.base";
|
|
2
2
|
import { type LoggerWithScope } from "../shared/log";
|
|
3
|
+
import { type PatchClaimTaskPayload } from "./task.api";
|
|
3
4
|
/**
|
|
4
5
|
* Task worker node: commands → heartbeat → claim & execute tasks.
|
|
5
6
|
* Built on {@link BaseNodeRuntime}.
|
|
@@ -8,6 +9,8 @@ declare class TaskNode extends BaseNodeRuntime {
|
|
|
8
9
|
private _availableTaskList;
|
|
9
10
|
private _includeRemote;
|
|
10
11
|
get availableTaskList(): readonly string[];
|
|
12
|
+
/** Optional claim filters passed to {@link patchClaimTask} (e.g. name prefix per task type). */
|
|
13
|
+
protected getPatchClaimTaskOptions(): Pick<PatchClaimTaskPayload, "namePrefixList">;
|
|
11
14
|
constructor();
|
|
12
15
|
protected onBeforeRegisterNode(logger: LoggerWithScope): Promise<void>;
|
|
13
16
|
protected runLoopSteps(ctx: NodeLoopContext, heartbeatOk: boolean): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"task-node.d.ts","sourceRoot":"","sources":["../../src/task/task-node.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAkB,KAAK,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAClG,OAAO,EAA6B,KAAK,eAAe,EAAc,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"task-node.d.ts","sourceRoot":"","sources":["../../src/task/task-node.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAkB,KAAK,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAClG,OAAO,EAA6B,KAAK,eAAe,EAAc,MAAM,eAAe,CAAC;AAE5F,OAAO,EAAE,KAAK,qBAAqB,EAAyC,MAAM,YAAY,CAAC;AAK/F;;;GAGG;AACH,cAAM,QAAS,SAAQ,eAAe;IAClC,OAAO,CAAC,kBAAkB,CAAgB;IAC1C,OAAO,CAAC,cAAc,CAAQ;IAE9B,IAAI,iBAAiB,IAAI,SAAS,MAAM,EAAE,CAEzC;IAED,gGAAgG;IAChG,SAAS,CAAC,wBAAwB,IAAI,IAAI,CAAC,qBAAqB,EAAE,gBAAgB,CAAC;;cAQnE,oBAAoB,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;cAM5D,YAAY,CAAC,GAAG,EAAE,eAAe,EAAE,WAAW,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;YAezE,WAAW;YA8KX,YAAY;YA2BZ,eAAe;CAqDhC;AAED,OAAO,EAAE,QAAQ,EAAE,CAAC"}
|
package/dist/task/task.api.d.ts
CHANGED
|
@@ -127,16 +127,24 @@ export declare const patchTaskProgressSchema: z.ZodObject<{
|
|
|
127
127
|
export type PatchTaskProgressPayload = z.infer<typeof patchTaskProgressSchema>;
|
|
128
128
|
export declare const patchClaimTaskSchema: z.ZodObject<{
|
|
129
129
|
nodeId: z.ZodString;
|
|
130
|
+
/** Task `value` must be in this list (always applied). */
|
|
130
131
|
availableTaskList: z.ZodArray<z.ZodString, "many">;
|
|
131
132
|
traceId: z.ZodOptional<z.ZodString>;
|
|
133
|
+
/**
|
|
134
|
+
* Optional second axis on `name` (AND with `availableTaskList`, not coupled to `value`).
|
|
135
|
+
* When non-empty: `name` must equal a prefix or start with `prefix:`; query uses PostgREST `ilike`.
|
|
136
|
+
*/
|
|
137
|
+
namePrefixList: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
132
138
|
}, "strip", z.ZodTypeAny, {
|
|
133
139
|
nodeId: string;
|
|
134
140
|
availableTaskList: string[];
|
|
135
141
|
traceId?: string | undefined;
|
|
142
|
+
namePrefixList?: string[] | undefined;
|
|
136
143
|
}, {
|
|
137
144
|
nodeId: string;
|
|
138
145
|
availableTaskList: string[];
|
|
139
146
|
traceId?: string | undefined;
|
|
147
|
+
namePrefixList?: string[] | undefined;
|
|
140
148
|
}>;
|
|
141
149
|
export type PatchClaimTaskPayload = z.infer<typeof patchClaimTaskSchema>;
|
|
142
150
|
export declare const patchChangeTaskStatus: (payload: {
|
|
@@ -185,7 +193,11 @@ export declare const patchTaskProgress: (payload: {
|
|
|
185
193
|
/**
|
|
186
194
|
* Discover and claim one TODO task for this node.
|
|
187
195
|
*
|
|
188
|
-
*
|
|
196
|
+
* Match criteria (all required): `details.status = TODO`, `value ∈ availableTaskList`, and when
|
|
197
|
+
* `namePrefixList` is non-empty, `name` matches any listed prefix. Those filters are independent
|
|
198
|
+
* (AND); omitting `namePrefixList` does not relax the `value` constraint.
|
|
199
|
+
*
|
|
200
|
+
* Flow: oldest matching row (FIFO by `created_at`) → {@link claimTaskById}.
|
|
189
201
|
* Equivalent to `patchChangeTaskStatus({ id, action: CLAIM, nodeId })` when `id` is already known.
|
|
190
202
|
*
|
|
191
203
|
* Returns `data: null` when no TODO task matches.
|
|
@@ -195,5 +207,6 @@ export declare const patchClaimTask: (payload: {
|
|
|
195
207
|
nodeId: string;
|
|
196
208
|
availableTaskList: string[];
|
|
197
209
|
traceId?: string | undefined;
|
|
210
|
+
namePrefixList?: string[] | undefined;
|
|
198
211
|
}) => SupabaseResponse<Task | null> | Promise<SupabaseResponse<Task | null>>;
|
|
199
212
|
//# sourceMappingURL=task.api.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"task.api.d.ts","sourceRoot":"","sources":["../../src/task/task.api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"task.api.d.ts","sourceRoot":"","sources":["../../src/task/task.api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAWxB,OAAO,EAAoB,KAAK,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAM5E,OAAO,EAA4B,KAAK,IAAI,EAAgC,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AA0DvH,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IA2BtC,CAAC;AAEH,MAAM,MAAM,4BAA4B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAEvF,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;EAKlC,CAAC;AAEH,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAE/E,eAAO,MAAM,oBAAoB;;IAE7B,0DAA0D;;;IAG1D;;;OAGG;;;;;;;;;;;;EAEL,CAAC;AAEH,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AA0GzE,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oEAmGhC,CAAC;AAIH,gGAAgG;AAChG,eAAO,MAAM,iBAAiB;;;;;8DAe5B,CAAC;AAIH;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,cAAc;;;;;4EAkFzB,CAAC"}
|
package/package.json
CHANGED
package/src/node.ts
CHANGED
|
@@ -160,6 +160,9 @@ export {
|
|
|
160
160
|
export { RepoManager } from "./repo/repo-manager";
|
|
161
161
|
export type {
|
|
162
162
|
CheckFeedLocalAvailabilityInput,
|
|
163
|
+
CheckFeedOssAvailabilityOptions,
|
|
164
|
+
CreateFeedInput,
|
|
165
|
+
CreateFeedResult,
|
|
163
166
|
FeedAvailabilityResult,
|
|
164
167
|
FeedIngestSource,
|
|
165
168
|
RegisterFeedInput,
|
|
@@ -168,6 +171,7 @@ export type {
|
|
|
168
171
|
export {
|
|
169
172
|
checkFeedLocalAvailability,
|
|
170
173
|
checkFeedOssAvailability,
|
|
174
|
+
createFeed,
|
|
171
175
|
FEED_INGEST_SOURCES,
|
|
172
176
|
isFeedIngestSource,
|
|
173
177
|
registerFeed,
|
|
@@ -36,7 +36,7 @@ import {
|
|
|
36
36
|
} from "target-supabase-sdk";
|
|
37
37
|
|
|
38
38
|
// Node — ingest after .public draft exists
|
|
39
|
-
import { registerFeed, FEED_INGEST_SOURCES } from "target-supabase-sdk/node";
|
|
39
|
+
import { registerFeed, createFeed, checkFeedOssAvailability, FEED_INGEST_SOURCES } from "target-supabase-sdk/node";
|
|
40
40
|
```
|
|
41
41
|
|
|
42
42
|
---
|
|
@@ -45,6 +45,7 @@ import { registerFeed, FEED_INGEST_SOURCES } from "target-supabase-sdk/node";
|
|
|
45
45
|
|
|
46
46
|
1. **Create (browser or any host)** — `buildFeedLinkDraft` with `source: ".public"`, public `locator` (often a platform URL), `original.storageProvider: ""`. Persist via `postTarget` / `createTarget`.
|
|
47
47
|
2. **Ingest (Node)** — `registerFeed` on an existing row `id`: validate the new locator, then promote `tagList` from `.public` to `.local` or `.oss` and update `original.storageProvider` + `locator`. Preserves `twinList` and other non-source tags.
|
|
48
|
+
3. **Create already ingested (Node)** — `createFeed` inserts a new row already tagged `.local` or `.oss` (Filter / OSS-first). Dedup on `category` + `value` + `name`.
|
|
48
49
|
|
|
49
50
|
---
|
|
50
51
|
|
|
@@ -108,7 +109,7 @@ Leading `.` marks **system** tags (not user tags).
|
|
|
108
109
|
3. Require `tagList` to include `.public` (not already ingested with different state).
|
|
109
110
|
4. **Availability**
|
|
110
111
|
- `.local` — `checkFeedLocalAvailability`: `storageProvider === localStorageProvider`, absolute path, file (or directory for `feed.image-list`) exists and is non-empty where applicable.
|
|
111
|
-
- `.oss` — `checkFeedOssAvailability`: `http(s)` URL; `HEAD`, then `GET` with `Range: bytes=0-0` if `HEAD` is not supported.
|
|
112
|
+
- `.oss` — `checkFeedOssAvailability`: `http(s)` URL; optional `tryLocalResolve` then `HEAD`, then `GET` with `Range: bytes=0-0` if `HEAD` is not supported. Pass `ossCheck.fetch` for proxied outbound.
|
|
112
113
|
5. **Update** — `updateTarget` with optimistic lock `details->original->>storageProvider` eq `""` (still `.public` tier). Sets `tagList` via `buildFeedTagList`, updates `original.storageProvider` and `locator`, keeps `twinList`.
|
|
113
114
|
|
|
114
115
|
**Errors (representative)**
|
|
@@ -149,7 +150,7 @@ Same variable as media register — see `src/shared/media/README.md`.
|
|
|
149
150
|
|
|
150
151
|
| Topic | Current behavior | Possible follow-up |
|
|
151
152
|
|-------|------------------|-------------------|
|
|
152
|
-
| OSS
|
|
153
|
+
| OSS HTTP | Default global `fetch`; inject `fetch` / `tryLocalResolve` | Signed URLs or provider-specific probes |
|
|
153
154
|
| `.local` on another host | Fails availability (by design, like media) | Remote agent ingest |
|
|
154
155
|
| `twinList` on ingest | Not re-validated | Optional probe per twin |
|
|
155
156
|
| Re-ingest / migrate `.local` → `.oss` | Not supported | New API with lock on prior source tag |
|
|
@@ -13,9 +13,11 @@ import { buildFeedLinkName } from "./feed.name";
|
|
|
13
13
|
|
|
14
14
|
export interface BuildFeedLinkDraftInput {
|
|
15
15
|
value: FeedValue;
|
|
16
|
+
/** When set, used as Link `name` (skips {@link buildFeedLinkName}). */
|
|
17
|
+
name?: string;
|
|
16
18
|
/** Dots are normalized to spaces in {@link buildFeedLinkName} before joining with `feedKey`. */
|
|
17
|
-
platformName
|
|
18
|
-
feedKey
|
|
19
|
+
platformName?: string;
|
|
20
|
+
feedKey?: string;
|
|
19
21
|
source: FeedSource;
|
|
20
22
|
locator: string;
|
|
21
23
|
/**
|
|
@@ -75,7 +77,8 @@ export function buildFeedLinkDraft(input: BuildFeedLinkDraftInput): TargetDraft<
|
|
|
75
77
|
throw new Error(`buildFeedLinkDraft: unsupported feed source: ${String(input.source)}`);
|
|
76
78
|
}
|
|
77
79
|
|
|
78
|
-
const
|
|
80
|
+
const explicitName = input.name?.trim() ?? "";
|
|
81
|
+
const name = explicitName !== "" ? explicitName : buildFeedLinkName(input.platformName ?? "", input.feedKey ?? "");
|
|
79
82
|
const locator = input.locator.trim();
|
|
80
83
|
if (locator === "") {
|
|
81
84
|
throw new Error("buildFeedLinkDraft: locator is empty");
|
|
@@ -87,7 +87,19 @@ export async function checkFeedLocalAvailability(
|
|
|
87
87
|
const OSS_CHECK_TIMEOUT_MS = 15_000;
|
|
88
88
|
const OSS_CHECK_MAX_ATTEMPTS = 2;
|
|
89
89
|
|
|
90
|
-
|
|
90
|
+
export interface CheckFeedOssAvailabilityOptions {
|
|
91
|
+
/** Override global `fetch` (proxied outbound, etc.). Default: SDK `fetchWithRetry`. */
|
|
92
|
+
fetch?: (url: string, init?: RequestInit) => Promise<Response>;
|
|
93
|
+
/** When true, skip HTTP (co-located Files sandbox, etc.). */
|
|
94
|
+
tryLocalResolve?: (locator: string) => Promise<boolean>;
|
|
95
|
+
timeoutMs?: number;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async function probeHttpLocator(
|
|
99
|
+
locator: string,
|
|
100
|
+
method: "HEAD" | "GET",
|
|
101
|
+
options?: CheckFeedOssAvailabilityOptions,
|
|
102
|
+
): Promise<Response> {
|
|
91
103
|
return fetchWithRetry(
|
|
92
104
|
locator,
|
|
93
105
|
{
|
|
@@ -96,8 +108,9 @@ async function probeHttpLocator(locator: string, method: "HEAD" | "GET"): Promis
|
|
|
96
108
|
},
|
|
97
109
|
{
|
|
98
110
|
label: "feed-oss-check",
|
|
99
|
-
timeoutMs: OSS_CHECK_TIMEOUT_MS,
|
|
111
|
+
timeoutMs: options?.timeoutMs ?? OSS_CHECK_TIMEOUT_MS,
|
|
100
112
|
maxAttempts: OSS_CHECK_MAX_ATTEMPTS,
|
|
113
|
+
...(options?.fetch != null ? { fetch: options.fetch } : {}),
|
|
101
114
|
},
|
|
102
115
|
);
|
|
103
116
|
}
|
|
@@ -105,11 +118,15 @@ async function probeHttpLocator(locator: string, method: "HEAD" | "GET"): Promis
|
|
|
105
118
|
/**
|
|
106
119
|
* Remote reachability for feed ingest (`.oss` source) via HTTP(S).
|
|
107
120
|
*
|
|
108
|
-
*
|
|
121
|
+
* Optional `tryLocalResolve` first (same-host Files). Then `HEAD`, then `GET` with
|
|
122
|
+
* `Range: bytes=0-0` when `HEAD` is not allowed. Pass `fetch` for proxied outbound.
|
|
109
123
|
*
|
|
110
124
|
* **Node-only**
|
|
111
125
|
*/
|
|
112
|
-
export async function checkFeedOssAvailability(
|
|
126
|
+
export async function checkFeedOssAvailability(
|
|
127
|
+
locator: string,
|
|
128
|
+
options?: CheckFeedOssAvailabilityOptions,
|
|
129
|
+
): Promise<FeedAvailabilityResult> {
|
|
113
130
|
const trimmed = locator.trim();
|
|
114
131
|
if (trimmed === "") {
|
|
115
132
|
return { ok: false, reason: "locator is empty" };
|
|
@@ -118,10 +135,20 @@ export async function checkFeedOssAvailability(locator: string): Promise<FeedAva
|
|
|
118
135
|
return { ok: false, reason: `oss locator must be an http(s) URL: ${trimmed}` };
|
|
119
136
|
}
|
|
120
137
|
|
|
138
|
+
if (options?.tryLocalResolve != null) {
|
|
139
|
+
try {
|
|
140
|
+
if (await options.tryLocalResolve(trimmed)) {
|
|
141
|
+
return { ok: true };
|
|
142
|
+
}
|
|
143
|
+
} catch {
|
|
144
|
+
// fall through to HTTP
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
121
148
|
try {
|
|
122
|
-
let response = await probeHttpLocator(trimmed, "HEAD");
|
|
149
|
+
let response = await probeHttpLocator(trimmed, "HEAD", options);
|
|
123
150
|
if (response.status === 405 || response.status === 501) {
|
|
124
|
-
response = await probeHttpLocator(trimmed, "GET");
|
|
151
|
+
response = await probeHttpLocator(trimmed, "GET", options);
|
|
125
152
|
}
|
|
126
153
|
if (response.ok || (response.status >= 200 && response.status < 400)) {
|
|
127
154
|
return { ok: true };
|
|
@@ -1,8 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
getPossibleTarget,
|
|
3
|
+
getTarget,
|
|
4
|
+
isCreateTargetAlreadyExistsError,
|
|
5
|
+
postTarget,
|
|
6
|
+
updateTarget,
|
|
7
|
+
} from "../../../core.api";
|
|
2
8
|
import type { Target } from "../../../core.interface";
|
|
3
9
|
import { CategoryLink, type Link, type LinkDetails } from "../../../link/link.interface";
|
|
4
10
|
import { createLogger } from "../../log/core/create-logger";
|
|
5
|
-
import { buildFeedTagList, resolveFeedOriginalStorageProvider } from "../feed.build";
|
|
11
|
+
import { buildFeedLinkDraft, buildFeedTagList, resolveFeedOriginalStorageProvider } from "../feed.build";
|
|
6
12
|
import {
|
|
7
13
|
type FeedIngestSource,
|
|
8
14
|
type FeedOriginal,
|
|
@@ -10,7 +16,12 @@ import {
|
|
|
10
16
|
isFeedIngestSource,
|
|
11
17
|
isFeedValue,
|
|
12
18
|
} from "../feed.interface";
|
|
13
|
-
import {
|
|
19
|
+
import { feedLinkDedupFilters } from "../feed.query";
|
|
20
|
+
import {
|
|
21
|
+
type CheckFeedOssAvailabilityOptions,
|
|
22
|
+
checkFeedLocalAvailability,
|
|
23
|
+
checkFeedOssAvailability,
|
|
24
|
+
} from "./feed.access";
|
|
14
25
|
|
|
15
26
|
const logger = createLogger({ module: "feed" });
|
|
16
27
|
|
|
@@ -22,6 +33,8 @@ export interface RegisterFeedInput {
|
|
|
22
33
|
locator: string;
|
|
23
34
|
/** Required when `source` is `.local` (e.g. `process.env.LOCAL_STORAGE_PROVIDER`). Ignored for `.oss`. */
|
|
24
35
|
localStorageProvider?: string;
|
|
36
|
+
/** `.oss` HTTP check: inject `fetch` / same-host resolve. */
|
|
37
|
+
ossCheck?: CheckFeedOssAvailabilityOptions;
|
|
25
38
|
}
|
|
26
39
|
|
|
27
40
|
export interface RegisterFeedResult {
|
|
@@ -137,7 +150,7 @@ export async function registerFeed(input: RegisterFeedInput): Promise<RegisterFe
|
|
|
137
150
|
throw new Error(`registerFeed: local locator unavailable — ${availability.reason ?? "unknown"}`);
|
|
138
151
|
}
|
|
139
152
|
} else {
|
|
140
|
-
const availability = await checkFeedOssAvailability(locator);
|
|
153
|
+
const availability = await checkFeedOssAvailability(locator, input.ossCheck);
|
|
141
154
|
if (!availability.ok) {
|
|
142
155
|
throw new Error(`registerFeed: oss locator unavailable — ${availability.reason ?? "unknown"}`);
|
|
143
156
|
}
|
|
@@ -181,3 +194,117 @@ export async function registerFeed(input: RegisterFeedInput): Promise<RegisterFe
|
|
|
181
194
|
});
|
|
182
195
|
return { id, updated: true };
|
|
183
196
|
}
|
|
197
|
+
|
|
198
|
+
export interface CreateFeedInput {
|
|
199
|
+
/** Full Link `name` (Filter itemName, etc.). */
|
|
200
|
+
name: string;
|
|
201
|
+
value: FeedValue;
|
|
202
|
+
source: FeedIngestSource;
|
|
203
|
+
storageProvider: string;
|
|
204
|
+
locator: string;
|
|
205
|
+
description?: string;
|
|
206
|
+
preview?: string;
|
|
207
|
+
tagList?: string[];
|
|
208
|
+
localStorageProvider?: string;
|
|
209
|
+
ossCheck?: CheckFeedOssAvailabilityOptions;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export interface CreateFeedResult {
|
|
213
|
+
id: string;
|
|
214
|
+
created: boolean;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Insert a new feed Link already on `.local` or `.oss` (not a `.public` promote).
|
|
219
|
+
* Dedup: `category` + `value` + `name`. Same original → `{ created: false }`.
|
|
220
|
+
*/
|
|
221
|
+
export async function createFeed(input: CreateFeedInput): Promise<CreateFeedResult> {
|
|
222
|
+
if (!isFeedValue(input.value)) {
|
|
223
|
+
throw new Error(`createFeed: unsupported feed value: ${String(input.value)}`);
|
|
224
|
+
}
|
|
225
|
+
if (!isFeedIngestSource(input.source)) {
|
|
226
|
+
throw new Error(`createFeed: source must be .local or .oss, got ${String(input.source)}`);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const name = input.name.trim();
|
|
230
|
+
if (name === "") {
|
|
231
|
+
throw new Error("createFeed: name is empty");
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const resolvedStorageProvider = resolveFeedOriginalStorageProvider(input.source, input.storageProvider);
|
|
235
|
+
const locator = input.locator.trim();
|
|
236
|
+
if (locator === "") {
|
|
237
|
+
throw new Error("createFeed: locator is empty");
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const { data: existing } = await getPossibleTarget({
|
|
241
|
+
filterList: feedLinkDedupFilters({ value: input.value, name }),
|
|
242
|
+
});
|
|
243
|
+
if (existing != null) {
|
|
244
|
+
const link = existing as Link;
|
|
245
|
+
if (isSameIngestState(link, input.source, resolvedStorageProvider, locator)) {
|
|
246
|
+
return { id: existing.id, created: false };
|
|
247
|
+
}
|
|
248
|
+
throw new Error("createFeed: feed name already exists with different source, storageProvider, or locator");
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (input.source === ".local") {
|
|
252
|
+
const localStorageProvider = input.localStorageProvider?.trim() ?? "";
|
|
253
|
+
if (localStorageProvider === "") {
|
|
254
|
+
throw new Error("createFeed: localStorageProvider is required when source is .local");
|
|
255
|
+
}
|
|
256
|
+
const availability = await checkFeedLocalAvailability({
|
|
257
|
+
value: input.value,
|
|
258
|
+
storageProvider: resolvedStorageProvider,
|
|
259
|
+
locator,
|
|
260
|
+
localStorageProvider,
|
|
261
|
+
});
|
|
262
|
+
if (!availability.ok) {
|
|
263
|
+
throw new Error(`createFeed: local locator unavailable — ${availability.reason ?? "unknown"}`);
|
|
264
|
+
}
|
|
265
|
+
} else {
|
|
266
|
+
const availability = await checkFeedOssAvailability(locator, input.ossCheck);
|
|
267
|
+
if (!availability.ok) {
|
|
268
|
+
throw new Error(`createFeed: oss locator unavailable — ${availability.reason ?? "unknown"}`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const draft = buildFeedLinkDraft({
|
|
273
|
+
name,
|
|
274
|
+
value: input.value,
|
|
275
|
+
source: input.source,
|
|
276
|
+
storageProvider: resolvedStorageProvider,
|
|
277
|
+
locator,
|
|
278
|
+
description: input.description,
|
|
279
|
+
preview: input.preview ?? locator,
|
|
280
|
+
tagList: input.tagList,
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
try {
|
|
284
|
+
const { data, error } = await postTarget({
|
|
285
|
+
name: draft.name,
|
|
286
|
+
value: draft.value,
|
|
287
|
+
category: draft.category,
|
|
288
|
+
tagList: draft.tagList,
|
|
289
|
+
details: draft.details,
|
|
290
|
+
});
|
|
291
|
+
if (error != null || data == null) {
|
|
292
|
+
throw new Error(error?.message ?? "createFeed: postTarget failed");
|
|
293
|
+
}
|
|
294
|
+
logger.info("feed created", {
|
|
295
|
+
topic: "register",
|
|
296
|
+
data: { id: data.id, source: input.source, name, locator },
|
|
297
|
+
});
|
|
298
|
+
return { id: data.id, created: true };
|
|
299
|
+
} catch (error) {
|
|
300
|
+
if (isCreateTargetAlreadyExistsError(error)) {
|
|
301
|
+
const raced = await getPossibleTarget({
|
|
302
|
+
filterList: feedLinkDedupFilters({ value: input.value, name }),
|
|
303
|
+
});
|
|
304
|
+
if (raced.data != null) {
|
|
305
|
+
return { id: raced.data.id, created: false };
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
throw error;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
@@ -5,7 +5,11 @@
|
|
|
5
5
|
|
|
6
6
|
export type { FeedIngestSource } from "../feed.interface";
|
|
7
7
|
export { FEED_INGEST_SOURCES, isFeedIngestSource } from "../feed.interface";
|
|
8
|
-
export type {
|
|
8
|
+
export type {
|
|
9
|
+
CheckFeedLocalAvailabilityInput,
|
|
10
|
+
CheckFeedOssAvailabilityOptions,
|
|
11
|
+
FeedAvailabilityResult,
|
|
12
|
+
} from "./feed.access";
|
|
9
13
|
export { checkFeedLocalAvailability, checkFeedOssAvailability } from "./feed.access";
|
|
10
|
-
export type { RegisterFeedInput, RegisterFeedResult } from "./feed.service";
|
|
11
|
-
export { registerFeed } from "./feed.service";
|
|
14
|
+
export type { CreateFeedInput, CreateFeedResult, RegisterFeedInput, RegisterFeedResult } from "./feed.service";
|
|
15
|
+
export { createFeed, registerFeed } from "./feed.service";
|
|
@@ -28,6 +28,8 @@ export interface FetchRetryOptions {
|
|
|
28
28
|
/** When set, retry/success lines are logged via SDK logger. */
|
|
29
29
|
logger?: LoggerWithScope;
|
|
30
30
|
hint?: string;
|
|
31
|
+
/** Override global `fetch` (e.g. proxied outbound fetch). Ignores `dispatcher`. */
|
|
32
|
+
fetch?: (url: string, init?: RequestInit) => Promise<Response>;
|
|
31
33
|
}
|
|
32
34
|
|
|
33
35
|
function backoffMs(retryBaseMs: number, attempt: number, response?: Response, maxBackoffMs?: number): number {
|
|
@@ -47,11 +49,20 @@ function backoffMs(retryBaseMs: number, attempt: number, response?: Response, ma
|
|
|
47
49
|
return delayMs;
|
|
48
50
|
}
|
|
49
51
|
|
|
50
|
-
async function fetchOnce(
|
|
52
|
+
async function fetchOnce(
|
|
53
|
+
url: string,
|
|
54
|
+
init: RequestInit,
|
|
55
|
+
timeoutMs: number,
|
|
56
|
+
dispatcher?: unknown,
|
|
57
|
+
fetchImpl?: (url: string, init?: RequestInit) => Promise<Response>,
|
|
58
|
+
): Promise<Response> {
|
|
51
59
|
const options: RequestInit & { dispatcher?: unknown } = {
|
|
52
60
|
...init,
|
|
53
61
|
signal: AbortSignal.timeout(timeoutMs),
|
|
54
62
|
};
|
|
63
|
+
if (fetchImpl != null) {
|
|
64
|
+
return fetchImpl(url, options);
|
|
65
|
+
}
|
|
55
66
|
if (dispatcher != null) {
|
|
56
67
|
options.dispatcher = dispatcher;
|
|
57
68
|
}
|
|
@@ -76,7 +87,7 @@ export async function fetchWithRetry(
|
|
|
76
87
|
|
|
77
88
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
78
89
|
try {
|
|
79
|
-
const response = await fetchOnce(url, buildInit(), options.timeoutMs, options.dispatcher);
|
|
90
|
+
const response = await fetchOnce(url, buildInit(), options.timeoutMs, options.dispatcher, options.fetch);
|
|
80
91
|
|
|
81
92
|
if (!response.ok && isRetryableStatus(response.status) && attempt < maxAttempts) {
|
|
82
93
|
const delayMs = backoffMs(retryBaseMs, attempt, response, options.maxBackoffMs);
|