studiograph 1.3.48-next.243 → 1.3.48-next.245
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/core/format-registry.d.ts +3 -4
- package/dist/core/format-registry.js +2 -22
- package/dist/core/format-registry.js.map +1 -1
- package/dist/server/routes/graph-api.js +0 -367
- package/dist/server/routes/graph-api.js.map +1 -1
- package/dist/services/asset-import-service.js +8 -2
- package/dist/services/asset-import-service.js.map +1 -1
- package/dist/web/_app/immutable/assets/0.D5hAEdWF.css +2 -0
- package/dist/web/_app/immutable/assets/{2.pVA2JaVn.css → 2.Ze9FHgHB.css} +1 -1
- package/dist/web/_app/immutable/assets/{6.DONJs2Ua.css → 6.C-Pcsj3e.css} +1 -1
- package/dist/web/_app/immutable/assets/FolderPicker.DcXaRoRh.css +1 -0
- package/dist/web/_app/immutable/assets/Link.vV0ne105.css +1 -0
- package/dist/web/_app/immutable/assets/SearchInput.DTB6W35f.css +1 -0
- package/dist/web/_app/immutable/chunks/{CSYiGoxd.js → -z3EvSM8.js} +1 -1
- package/dist/web/_app/immutable/chunks/1QBhkNGY.js +2 -0
- package/dist/web/_app/immutable/chunks/4rfliCzl.js +1 -0
- package/dist/web/_app/immutable/chunks/{D_1fVNTS2.js → B4xnctSC2.js} +1 -1
- package/dist/web/_app/immutable/chunks/{BYwcH5OJ.js → B54CoVWW.js} +5 -5
- package/dist/web/_app/immutable/chunks/B6F3lR9e.js +2 -0
- package/dist/web/_app/immutable/chunks/BDB7ntCn2.js +2 -0
- package/dist/web/_app/immutable/chunks/BF_7kq6S2.js +1 -0
- package/dist/web/_app/immutable/chunks/{OlaBOMTk.js → BNJcvVdS.js} +1 -1
- package/dist/web/_app/immutable/chunks/{oYsB08kS.js → BNjzaVxQ.js} +1 -1
- package/dist/web/_app/immutable/chunks/BOrqtMhf.js +2 -0
- package/dist/web/_app/immutable/chunks/BWK7OscY.js +1 -0
- package/dist/web/_app/immutable/chunks/{Dz0NKq7k.js → BXmqyEbG.js} +1 -1
- package/dist/web/_app/immutable/chunks/{BJDXKR9Z.js → BZrgB-Bp.js} +1 -1
- package/dist/web/_app/immutable/chunks/{D7pxZWUe2.js → B_AfdjZ82.js} +1 -1
- package/dist/web/_app/immutable/chunks/{BxFrEcwX.js → BfntpPao.js} +1 -1
- package/dist/web/_app/immutable/chunks/{DjWIE8kE.js → BgMUch8k.js} +1 -1
- package/dist/web/_app/immutable/chunks/{B8Yk5rbB.js → BlY4Q2zs.js} +1 -1
- package/dist/web/_app/immutable/chunks/{D6T1mkH32.js → Bn0dTMMt2.js} +1 -1
- package/dist/web/_app/immutable/chunks/BpmIioBS.js +1 -0
- package/dist/web/_app/immutable/chunks/BrkRt7iO.js +1 -0
- package/dist/web/_app/immutable/chunks/BwrYnqJC.js +1 -0
- package/dist/web/_app/immutable/chunks/ByIJrhBL2.js +2 -0
- package/dist/web/_app/immutable/chunks/C1_nAVpU2.js +2 -0
- package/dist/web/_app/immutable/chunks/{Dqt3ZQbf.js → C2AjEWMe.js} +1 -1
- package/dist/web/_app/immutable/chunks/C2KomZNy2.js +1 -0
- package/dist/web/_app/immutable/chunks/{Blkb3o9A.js → C34xLjx92.js} +1 -1
- package/dist/web/_app/immutable/chunks/{DvDv3PXS.js → C4h89ZLp.js} +1 -1
- package/dist/web/_app/immutable/chunks/CBtjuC5V2.js +1 -0
- package/dist/web/_app/immutable/chunks/CFLSDGmb.js +2 -0
- package/dist/web/_app/immutable/chunks/CFYPGOQ7.js +1 -0
- package/dist/web/_app/immutable/chunks/CFsuplGo.js +1 -0
- package/dist/web/_app/immutable/chunks/{CIgtLr8b.js → CJ90nsQc.js} +1 -1
- package/dist/web/_app/immutable/chunks/CL6ALc46.js +1 -0
- package/dist/web/_app/immutable/chunks/CMQbCljv2.js +1 -0
- package/dist/web/_app/immutable/chunks/CMoYreLc2.js +1 -0
- package/dist/web/_app/immutable/chunks/{DMpy3mnJ2.js → COsIRifI2.js} +1 -1
- package/dist/web/_app/immutable/chunks/CQy56kWA.js +2 -0
- package/dist/web/_app/immutable/chunks/{D1Jt3dpR.js → CVNjqiSP.js} +3 -3
- package/dist/web/_app/immutable/chunks/{WnsexHKM.js → CXTC8UWv.js} +1 -1
- package/dist/web/_app/immutable/chunks/{BsGGYtBY.js → CYH47SHC.js} +3 -3
- package/dist/web/_app/immutable/chunks/CYaenFR32.js +2 -0
- package/dist/web/_app/immutable/chunks/{BDAt3t0d.js → Ce9bF2qC.js} +1 -1
- package/dist/web/_app/immutable/chunks/{-P-_45Lq.js → ChgunDUU.js} +1 -1
- package/dist/web/_app/immutable/chunks/{Dm3FeQiy.js → CimqeClk.js} +2 -2
- package/dist/web/_app/immutable/chunks/{CAGPIH5O2.js → Cu0o7El92.js} +1 -1
- package/dist/web/_app/immutable/chunks/{7hwha0Sh.js → Cy3vI1wf.js} +1 -1
- package/dist/web/_app/immutable/chunks/CzVORdhE.js +1 -0
- package/dist/web/_app/immutable/chunks/{DTRR5v8f.js → D0n5yS-S.js} +1 -1
- package/dist/web/_app/immutable/chunks/{41TuxWjY2.js → DNhhiBhn2.js} +1 -1
- package/dist/web/_app/immutable/chunks/{BuiYznRn2.js → DOlahtYE2.js} +1 -1
- package/dist/web/_app/immutable/chunks/DP9Z_7Vu.js +1 -0
- package/dist/web/_app/immutable/chunks/{DoBrc_MD.js → DPBxDXH4.js} +1 -1
- package/dist/web/_app/immutable/chunks/{x2r4Enci.js → DS69hN0g.js} +1 -1
- package/dist/web/_app/immutable/chunks/DVM0S-P32.js +83 -0
- package/dist/web/_app/immutable/chunks/DW1Na4p7.js +1 -0
- package/dist/web/_app/immutable/chunks/DYkwX6G8.js +2 -0
- package/dist/web/_app/immutable/chunks/{94Ryxh_u.js → DcomKxgG.js} +1 -1
- package/dist/web/_app/immutable/chunks/{BetCjBPr.js → Dh3jEtCq.js} +1 -1
- package/dist/web/_app/immutable/chunks/{yhLA_1G0.js → Dpto9HaZ.js} +1 -1
- package/dist/web/_app/immutable/chunks/{8fpcZDYw.js → DtJP_HMC.js} +1 -1
- package/dist/web/_app/immutable/chunks/{DJ-tF4mo.js → Du23QlMj.js} +1 -1
- package/dist/web/_app/immutable/chunks/DwklDnfc.js +1 -0
- package/dist/web/_app/immutable/chunks/DyD16MJU2.js +1 -0
- package/dist/web/_app/immutable/chunks/FuiJnZG02.js +1 -0
- package/dist/web/_app/immutable/chunks/HAfHtORs2.js +1 -0
- package/dist/web/_app/immutable/chunks/{u78dShfg.js → I-xKVMZ9.js} +1 -1
- package/dist/web/_app/immutable/chunks/{By5fEhAV.js → Qtdx5S6F.js} +1 -1
- package/dist/web/_app/immutable/chunks/{CMKOh3Gu.js → SM0Vdv1t.js} +1 -1
- package/dist/web/_app/immutable/chunks/{CkvGsRCA.js → VlpndFmu.js} +1 -1
- package/dist/web/_app/immutable/chunks/XNON8rtN.js +1 -0
- package/dist/web/_app/immutable/chunks/YxQS0TKh2.js +1 -0
- package/dist/web/_app/immutable/chunks/{deOZsxWU.js → ZcsAuPXO.js} +1 -1
- package/dist/web/_app/immutable/chunks/{2dmmroBJ.js → ikgefdsd.js} +2 -2
- package/dist/web/_app/immutable/chunks/{DFRK-opu.js → lkjqIzcO.js} +1 -1
- package/dist/web/_app/immutable/chunks/mKpfiVnr.js +1 -0
- package/dist/web/_app/immutable/chunks/{BdpPyI9Q.js → nEriPaNX.js} +1 -1
- package/dist/web/_app/immutable/chunks/qkOWImlF.js +1 -0
- package/dist/web/_app/immutable/chunks/rcrWR_Uf.js +6 -0
- package/dist/web/_app/immutable/chunks/s6jAJaWg.js +1 -0
- package/dist/web/_app/immutable/chunks/{DeNJFJmW.js → uX1g-Pg1.js} +1 -1
- package/dist/web/_app/immutable/chunks/{Blnr7o0u2.js → vjPImE3J2.js} +1 -1
- package/dist/web/_app/immutable/chunks/{3NWakot5.js → zyFQM6y92.js} +1 -1
- package/dist/web/_app/immutable/entry/app.BexZ6OWi.js +2 -0
- package/dist/web/_app/immutable/entry/start.Cwsmk9Mi.js +1 -0
- package/dist/web/_app/immutable/nodes/{0.De1hV4CK.js → 0.CheCRcGh.js} +2 -2
- package/dist/web/_app/immutable/nodes/{1.Byc9lVX0.js → 1.OoAhEFS7.js} +1 -1
- package/dist/web/_app/immutable/nodes/{10.5O2PtJDQ.js → 10.3QG8d2aa.js} +1 -1
- package/dist/web/_app/immutable/nodes/{11.B5We-hs7.js → 11.DwKK--w7.js} +1 -1
- package/dist/web/_app/immutable/nodes/{12.BJY0oraq.js → 12.Cpj9cPr1.js} +1 -1
- package/dist/web/_app/immutable/nodes/{13.3tv4r7-W.js → 13._-DG9xgy.js} +1 -1
- package/dist/web/_app/immutable/nodes/{15.CXxJLwsA.js → 15.bfp3xrm4.js} +1 -1
- package/dist/web/_app/immutable/nodes/{16.DAZbcz9r.js → 16.gd23XK23.js} +1 -1
- package/dist/web/_app/immutable/nodes/{17.DNfdzJKO.js → 17.CoVBF1Re.js} +1 -1
- package/dist/web/_app/immutable/nodes/{18.CRWiO34_.js → 18.CQTBp75F.js} +1 -1
- package/dist/web/_app/immutable/nodes/2.DwGpZjOV.js +125 -0
- package/dist/web/_app/immutable/nodes/3.B57VKMc9.js +6 -0
- package/dist/web/_app/immutable/nodes/4.phbkRbal.js +16 -0
- package/dist/web/_app/immutable/nodes/{5.B7l9_B2T.js → 5.7wI26af-.js} +1 -1
- package/dist/web/_app/immutable/nodes/6.Ba0DxgU1.js +6 -0
- package/dist/web/_app/immutable/nodes/{7.CQ8iYk-b.js → 7.Co33Gksv.js} +1 -1
- package/dist/web/_app/immutable/nodes/{8.B-B5mqzx.js → 8.Br7vtWv7.js} +1 -1
- package/dist/web/_app/immutable/nodes/9.CPuVinHx.js +1 -0
- package/dist/web/_app/version.json +1 -1
- package/dist/web/index.html +17 -17
- package/package.json +1 -1
- package/dist/web/_app/immutable/assets/0.4q9K6_aj.css +0 -2
- package/dist/web/_app/immutable/assets/ChevronDown.DxbcH3MF.css +0 -1
- package/dist/web/_app/immutable/assets/FolderPicker.BIvdgOqd.css +0 -1
- package/dist/web/_app/immutable/assets/Link.sbZLqnki.css +0 -1
- package/dist/web/_app/immutable/chunks/39GYFK-92.js +0 -2
- package/dist/web/_app/immutable/chunks/9eAJQZrp.js +0 -2
- package/dist/web/_app/immutable/chunks/B5t0eFJ92.js +0 -1
- package/dist/web/_app/immutable/chunks/BKFu0lwN.js +0 -1
- package/dist/web/_app/immutable/chunks/BKoDfR5M.js +0 -1
- package/dist/web/_app/immutable/chunks/BMH0TYNu.js +0 -1
- package/dist/web/_app/immutable/chunks/BZaiSHRW.js +0 -2
- package/dist/web/_app/immutable/chunks/B_ZKmxdt.js +0 -2
- package/dist/web/_app/immutable/chunks/BeLBbSLQ.js +0 -1
- package/dist/web/_app/immutable/chunks/Bgvs_OLG.js +0 -2
- package/dist/web/_app/immutable/chunks/BqXMLvhL.js +0 -1
- package/dist/web/_app/immutable/chunks/Bu_5WOFt2.js +0 -2
- package/dist/web/_app/immutable/chunks/BudZbwja2.js +0 -1
- package/dist/web/_app/immutable/chunks/ByDn7_Qm.js +0 -1
- package/dist/web/_app/immutable/chunks/C-mgrnTp2.js +0 -1
- package/dist/web/_app/immutable/chunks/C17GgqdR.js +0 -2
- package/dist/web/_app/immutable/chunks/CQ7ssHiz2.js +0 -1
- package/dist/web/_app/immutable/chunks/CQz_RPfB.js +0 -1
- package/dist/web/_app/immutable/chunks/CVGnbOuS2.js +0 -2
- package/dist/web/_app/immutable/chunks/CbjSpBcK2.js +0 -83
- package/dist/web/_app/immutable/chunks/Ciqwqq772.js +0 -1
- package/dist/web/_app/immutable/chunks/Cj6oCHku.js +0 -6
- package/dist/web/_app/immutable/chunks/CpUmT5hX2.js +0 -1
- package/dist/web/_app/immutable/chunks/Cr7JY6F4.js +0 -1
- package/dist/web/_app/immutable/chunks/D-p0AOc9.js +0 -1
- package/dist/web/_app/immutable/chunks/D5PyYNZN2.js +0 -1
- package/dist/web/_app/immutable/chunks/D62Ewp98.js +0 -1
- package/dist/web/_app/immutable/chunks/DYdH-va_.js +0 -2
- package/dist/web/_app/immutable/chunks/Db11KpbT.js +0 -1
- package/dist/web/_app/immutable/chunks/DioG9gEY.js +0 -1
- package/dist/web/_app/immutable/chunks/DwcahpVZ2.js +0 -2
- package/dist/web/_app/immutable/chunks/JPkInEiy2.js +0 -2
- package/dist/web/_app/immutable/chunks/VYOuBRrp2.js +0 -1
- package/dist/web/_app/immutable/chunks/_4BKTeRf.js +0 -1
- package/dist/web/_app/immutable/chunks/_nxBVnQa.js +0 -1
- package/dist/web/_app/immutable/chunks/kkDjm5WC.js +0 -1
- package/dist/web/_app/immutable/entry/app.CyaMSTD-.js +0 -2
- package/dist/web/_app/immutable/entry/start.w51yOSqv.js +0 -1
- package/dist/web/_app/immutable/nodes/2.D2zEcEUa.js +0 -125
- package/dist/web/_app/immutable/nodes/3.DtSKWUk0.js +0 -6
- package/dist/web/_app/immutable/nodes/4.C1C-ZltD.js +0 -16
- package/dist/web/_app/immutable/nodes/6.DT5EDRvz.js +0 -4
- package/dist/web/_app/immutable/nodes/9.A8hgLZWF.js +0 -1
- /package/dist/web/_app/immutable/assets/{Download.BdRFHJDt.css → FileRowList.BdRFHJDt.css} +0 -0
- /package/dist/web/_app/immutable/assets/{ActionMenu.ZD6bzqtr.css → FolderTreeField.ZD6bzqtr.css} +0 -0
- /package/dist/web/_app/immutable/assets/{select.CAcHq8G6.css → checkbox.CAcHq8G6.css} +0 -0
- /package/dist/web/_app/immutable/chunks/{BpCG9m962.js → BpCG9m96.js} +0 -0
- /package/dist/web/_app/immutable/chunks/{CQQp6PxD2.js → CQQp6PxD.js} +0 -0
- /package/dist/web/_app/immutable/chunks/{DcPrzUMJ2.js → DcPrzUMJ.js} +0 -0
- /package/dist/web/_app/immutable/chunks/{DiMsLIh3.js → DiMsLIh32.js} +0 -0
|
@@ -24,9 +24,9 @@
|
|
|
24
24
|
* `path` builtin is fine and shared with the rest of `src/core`.
|
|
25
25
|
*/
|
|
26
26
|
/** How a format is dispatched + what it ultimately becomes. */
|
|
27
|
-
export type FormatCategory = 'direct-note' | 'direct-dataset' | 'asset-convertible' | 'asset'
|
|
27
|
+
export type FormatCategory = 'direct-note' | 'direct-dataset' | 'asset-convertible' | 'asset';
|
|
28
28
|
/** Which server handler ingests the bytes. */
|
|
29
|
-
export type FormatHandler = 'importDirect' | 'uploadAsset'
|
|
29
|
+
export type FormatHandler = 'importDirect' | 'uploadAsset';
|
|
30
30
|
/** The entity a convertible format derives to (entry-primary, D1). */
|
|
31
31
|
export type FormatDeriveTo = 'note' | 'dataset' | 'reference' | null;
|
|
32
32
|
/** When the derivation happens. */
|
|
@@ -97,8 +97,7 @@ export declare function lookupByMime(mime: string): FormatEntry | undefined;
|
|
|
97
97
|
* Resolve the disposition for a file. Never returns undefined — unknown
|
|
98
98
|
* types resolve to `FALLBACK_ASSET_ENTRY` (identity-checkable). Extension is
|
|
99
99
|
* the primary key; `mime` is a fallback when the filename carries no usable
|
|
100
|
-
* extension.
|
|
101
|
-
* NOT handled here.
|
|
100
|
+
* extension.
|
|
102
101
|
*/
|
|
103
102
|
export declare function resolveFormat(filename: string, mime?: string): FormatEntry;
|
|
104
103
|
/** A format is searchable when it declares any index extraction. */
|
|
@@ -32,7 +32,7 @@ const TEXT_MAX_BYTES = 25 * MB;
|
|
|
32
32
|
/**
|
|
33
33
|
* The canonical format table. Order is not significant for lookup (maps are
|
|
34
34
|
* built below), but it reads top-to-bottom as: direct text → convertibles →
|
|
35
|
-
* image assets → opaque binary assets
|
|
35
|
+
* image assets → opaque binary assets.
|
|
36
36
|
*
|
|
37
37
|
* The opaque-asset block intentionally enumerates the FULL set the web import
|
|
38
38
|
* dispatcher already accepts as assets today (video / audio / legacy office /
|
|
@@ -144,25 +144,6 @@ const FORMATS = [
|
|
|
144
144
|
// Enumerated to preserve current dispatcher behavior: these all become
|
|
145
145
|
// assets today via ASSET_UPLOAD_EXTENSIONS. None are content-indexed.
|
|
146
146
|
...opaqueAssets(['.mp4', 'video/mp4'], ['.mov', 'video/quicktime'], ['.webm', 'video/webm'], ['.avi', 'video/x-msvideo'], ['.mkv', 'video/x-matroska'], ['.m4v', 'video/x-m4v'], ['.mp3', 'audio/mpeg'], ['.wav', 'audio/wav'], ['.m4a', 'audio/mp4'], ['.ogg', 'audio/ogg'], ['.flac', 'audio/flac'], ['.aac', 'audio/aac'], ['.xls', 'application/vnd.ms-excel'], ['.numbers', 'application/octet-stream'], ['.tsv', 'text/tab-separated-values'], ['.doc', 'application/msword'], ['.pptx', 'application/vnd.openxmlformats-officedocument.presentationml.presentation'], ['.ppt', 'application/vnd.ms-powerpoint'], ['.key', 'application/octet-stream'], ['.pages', 'application/octet-stream'], ['.indd', 'application/octet-stream'], ['.idml', 'application/octet-stream'], ['.sketch', 'application/octet-stream'], ['.psd', 'image/vnd.adobe.photoshop'], ['.ai', 'application/postscript'], ['.fig', 'application/octet-stream'], ['.zip', 'application/zip'], ['.tar', 'application/x-tar'], ['.gz', 'application/gzip'], ['.7z', 'application/x-7z-compressed'], ['.rar', 'application/vnd.rar'], ['.woff2', 'font/woff2'], ['.woff', 'font/woff'], ['.ttf', 'font/ttf'], ['.otf', 'font/otf']),
|
|
147
|
-
// ── Bookmarks (Netscape HTML → reference entities) ────────────────────────
|
|
148
|
-
// Detected by content (`isNetscapeBookmarkFile`), NOT by extension — a
|
|
149
|
-
// `.html` file is not necessarily a bookmark export. The dispatcher's
|
|
150
|
-
// content-sniff + routing lands in P8; until then bookmark exports use the
|
|
151
|
-
// dedicated `POST /api/repos/:repo/import-bookmarks` endpoint and a bare
|
|
152
|
-
// `.html` resolves to the fallback (rejected in P0). This entry carries no
|
|
153
|
-
// extensions so it is reachable only via the content sniff.
|
|
154
|
-
{
|
|
155
|
-
extensions: [],
|
|
156
|
-
mimeTypes: ['text/html'],
|
|
157
|
-
category: 'bookmarks',
|
|
158
|
-
retainSource: false,
|
|
159
|
-
deriveTo: 'reference',
|
|
160
|
-
derive: 'inline',
|
|
161
|
-
indexAs: 'body',
|
|
162
|
-
handler: 'importBookmarks',
|
|
163
|
-
extractor: null,
|
|
164
|
-
maxBytes: TEXT_MAX_BYTES,
|
|
165
|
-
},
|
|
166
147
|
];
|
|
167
148
|
/** Concise builder for the caption-indexed image-asset block. */
|
|
168
149
|
function imageAssets(...specs) {
|
|
@@ -252,8 +233,7 @@ export function lookupByMime(mime) {
|
|
|
252
233
|
* Resolve the disposition for a file. Never returns undefined — unknown
|
|
253
234
|
* types resolve to `FALLBACK_ASSET_ENTRY` (identity-checkable). Extension is
|
|
254
235
|
* the primary key; `mime` is a fallback when the filename carries no usable
|
|
255
|
-
* extension.
|
|
256
|
-
* NOT handled here.
|
|
236
|
+
* extension.
|
|
257
237
|
*/
|
|
258
238
|
export function resolveFormat(filename, mime) {
|
|
259
239
|
const byExt = byExtension.get(extensionOf(filename));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format-registry.js","sourceRoot":"","sources":["../../src/core/format-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"format-registry.js","sourceRoot":"","sources":["../../src/core/format-registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -1688,373 +1688,6 @@ export async function registerGraphApiRoutes(fastify, workspaceManager, authServ
|
|
|
1688
1688
|
const committed = await sessionManager.flush(repo);
|
|
1689
1689
|
return reply.send({ committed });
|
|
1690
1690
|
});
|
|
1691
|
-
// POST /api/repos/:repo/import — import files into a folder.
|
|
1692
|
-
//
|
|
1693
|
-
// Routing by extension:
|
|
1694
|
-
// .md / .txt / .csv → direct write via importFile() — no LLM
|
|
1695
|
-
// round-trip, preserves the source faithfully. Markdown frontmatter
|
|
1696
|
-
// (entity_type, entity_id) is honored when present; otherwise
|
|
1697
|
-
// sensible defaults.
|
|
1698
|
-
// .pdf + opaque binaries → asset wrapper via uploadAsset() —
|
|
1699
|
-
// creates an `asset` entity, stores the binary in R2 or the local
|
|
1700
|
-
// volume, writes a PDF text sidecar for vector indexing.
|
|
1701
|
-
// .docx → text extraction + agent capture
|
|
1702
|
-
// pipeline (until the mammoth → markdown workstream lands).
|
|
1703
|
-
fastify.post('/api/repos/:repo/import', async (req, reply) => {
|
|
1704
|
-
const { repo } = req.params;
|
|
1705
|
-
// Access enforced by graphForWrite — import creates entities, so write
|
|
1706
|
-
// is required. Graph captured here is reused by the inner closures so
|
|
1707
|
-
// they don't need to re-resolve (which would also bypass the gate).
|
|
1708
|
-
const repoGraph = graphForWrite(req, reply, repo);
|
|
1709
|
-
if (!repoGraph)
|
|
1710
|
-
return;
|
|
1711
|
-
const body = req.body;
|
|
1712
|
-
if (!body?.files || !Array.isArray(body.files)) {
|
|
1713
|
-
return reply.status(400).send({ error: 'Expected { files: [{ filename, content }] }' });
|
|
1714
|
-
}
|
|
1715
|
-
// Reject paths with traversal segments; importFile slugifies the rest.
|
|
1716
|
-
// Empty / undefined = repo root (existing behavior).
|
|
1717
|
-
const targetFolderPath = (body.targetFolderPath ?? '').trim();
|
|
1718
|
-
if (targetFolderPath.split('/').some(seg => seg === '..' || seg === '.')) {
|
|
1719
|
-
return reply.status(400).send({ error: 'Invalid targetFolderPath: contains traversal segments' });
|
|
1720
|
-
}
|
|
1721
|
-
const captureRepo = body.agentDecides ? undefined : repo;
|
|
1722
|
-
const gitUser = reqGitUser(req);
|
|
1723
|
-
// Cancel support (CANCEL): the web client aborts the fetch via an
|
|
1724
|
-
// AbortController. Listen on the RESPONSE stream's `close` — it fires when
|
|
1725
|
-
// the connection drops, and `writableEnded` is false iff we haven't
|
|
1726
|
-
// finished sending the response yet, i.e. a genuine mid-import client
|
|
1727
|
-
// abort (vs the normal post-response close, where writableEnded is true).
|
|
1728
|
-
// `reply.raw` is the ServerResponse, the canonical place to detect that
|
|
1729
|
-
// the peer went away while we're still working; `req.raw` (the parsed
|
|
1730
|
-
// request) is a less reliable signal for an abort during processing.
|
|
1731
|
-
// Best-effort: the batch runner only reads this between batches, so a
|
|
1732
|
-
// single-batch import still commits its completed prefix (additive +
|
|
1733
|
-
// collision-safe). The listener is on this request's per-request
|
|
1734
|
-
// ServerResponse, so it's GC'd with the request — no cross-request leak.
|
|
1735
|
-
let clientGone = false;
|
|
1736
|
-
reply.raw.on('close', () => {
|
|
1737
|
-
if (!reply.raw.writableEnded)
|
|
1738
|
-
clientGone = true;
|
|
1739
|
-
});
|
|
1740
|
-
try {
|
|
1741
|
-
const { extractText, importFile } = await import('../../services/import-service.js');
|
|
1742
|
-
const { importAssetAsEntity } = await import('../../services/asset-import-service.js');
|
|
1743
|
-
const { resolveFormat, isIndexable, isVerbatimTextAsset, FALLBACK_ASSET_ENTRY } = await import('../../core/format-registry.js');
|
|
1744
|
-
const results = [];
|
|
1745
|
-
const CONCURRENCY = 3;
|
|
1746
|
-
async function processDirect(file) {
|
|
1747
|
-
// Reuse the access-checked graph captured in the outer scope. Do
|
|
1748
|
-
// NOT re-resolve via workspaceManager.getGraph here — the audit's
|
|
1749
|
-
// boundary is the outer graphForWrite call.
|
|
1750
|
-
const graph = repoGraph;
|
|
1751
|
-
const result = await importFile(graph, file.filename, file.content, {
|
|
1752
|
-
user: gitUser,
|
|
1753
|
-
// Caller-chosen destination subfolder; importFile combines it
|
|
1754
|
-
// with each file's own relative directory.
|
|
1755
|
-
folderPath: targetFolderPath || undefined,
|
|
1756
|
-
// Defer commits — we batch them into one transaction at the
|
|
1757
|
-
// end of the request so a 500-file import is one git commit
|
|
1758
|
-
// instead of 500. Per-file commits dominated wall time
|
|
1759
|
-
// (~50-200ms each) before this; batching collapses that to
|
|
1760
|
-
// one commit for the whole batch.
|
|
1761
|
-
skipCommit: true,
|
|
1762
|
-
});
|
|
1763
|
-
if (result.status === 'created' && result.path) {
|
|
1764
|
-
return {
|
|
1765
|
-
filename: file.filename,
|
|
1766
|
-
status: 'created',
|
|
1767
|
-
entities: [{ entityId: result.entityId, entityType: result.entityType }],
|
|
1768
|
-
entry: {
|
|
1769
|
-
operation: 'create',
|
|
1770
|
-
entityType: result.entityType,
|
|
1771
|
-
entityId: result.entityId,
|
|
1772
|
-
filePath: result.path,
|
|
1773
|
-
},
|
|
1774
|
-
sidecarPaths: result.sidecarPaths,
|
|
1775
|
-
};
|
|
1776
|
-
}
|
|
1777
|
-
return { filename: file.filename, status: result.status, error: result.error };
|
|
1778
|
-
}
|
|
1779
|
-
async function processAsset(file, entry) {
|
|
1780
|
-
// Reuse the outer access-checked graph; re-resolving would bypass
|
|
1781
|
-
// the architectural boundary even though the closure captures repo.
|
|
1782
|
-
const graph = repoGraph;
|
|
1783
|
-
try {
|
|
1784
|
-
// Wire encoding is a property of the FORMAT, mirrored by the
|
|
1785
|
-
// client's shouldReadAsText: verbatim-text assets (txt/csv)
|
|
1786
|
-
// travel as UTF-8 text — exactly as they did when they were
|
|
1787
|
-
// direct-write formats — every other asset as base64. Decoding
|
|
1788
|
-
// base64 here for a text payload would silently mojibake the
|
|
1789
|
-
// derived note/dataset body.
|
|
1790
|
-
const buffer = isVerbatimTextAsset(entry)
|
|
1791
|
-
? Buffer.from(file.content, 'utf8')
|
|
1792
|
-
: Buffer.from(file.content, 'base64');
|
|
1793
|
-
// Asset-as-embed (Phase B): store the binary folder-owned + create a
|
|
1794
|
-
// properly-TYPED entity (document / dataset×N / material) that embeds
|
|
1795
|
-
// it via asset_ref. No self-owning `asset` entity, no text sidecar
|
|
1796
|
-
// (the seeded body is indexed natively). xlsx fans out to N datasets,
|
|
1797
|
-
// so this returns 1..N entities — `commitEntries` carries them all.
|
|
1798
|
-
const result = await importAssetAsEntity({
|
|
1799
|
-
graph,
|
|
1800
|
-
filename: file.filename,
|
|
1801
|
-
buffer,
|
|
1802
|
-
folderPath: targetFolderPath || undefined,
|
|
1803
|
-
user: gitUser,
|
|
1804
|
-
uploaderUserId: reqUser(req)?.id != null ? String(reqUser(req).id) : null,
|
|
1805
|
-
// Defer commits — the dispatcher's batched commitPending call at
|
|
1806
|
-
// the bottom of the route covers every entry in one transaction.
|
|
1807
|
-
skipCommit: true,
|
|
1808
|
-
});
|
|
1809
|
-
return {
|
|
1810
|
-
filename: file.filename,
|
|
1811
|
-
status: 'created',
|
|
1812
|
-
entities: result.entities.map(e => ({ entityId: e.entityId, entityType: e.entityType })),
|
|
1813
|
-
commitEntries: result.entities.map(e => ({
|
|
1814
|
-
operation: 'create',
|
|
1815
|
-
entityType: e.entityType,
|
|
1816
|
-
entityId: e.entityId,
|
|
1817
|
-
filePath: e.filePath,
|
|
1818
|
-
})),
|
|
1819
|
-
};
|
|
1820
|
-
}
|
|
1821
|
-
catch (err) {
|
|
1822
|
-
return { filename: file.filename, status: 'error', error: err.message ?? 'Asset import failed' };
|
|
1823
|
-
}
|
|
1824
|
-
}
|
|
1825
|
-
async function processViaAgent(file) {
|
|
1826
|
-
try {
|
|
1827
|
-
const text = await extractText(file.filename, file.content);
|
|
1828
|
-
if (!text) {
|
|
1829
|
-
log.warn(`[import] No text extracted from ${file.filename}`);
|
|
1830
|
-
return { filename: file.filename, status: 'error', error: 'No text content extracted' };
|
|
1831
|
-
}
|
|
1832
|
-
log.info(`[import] Extracted ${text.length} chars from ${file.filename}`);
|
|
1833
|
-
const captureRes = await fastify.inject({
|
|
1834
|
-
method: 'POST',
|
|
1835
|
-
url: '/api/capture',
|
|
1836
|
-
headers: {
|
|
1837
|
-
'content-type': 'application/json',
|
|
1838
|
-
...(req.headers.authorization ? { authorization: req.headers.authorization } : {}),
|
|
1839
|
-
...(req.headers.cookie ? { cookie: req.headers.cookie } : {}),
|
|
1840
|
-
},
|
|
1841
|
-
payload: JSON.stringify({ text: `[Imported from ${file.filename}]\n\n${text}`, ...(captureRepo ? { repo: captureRepo } : {}) }),
|
|
1842
|
-
});
|
|
1843
|
-
const captureBody = JSON.parse(captureRes.body);
|
|
1844
|
-
if (captureRes.statusCode !== 200 || !captureBody.success) {
|
|
1845
|
-
log.warn(`[import] Capture failed for ${file.filename}: ${captureRes.statusCode} ${captureBody.error ?? JSON.stringify(captureBody).slice(0, 200)}`);
|
|
1846
|
-
return { filename: file.filename, status: 'error', error: captureBody.error ?? 'Agent processing failed' };
|
|
1847
|
-
}
|
|
1848
|
-
return { filename: file.filename, status: 'created', entities: captureBody.entities };
|
|
1849
|
-
}
|
|
1850
|
-
catch (err) {
|
|
1851
|
-
return { filename: file.filename, status: 'error', error: err.message ?? 'Processing failed' };
|
|
1852
|
-
}
|
|
1853
|
-
}
|
|
1854
|
-
// Route a single file off the format registry — the one source of
|
|
1855
|
-
// truth for every ingestion surface. P0 is a parity refactor: the
|
|
1856
|
-
// routing below reproduces today's behavior exactly. The only
|
|
1857
|
-
// intentional follow-ups are flagged inline (docx/svg transitional
|
|
1858
|
-
// paths; the FALLBACK reject that P0b flips to lenient).
|
|
1859
|
-
async function routeByFormat(file, entry) {
|
|
1860
|
-
// Transitional formats keep the current agent-extraction path until
|
|
1861
|
-
// their real handler lands. After P2 this is svg only — it routes
|
|
1862
|
-
// here until sanitize-html is wired (P3); routing it to uploadAsset
|
|
1863
|
-
// before then would store unsanitized svg (stored-XSS). docx flipped
|
|
1864
|
-
// to the asset path in P2 (uploads as asset, converts on demand).
|
|
1865
|
-
if (entry.transitional === 'agent-extract') {
|
|
1866
|
-
return processViaAgent(file);
|
|
1867
|
-
}
|
|
1868
|
-
switch (entry.handler) {
|
|
1869
|
-
case 'importDirect':
|
|
1870
|
-
return processDirect(file);
|
|
1871
|
-
case 'uploadAsset':
|
|
1872
|
-
// P0 parity: genuinely-unknown types are still rejected. The
|
|
1873
|
-
// lenient generic-asset fallback (accept unknowns as assets) is
|
|
1874
|
-
// P0b — a deliberate behavior change made by removing exactly
|
|
1875
|
-
// this guard.
|
|
1876
|
-
if (entry === FALLBACK_ASSET_ENTRY) {
|
|
1877
|
-
return { filename: file.filename, status: 'error', error: `Unsupported file: ${file.filename}` };
|
|
1878
|
-
}
|
|
1879
|
-
return processAsset(file, entry);
|
|
1880
|
-
default:
|
|
1881
|
-
// importBookmarks is not routed through the dispatcher until
|
|
1882
|
-
// P8 (content-sniff); bookmark exports still use the dedicated
|
|
1883
|
-
// POST /api/repos/:repo/import-bookmarks endpoint, so a bare
|
|
1884
|
-
// .html lands here and is rejected — matching today.
|
|
1885
|
-
return { filename: file.filename, status: 'error', error: `Unsupported file: ${file.filename}` };
|
|
1886
|
-
}
|
|
1887
|
-
}
|
|
1888
|
-
async function processFile(file) {
|
|
1889
|
-
if (!file.filename) {
|
|
1890
|
-
return { filename: file.filename, status: 'error', error: 'Missing filename' };
|
|
1891
|
-
}
|
|
1892
|
-
// Resolve the format once and thread it through routing.
|
|
1893
|
-
// `searchable` (OV3 server contract) reflects the registry's
|
|
1894
|
-
// DECLARED indexability for the file — the client (P5) renders the
|
|
1895
|
-
// honest "stored as-is / not searchable" callouts from it.
|
|
1896
|
-
// KNOWN LIMITATION: this is declared, not actual. A scanned/encrypted
|
|
1897
|
-
// PDF declares searchable:true (indexAs:'sidecar') but yields no text,
|
|
1898
|
-
// so it won't be flagged "stored as-is" even though its content is
|
|
1899
|
-
// effectively filename-only. Closing that gap needs the extractor to
|
|
1900
|
-
// report actual text-yield back into this flag (P3-indexing scope);
|
|
1901
|
-
// the negative claim stays accurate, the positive one is best-effort.
|
|
1902
|
-
const entry = resolveFormat(file.filename);
|
|
1903
|
-
const searchable = isIndexable(entry);
|
|
1904
|
-
const result = await routeByFormat(file, entry);
|
|
1905
|
-
return { ...result, searchable };
|
|
1906
|
-
}
|
|
1907
|
-
// Process files in cancellable batches of CONCURRENCY. The runner
|
|
1908
|
-
// checks the abort flag between batches: if the client disconnected
|
|
1909
|
-
// (web AbortController), it stops dispatching the remainder, lets the
|
|
1910
|
-
// in-flight batch settle, and returns the completed prefix — which the
|
|
1911
|
-
// commitPending step below commits in one transaction (no uncommitted
|
|
1912
|
-
// files left behind). Cancel is partial + honest: import is additive
|
|
1913
|
-
// and collision-safe.
|
|
1914
|
-
const { runImportBatches } = await import('./import-batch.js');
|
|
1915
|
-
const { results: batched, cancelled } = await runImportBatches(body.files, processFile, { concurrency: CONCURRENCY, isAborted: () => clientGone });
|
|
1916
|
-
results.push(...batched);
|
|
1917
|
-
// Direct-write path deferred git commits — flush them all in one
|
|
1918
|
-
// transaction now. Collapses N per-file commits into one batch
|
|
1919
|
-
// commit (~50ms vs N×50ms). Agent-path files committed inline via
|
|
1920
|
-
// /api/capture so they're not in this set.
|
|
1921
|
-
const pendingEntries = results.flatMap(r => {
|
|
1922
|
-
const pf = r;
|
|
1923
|
-
// Asset-import files carry commitEntries (1..N); direct-write files
|
|
1924
|
-
// carry a single entry. Prefer the plural, fall back to the singular.
|
|
1925
|
-
if (pf.commitEntries && pf.commitEntries.length > 0)
|
|
1926
|
-
return pf.commitEntries;
|
|
1927
|
-
return pf.entry ? [pf.entry] : [];
|
|
1928
|
-
});
|
|
1929
|
-
// Collect every sidecar path produced by the import — these stage in
|
|
1930
|
-
// the same commit as the entity files, so the folder display name
|
|
1931
|
-
// and the entries it labels land atomically. De-dup because two
|
|
1932
|
-
// imported files inside the same Schema_OS/widgets/ would otherwise
|
|
1933
|
-
// produce duplicate sidecar entries.
|
|
1934
|
-
const sidecarPaths = Array.from(new Set(results.flatMap(r => r.sidecarPaths ?? [])));
|
|
1935
|
-
// commitPending failure used to be swallowed and the route returned
|
|
1936
|
-
// success=true even though the files were never committed. The audit
|
|
1937
|
-
// (2026-05) flagged this: a 500-file import that failed mid-commit
|
|
1938
|
-
// looked successful to the client but left N untracked files. Now
|
|
1939
|
-
// the failure propagates as 500 with a partial-failure shape so the
|
|
1940
|
-
// caller knows which entries are stranded.
|
|
1941
|
-
let commitError = null;
|
|
1942
|
-
if (pendingEntries.length > 0) {
|
|
1943
|
-
try {
|
|
1944
|
-
await repoGraph.commitPending(pendingEntries, {
|
|
1945
|
-
commitMessage: `Import ${pendingEntries.length} ${pendingEntries.length === 1 ? 'entry' : 'entries'} from web UI`,
|
|
1946
|
-
user: gitUser,
|
|
1947
|
-
extraPaths: sidecarPaths.length > 0 ? sidecarPaths : undefined,
|
|
1948
|
-
});
|
|
1949
|
-
}
|
|
1950
|
-
catch (err) {
|
|
1951
|
-
commitError = err instanceof Error ? err.message : String(err);
|
|
1952
|
-
log.warn(`[import] batch commit failed for ${repo}: ${commitError}`);
|
|
1953
|
-
}
|
|
1954
|
-
}
|
|
1955
|
-
const created = results.filter(r => r.status === 'created').length;
|
|
1956
|
-
const entityCount = results.reduce((sum, r) => sum + (r.entities?.length ?? 0), 0);
|
|
1957
|
-
// Single repo_sync broadcast for the whole import. The realtime
|
|
1958
|
-
// store's repo_sync handler refreshes the repo's entity list once
|
|
1959
|
-
// — vs. one entity_change per file, which would do N re-renders
|
|
1960
|
-
// on every connected client and freeze the UI for large imports.
|
|
1961
|
-
// Skip when commit failed: there's nothing for clients to refresh
|
|
1962
|
-
// beyond stranded uncommitted files, and the broadcast would lie
|
|
1963
|
-
// about persistence.
|
|
1964
|
-
if (created > 0 && !commitError) {
|
|
1965
|
-
wsHub?.broadcast({
|
|
1966
|
-
type: 'repo_sync', repo,
|
|
1967
|
-
actor: req.user?.displayName ?? 'API',
|
|
1968
|
-
source: 'api',
|
|
1969
|
-
summary: `imported ${created} ${created === 1 ? 'entry' : 'entries'}`,
|
|
1970
|
-
workspaceWide: workspaceManager.getRepoConfig(repo)?.workspace_wide === true,
|
|
1971
|
-
timestamp: new Date().toISOString(),
|
|
1972
|
-
});
|
|
1973
|
-
// Asset caption/extract enqueue is no longer wired here — it fires
|
|
1974
|
-
// from the single VectorService.onAssetPending chokepoint when the
|
|
1975
|
-
// asset's index state is seeded (covers every upload path uniformly).
|
|
1976
|
-
}
|
|
1977
|
-
const errors = results.filter(r => r.status === 'error').map(r => r.error);
|
|
1978
|
-
if (commitError) {
|
|
1979
|
-
// Files are on disk but uncommitted — partial failure. Surface as
|
|
1980
|
-
// 500 with details so the client doesn't show "imported N entries"
|
|
1981
|
-
// for entries that won't survive a server restart.
|
|
1982
|
-
return reply.status(500).send({
|
|
1983
|
-
success: false,
|
|
1984
|
-
error: `Import wrote ${pendingEntries.length} entries to disk but the git commit failed: ${commitError}`,
|
|
1985
|
-
commitError,
|
|
1986
|
-
uncommittedEntries: pendingEntries.length,
|
|
1987
|
-
imported: created,
|
|
1988
|
-
entityCount,
|
|
1989
|
-
skipped: errors.length,
|
|
1990
|
-
errors,
|
|
1991
|
-
results,
|
|
1992
|
-
});
|
|
1993
|
-
}
|
|
1994
|
-
return {
|
|
1995
|
-
success: true,
|
|
1996
|
-
imported: created,
|
|
1997
|
-
entityCount,
|
|
1998
|
-
skipped: errors.length,
|
|
1999
|
-
errors,
|
|
2000
|
-
results,
|
|
2001
|
-
// Cancel summary (CANCEL): when the client disconnected mid-import we
|
|
2002
|
-
// committed the completed prefix above and report N of M honestly.
|
|
2003
|
-
// The aborting client typically won't read this (its fetch rejected),
|
|
2004
|
-
// but it keeps the contract honest for any caller that does.
|
|
2005
|
-
cancelled,
|
|
2006
|
-
total: body.files.length,
|
|
2007
|
-
};
|
|
2008
|
-
}
|
|
2009
|
-
catch (err) {
|
|
2010
|
-
return reply.status(400).send({
|
|
2011
|
-
success: false,
|
|
2012
|
-
error: err instanceof Error ? err.message : 'Unknown error',
|
|
2013
|
-
});
|
|
2014
|
-
}
|
|
2015
|
-
});
|
|
2016
|
-
// POST /api/repos/:repo/import-bookmarks — import browser bookmark exports as reference entities
|
|
2017
|
-
fastify.post('/api/repos/:repo/import-bookmarks', async (req, reply) => {
|
|
2018
|
-
const { repo } = req.params;
|
|
2019
|
-
// Access enforced by graphForWrite — bookmark import creates entities.
|
|
2020
|
-
const graph = graphForWrite(req, reply, repo);
|
|
2021
|
-
if (!graph)
|
|
2022
|
-
return;
|
|
2023
|
-
const body = req.body;
|
|
2024
|
-
if (!body?.content || typeof body.content !== 'string') {
|
|
2025
|
-
return reply.status(400).send({ error: 'Expected { content: string }' });
|
|
2026
|
-
}
|
|
2027
|
-
try {
|
|
2028
|
-
const { isNetscapeBookmarkFile, importBookmarks } = await import('../../services/bookmark-import-service.js');
|
|
2029
|
-
if (!isNetscapeBookmarkFile(body.content)) {
|
|
2030
|
-
return reply.status(400).send({
|
|
2031
|
-
error: "This doesn't appear to be a browser bookmark export. Export bookmarks from your browser's bookmark manager to generate the correct format.",
|
|
2032
|
-
});
|
|
2033
|
-
}
|
|
2034
|
-
const user = reqGitUser(req);
|
|
2035
|
-
const result = await importBookmarks(graph, body.content, user);
|
|
2036
|
-
// Single git commit for all created entities
|
|
2037
|
-
if (result.created > 0) {
|
|
2038
|
-
const git = graph.getGitService();
|
|
2039
|
-
if (git) {
|
|
2040
|
-
const commitUser = user ?? { id: 'import', name: 'Import', email: 'import@studiograph' };
|
|
2041
|
-
git.commitAll(`Import ${result.created} bookmark${result.created !== 1 ? 's' : ''} as references`, commitUser);
|
|
2042
|
-
}
|
|
2043
|
-
}
|
|
2044
|
-
return {
|
|
2045
|
-
success: true,
|
|
2046
|
-
created: result.created,
|
|
2047
|
-
skipped: result.skipped,
|
|
2048
|
-
errors: result.errors,
|
|
2049
|
-
};
|
|
2050
|
-
}
|
|
2051
|
-
catch (err) {
|
|
2052
|
-
return reply.status(400).send({
|
|
2053
|
-
success: false,
|
|
2054
|
-
error: err instanceof Error ? err.message : 'Unknown error',
|
|
2055
|
-
});
|
|
2056
|
-
}
|
|
2057
|
-
});
|
|
2058
1691
|
// POST /api/repos/:repo/assets — generic asset upload.
|
|
2059
1692
|
//
|
|
2060
1693
|
// Multipart `file` field. Asset-as-embed (B2): stores a FREE-STANDING,
|