target-supabase-sdk 0.2.21 → 0.2.22

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.
@@ -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: string;
8
- feedKey: string;
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;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,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,CA+BpF"}
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
- * Tries `HEAD`, then `GET` with `Range: bytes=0-0` when `HEAD` is not allowed.
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;AAoBD;;;;;;GAMG;AACH,wBAAsB,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAsB/F"}
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":"AAKA,OAAO,EACH,KAAK,gBAAgB,EAKxB,MAAM,mBAAmB,CAAC;AAK3B,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;CACjC;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"}
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,EAAE,+BAA+B,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAC7F,OAAO,EAAE,0BAA0B,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAC;AACrF,YAAY,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAC5E,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC"}
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;CACjB;AA8BD,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
+ {"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 { Link } from "../../../link/link.interface";
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,EAAE,IAAI,EAAE,MAAM,8BAA8B,CAAC;AAGzD,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,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;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CAAC,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CA4EvG"}
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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "target-supabase-sdk",
3
- "version": "0.2.21",
3
+ "version": "0.2.22",
4
4
  "description": "Supabase client initializer and data access APIs",
5
5
  "license": "MIT",
6
6
  "author": "KyLoc20",
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 auth | Only anonymous `HEAD`/`GET` | Signed URLs or provider-specific probes |
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: string;
18
- feedKey: string;
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 name = buildFeedLinkName(input.platformName, input.feedKey);
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
- async function probeHttpLocator(locator: string, method: "HEAD" | "GET"): Promise<Response> {
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
- * Tries `HEAD`, then `GET` with `Range: bytes=0-0` when `HEAD` is not allowed.
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(locator: string): Promise<FeedAvailabilityResult> {
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 { getTarget, updateTarget } from "../../../core.api";
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 { checkFeedLocalAvailability, checkFeedOssAvailability } from "./feed.access";
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 { CheckFeedLocalAvailabilityInput, FeedAvailabilityResult } from "./feed.access";
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(url: string, init: RequestInit, timeoutMs: number, dispatcher?: unknown): Promise<Response> {
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);
@@ -93,10 +93,10 @@ Internal SDK code:
93
93
 
94
94
  ## Register flow (`registerMediaLink`)
95
95
 
96
- 1. **Dedup** — `category` + `value` + `name` via `findMediaLink`. If present → `{ id, created: false }`.
97
- 2. **Availability** — `storageProvider` must equal `localStorageProvider` (typically `LOCAL_STORAGE_PROVIDER` from the service `.env`); `locator` absolute and reachable.
98
- 3. **Probe** — stream SHA-256 / `stat` / MIME (or directory file count for `image-list`).
99
- 4. **Insert** — `buildMediaLinkDraft` → `postTarget` (race → re-find).
96
+ 1. **Dedup** — `category` + `value` + `name` via `findMediaLink`. Local locator: if present → `{ id, created: false }`. HTTP locator: patch `original` when storage/locator differ.
97
+ 2. **Availability** — local path: `storageProvider` must equal `localStorageProvider`; `locator` absolute and reachable. HTTP `locator`: require `localLocator` and probe that file on `localStorageProvider`.
98
+ 3. **Probe** — stream SHA-256 / `stat` / MIME (or directory file count for `image-list`) from the local file.
99
+ 4. **Insert** — `buildMediaLinkDraft` → `postTarget` (race → re-find / HTTP patch). Persisted `locator` may be the HTTP URL.
100
100
 
101
101
  ## Env (Node hosts)
102
102