target-supabase-sdk 0.2.20 → 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.
Files changed (45) hide show
  1. package/dist/browser.d.ts +1 -1
  2. package/dist/browser.d.ts.map +1 -1
  3. package/dist/browser.js +1 -1
  4. package/dist/chunks/{task-queue-CqmIVCBi.js → task-queue-DWFe0rCS.js} +20 -22
  5. package/dist/chunks/task-queue-DWFe0rCS.js.map +1 -0
  6. package/dist/node.d.ts +2 -2
  7. package/dist/node.d.ts.map +1 -1
  8. package/dist/node.js +174 -19
  9. package/dist/node.js.map +1 -1
  10. package/dist/shared/feed/feed.build.d.ts +4 -2
  11. package/dist/shared/feed/feed.build.d.ts.map +1 -1
  12. package/dist/shared/feed/node/feed.access.d.ts +10 -2
  13. package/dist/shared/feed/node/feed.access.d.ts.map +1 -1
  14. package/dist/shared/feed/node/feed.service.d.ts +26 -1
  15. package/dist/shared/feed/node/feed.service.d.ts.map +1 -1
  16. package/dist/shared/feed/node/index.d.ts +3 -3
  17. package/dist/shared/feed/node/index.d.ts.map +1 -1
  18. package/dist/shared/http/fetch-retry.d.ts +2 -0
  19. package/dist/shared/http/fetch-retry.d.ts.map +1 -1
  20. package/dist/shared/media/node/media.service.d.ts +8 -2
  21. package/dist/shared/media/node/media.service.d.ts.map +1 -1
  22. package/dist/shared/review/index.d.ts +1 -1
  23. package/dist/shared/review/index.d.ts.map +1 -1
  24. package/dist/shared/review/review.build.d.ts +3 -6
  25. package/dist/shared/review/review.build.d.ts.map +1 -1
  26. package/dist/shared/review/review.interface.d.ts +2 -2
  27. package/dist/shared/review/review.query.d.ts +2 -2
  28. package/dist/shared/review/review.query.d.ts.map +1 -1
  29. package/package.json +1 -1
  30. package/src/browser.ts +1 -1
  31. package/src/node.ts +4 -0
  32. package/src/shared/feed/README.md +4 -3
  33. package/src/shared/feed/feed.build.ts +6 -3
  34. package/src/shared/feed/node/feed.access.ts +33 -6
  35. package/src/shared/feed/node/feed.service.ts +131 -4
  36. package/src/shared/feed/node/index.ts +7 -3
  37. package/src/shared/http/fetch-retry.ts +13 -2
  38. package/src/shared/media/README.md +4 -4
  39. package/src/shared/media/node/media.service.ts +77 -10
  40. package/src/shared/review/README.md +15 -16
  41. package/src/shared/review/index.ts +1 -1
  42. package/src/shared/review/review.build.ts +8 -13
  43. package/src/shared/review/review.interface.ts +2 -2
  44. package/src/shared/review/review.query.ts +4 -4
  45. package/dist/chunks/task-queue-CqmIVCBi.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: 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"}
@@ -3,5 +3,5 @@
3
3
  */
4
4
  export type { BuildReviewLinkDraftInput } from "./review.build";
5
5
  export { buildReviewLinkDraft } from "./review.build";
6
- export { reviewLinksForTargetFilters } from "./review.query";
6
+ export { reviewLinksForNameFilters } from "./review.query";
7
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/shared/review/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,YAAY,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;AAChE,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAE,2BAA2B,EAAE,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/shared/review/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,YAAY,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;AAChE,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAC"}
@@ -1,11 +1,8 @@
1
1
  import type { TargetDraft } from "../../core.interface";
2
2
  import type { Link } from "../../link/link.interface";
