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.
@@ -1,6 +1,7 @@
1
- import { getPossibleTarget, isCreateTargetAlreadyExistsError, postTarget } from "../../../core.api";
2
- import type { Link } from "../../../link/link.interface";
1
+ import { getPossibleTarget, isCreateTargetAlreadyExistsError, postTarget, updateTarget } from "../../../core.api";
2
+ import { CategoryLink, type Link, type LinkDetails } from "../../../link/link.interface";
3
3
  import { createLogger } from "../../log/core/create-logger";
4
+ import { isHttpUrl } from "../../utils/fetch-url";
4
5
  import { buildMediaLinkDraft } from "../media.build";
5
6
  import { isMediaValue, type MediaValue } from "../media.interface";
6
7
  import { mediaLinkDedupFilters } from "../media.query";
@@ -35,6 +36,11 @@ export interface RegisterMediaLinkInput {
35
36
  locator: string;
36
37
  /** This host id for availability (e.g. `process.env.LOCAL_STORAGE_PROVIDER`). */
37
38
  localStorageProvider: string;
39
+ /**
40
+ * Local file to probe when `locator` is http(s) (OSS ingest).
41
+ * Availability uses this path + `localStorageProvider`.
42
+ */
43
+ localLocator?: string;
38
44
  description?: string;
39
45
  tagList?: string[];
40
46
  }
@@ -44,9 +50,53 @@ export interface RegisterMediaLinkResult {
44
50
  created: boolean;
45
51
  }
46
52
 
53
+ function mediaOriginalFromRow(row: { details?: unknown }): { storageProvider?: string; locator?: string } {
54
+ const details = row.details as LinkDetails | undefined;
55
+ const original = details?.original as { storageProvider?: string; locator?: string } | undefined;
56
+ return original ?? {};
57
+ }
58
+
59
+ async function patchMediaOriginal(
60
+ existing: Link,
61
+ storageProvider: string,
62
+ locator: string,
63
+ ): Promise<RegisterMediaLinkResult> {
64
+ const original = mediaOriginalFromRow(existing);
65
+ if (original.storageProvider === storageProvider && original.locator === locator) {
66
+ return { id: existing.id, created: false };
67
+ }
68
+ await updateTarget({
69
+ id: existing.id,
70
+ optimisticLockFilterList: [
71
+ { field: "category", operator: "eq", value: CategoryLink.LINK },
72
+ { field: "value", operator: "eq", value: existing.value },
73
+ ],
74
+ updateFn: (row) => {
75
+ const details = row.details as LinkDetails;
76
+ const prev = (details.original ?? {}) as Record<string, unknown>;
77
+ return {
78
+ details: {
79
+ ...details,
80
+ original: {
81
+ ...prev,
82
+ storageProvider,
83
+ locator,
84
+ },
85
+ },
86
+ };
87
+ },
88
+ });
89
+ logger.info("media original patched", {
90
+ topic: "register",
91
+ data: { id: existing.id, locator },
92
+ });
93
+ return { id: existing.id, created: false };
94
+ }
95
+
47
96
  /**
48
97
  * Register a media Link: dedup → availability → probe → `postTarget`.
49
- * Idempotent when `category`+`value`+`name` already exist.
98
+ * Idempotent when `category`+`value`+`name` already exist (local path).
99
+ * HTTP `locator` + `localLocator`: probe local file, persist remote URL; patch if name exists.
50
100
  *
51
101
  * **Node-only** — under `shared/media/node/`; do not import from the browser entry.
52
102
  */
@@ -59,19 +109,33 @@ export async function registerMediaLink(input: RegisterMediaLinkInput): Promise<
59
109
  throw new Error("registerMediaLink: name is empty");
60
110
  }
61
111
 
112
+ const storageProvider = input.storageProvider.trim();
113
+ const locator = input.locator.trim();
114
+ const localLocator = input.localLocator?.trim() ?? "";
115
+ const httpLocator = isHttpUrl(locator);
116
+
62
117
  const existing = await findMediaLink({ value: input.value, name });
63
- if (existing != null) {
118
+ if (existing != null && !httpLocator) {
64
119
  logger.info("media already registered", {
65
120
  topic: "dedup",
66
121
  data: { id: existing.id, value: input.value, name },
67
122
  });
68
123
  return { id: existing.id, created: false };
69
124
  }
125
+ if (existing != null && httpLocator) {
126
+ return patchMediaOriginal(existing, storageProvider, locator);
127
+ }
128
+
129
+ const probeStorageProvider = httpLocator ? input.localStorageProvider.trim() : storageProvider;
130
+ const probeLocator = httpLocator ? localLocator : locator;
131
+ if (httpLocator && probeLocator === "") {
132
+ throw new Error("registerMediaLink: localLocator is required when locator is http(s)");
133
+ }
70
134
 
71
135
  const availability = await checkMediaAvailability({
72
136
  value: input.value,
73
- storageProvider: input.storageProvider,
74
- locator: input.locator,
137
+ storageProvider: probeStorageProvider,
138
+ locator: probeLocator,
75
139
  localStorageProvider: input.localStorageProvider,
76
140
  });
77
141
  if (!availability.ok || availability.absolutePath == null) {
@@ -80,16 +144,16 @@ export async function registerMediaLink(input: RegisterMediaLinkInput): Promise<
80
144
 
81
145
  const probed = await probeMediaOriginal({
82
146
  value: input.value,
83
- storageProvider: input.storageProvider,
84
- locator: input.locator,
147
+ storageProvider: probeStorageProvider,
148
+ locator: probeLocator,
85
149
  absolutePath: availability.absolutePath,
86
150
  });
87
151
 
88
152
  const draft = buildMediaLinkDraft({
89
153
  value: input.value,
90
154
  name,
91
- storageProvider: probed.storageProvider,
92
- locator: probed.locator,
155
+ storageProvider: httpLocator ? storageProvider : probed.storageProvider,
156
+ locator: httpLocator ? locator : probed.locator,
93
157
  contentHash: probed.contentHash,
94
158
  size: probed.size,
95
159
  mimeType: probed.mimeType,
@@ -117,6 +181,9 @@ export async function registerMediaLink(input: RegisterMediaLinkInput): Promise<
117
181
  if (isCreateTargetAlreadyExistsError(error)) {
118
182
  const raced = await findMediaLink({ value: input.value, name });
119
183
  if (raced != null) {
184
+ if (httpLocator) {
185
+ return patchMediaOriginal(raced, storageProvider, locator);
186
+ }
120
187
  logger.info("media register race — using existing", {
121
188
  topic: "dedup",
122
189
  data: { id: raced.id, value: input.value, name },