3
- import { type ReviewValue } from "./review.interface";
4
3
  export interface BuildReviewLinkDraftInput {
5
- /** Must be {@link REVIEW_VALUE}. */
6
- value: ReviewValue;
7
- /** Associated Target id — written to `Link.name`. */
8
- refId: string;
4
+ /** Written to `Link.name` (caller-defined; often a subject Target id). */
5
+ name: string;
9
6
  /** Interaction payload — written to `Link.details.original`. */
10
7
  original: unknown;
11
8
  tagList?: string[];
@@ -13,7 +10,7 @@ export interface BuildReviewLinkDraftInput {
13
10
  }
14
11
  /**
15
12
  * Assemble a review {@link Link} draft (`category=link`, `loaderKey === value === "review"`).
16
- * Does not validate `refId` existence or write to Supabase.
13
+ * Does not validate `name` or write to Supabase.
17
14
  */
18
15
  export declare function buildReviewLinkDraft(input: BuildReviewLinkDraftInput): TargetDraft<Link>;
19
16
  //# sourceMappingURL=review.build.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"review.build.d.ts","sourceRoot":"","sources":["../../../src/shared/review/review.build.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,2BAA2B,CAAC;AACtD,OAAO,EAA+B,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEnF,MAAM,WAAW,yBAAyB;IACtC,oCAAoC;IACpC,KAAK,EAAE,WAAW,CAAC;IACnB,qDAAqD;IACrD,KAAK,EAAE,MAAM,CAAC;IACd,gEAAgE;IAChE,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,yBAAyB,GAAG,WAAW,CAAC,IAAI,CAAC,CAkBxF"}
1
+ {"version":3,"file":"review.build.d.ts","sourceRoot":"","sources":["../../../src/shared/review/review.build.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,2BAA2B,CAAC;AAGtD,MAAM,WAAW,yBAAyB;IACtC,0EAA0E;IAC1E,IAAI,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,yBAAyB,GAAG,WAAW,CAAC,IAAI,CAAC,CAexF"}
@@ -2,8 +2,8 @@
2
2
  * Review Link protocol — Link rows with `value` / `loaderKey` === {@link REVIEW_VALUE}.
3
3
  * No separate Target category; category remains `link`.
4
4
  *
5
- * A Review records one user interaction with a subject Target (`Link.name` = that Target's id).
6
- * Many review Links may share the same subject id.
5
+ * A Review records one user interaction; `Link.name` is caller-defined (often a subject Target id).
6
+ * Many review Links may share the same `name`.
7
7
  */
8
8
  export declare const REVIEW_VALUE: "review";
9
9
  /** Discriminator stored in both `Link.value` and `details.loaderKey`. */
@@ -1,7 +1,7 @@
1
1
  import type { QueryFilter } from "../../core.interface";
2
2
  /**
3
- * Filters for all review Links whose subject Target is `refId` (`Link.name === refId`).
3
+ * Filters for review Links with `Link.name === name`.
4
4
  * Many rows may match — the SDK does not define dedup for reviews.
5
5
  */
6
- export declare function reviewLinksForTargetFilters(refId: string): QueryFilter[];
6
+ export declare function reviewLinksForNameFilters(name: string): QueryFilter[];
7
7
  //# sourceMappingURL=review.query.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"review.query.d.ts","sourceRoot":"","sources":["../../../src/shared/review/review.query.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAIxD;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,EAAE,CAOxE"}
1
+ {"version":3,"file":"review.query.d.ts","sourceRoot":"","sources":["../../../src/shared/review/review.query.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAIxD;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,EAAE,CAOrE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "target-supabase-sdk",
3
- "version": "0.2.20",
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/browser.ts CHANGED
@@ -242,7 +242,7 @@ export {
242
242
  mediaLinkDedupFilters,
243
243
  } from "./shared/media";
244
244
  export type { BuildReviewLinkDraftInput } from "./shared/review";
245
- export { buildReviewLinkDraft, reviewLinksForTargetFilters } from "./shared/review";
245
+ export { buildReviewLinkDraft, reviewLinksForNameFilters } from "./shared/review";
246
246
  export { isHttpUrl, resolveFetchUrl } from "./shared/utils/fetch-url";
247
247
  export { getValueAtPath } from "./shared/utils/get-value-at-path";
248
248
  export type {
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";