@semiont/backend 0.5.5 → 0.5.7

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/index.js CHANGED
@@ -3,7 +3,7 @@ import winston from 'winston';
3
3
  import { recordSubscriberConnect, recordSubscriberDisconnect, withTraceparent, withSpan, recordBusEmit, SpanKind, injectTraceparent, getLogTraceContext } from '@semiont/observability';
4
4
  import { z } from 'zod';
5
5
  import jwt from 'jsonwebtoken';
6
- import { email, userId, googleCredential, agentToDid, EventBus, accessToken, userToDid, resourceId, CHANNEL_SCHEMAS, busLog, getPrimaryMediaType, decodeRepresentation } from '@semiont/core';
6
+ import { email, userId, googleCredential, agentToDid, EventBus, accessToken, userToDid, resourceId, CHANNEL_SCHEMAS, busLog, baseMediaType, isSupportedMediaType, getPrimaryMediaType } from '@semiont/core';
7
7
  import { cors } from 'hono/cors';
8
8
  import { serve } from '@hono/node-server';
9
9
  import { Hono } from 'hono';
@@ -10823,26 +10823,6 @@ var openapi_default = {
10823
10823
  $ref: "#/components/schemas/GatheredContext",
10824
10824
  description: "Gathered context for this annotation"
10825
10825
  },
10826
- sourceContext: {
10827
- type: "object",
10828
- description: "DEPRECATED: Use 'context' instead. Legacy source context format.",
10829
- properties: {
10830
- before: {
10831
- type: "string"
10832
- },
10833
- selected: {
10834
- type: "string"
10835
- },
10836
- after: {
10837
- type: "string"
10838
- }
10839
- },
10840
- required: [
10841
- "before",
10842
- "selected",
10843
- "after"
10844
- ]
10845
- },
10846
10826
  targetContext: {
10847
10827
  type: "object",
10848
10828
  properties: {
@@ -10856,29 +10836,6 @@ var openapi_default = {
10856
10836
  required: [
10857
10837
  "content"
10858
10838
  ]
10859
- },
10860
- suggestedResolution: {
10861
- type: "object",
10862
- properties: {
10863
- resourceId: {
10864
- type: "string"
10865
- },
10866
- resourceName: {
10867
- type: "string"
10868
- },
10869
- confidence: {
10870
- type: "number"
10871
- },
10872
- reasoning: {
10873
- type: "string"
10874
- }
10875
- },
10876
- required: [
10877
- "resourceId",
10878
- "resourceName",
10879
- "confidence",
10880
- "reasoning"
10881
- ]
10882
10839
  }
10883
10840
  },
10884
10841
  required: [
@@ -11387,9 +11344,78 @@ var openapi_default = {
11387
11344
  },
11388
11345
  ContentFormat: {
11389
11346
  type: "string",
11390
- description: "Content format as MIME type, optionally with charset parameter. Values include: text/plain, text/plain; charset=utf-8, text/plain; charset=iso-8859-1, text/markdown, text/markdown; charset=windows-1252, image/png, image/jpeg, application/pdf",
11347
+ description: "Content format as a MIME type, optionally with parameters. The base type (everything before the first ';') MUST be a SupportedMediaType; parameters such as charset are preserved as metadata. Semantic validation happens in code at the create/yield boundary \u2014 there is deliberately no pattern here, the vocabulary lives in SupportedMediaType. Examples: text/plain, text/plain; charset=iso-8859-1, text/markdown; charset=windows-1252, image/png, application/pdf",
11391
11348
  example: "text/plain; charset=utf-8"
11392
11349
  },
11350
+ SupportedMediaType: {
11351
+ type: "string",
11352
+ description: "Base MIME types (no parameters) admitted by Semiont. Membership is the create/yield gate \u2014 every member is storable, nameable, and uploadable. What more the system can do with a type (render, annotate, extract text, author) is curated per type in @semiont/core's media-type registry, which is keyed by this enum.",
11353
+ enum: [
11354
+ "text/plain",
11355
+ "text/markdown",
11356
+ "text/html",
11357
+ "text/css",
11358
+ "text/csv",
11359
+ "text/xml",
11360
+ "application/json",
11361
+ "application/xml",
11362
+ "application/yaml",
11363
+ "application/x-yaml",
11364
+ "application/pdf",
11365
+ "application/msword",
11366
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
11367
+ "application/vnd.ms-excel",
11368
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
11369
+ "application/vnd.ms-powerpoint",
11370
+ "application/vnd.openxmlformats-officedocument.presentationml.presentation",
11371
+ "application/zip",
11372
+ "application/gzip",
11373
+ "application/x-tar",
11374
+ "application/x-7z-compressed",
11375
+ "application/octet-stream",
11376
+ "application/wasm",
11377
+ "image/png",
11378
+ "image/jpeg",
11379
+ "image/gif",
11380
+ "image/webp",
11381
+ "image/svg+xml",
11382
+ "image/bmp",
11383
+ "image/tiff",
11384
+ "image/x-icon",
11385
+ "video/mp4",
11386
+ "video/mpeg",
11387
+ "video/webm",
11388
+ "video/ogg",
11389
+ "video/quicktime",
11390
+ "video/x-msvideo",
11391
+ "audio/mpeg",
11392
+ "audio/wav",
11393
+ "audio/ogg",
11394
+ "audio/webm",
11395
+ "audio/aac",
11396
+ "audio/flac",
11397
+ "text/javascript",
11398
+ "application/javascript",
11399
+ "text/x-typescript",
11400
+ "application/typescript",
11401
+ "text/x-python",
11402
+ "text/x-java",
11403
+ "text/x-c",
11404
+ "text/x-c++",
11405
+ "text/x-csharp",
11406
+ "text/x-go",
11407
+ "text/x-rust",
11408
+ "text/x-ruby",
11409
+ "text/x-php",
11410
+ "text/x-swift",
11411
+ "text/x-kotlin",
11412
+ "text/x-shell",
11413
+ "font/woff",
11414
+ "font/woff2",
11415
+ "font/ttf",
11416
+ "font/otf"
11417
+ ]
11418
+ },
11393
11419
  ContextualSummaryResponse: {
11394
11420
  type: "object",
11395
11421
  properties: {
@@ -11692,18 +11718,13 @@ var openapi_default = {
11692
11718
  type: "integer",
11693
11719
  description: "Monotonic position in the event log (ordering authority)"
11694
11720
  },
11695
- streamPosition: {
11696
- type: "integer",
11697
- description: "Byte offset in the JSONL file"
11698
- },
11699
11721
  correlationId: {
11700
11722
  type: "string",
11701
11723
  description: "Optional correlation id propagated from a command. Lets clients match command-result events back to the POST that initiated them. Set by EventStore.appendEvent's options when a route handler passes one through."
11702
11724
  }
11703
11725
  },
11704
11726
  required: [
11705
- "sequenceNumber",
11706
- "streamPosition"
11727
+ "sequenceNumber"
11707
11728
  ]
11708
11729
  },
11709
11730
  SelectionData: {
@@ -17187,9 +17208,11 @@ exchangeRouter.post("/api/moderate/exchange/import", async (c) => {
17187
17208
  send({
17188
17209
  phase: "complete",
17189
17210
  result: {
17190
- resourcesCreated: result.resourcesCreated,
17191
- annotationsCreated: result.annotationsCreated,
17192
- entityTypesAdded: result.entityTypesAdded
17211
+ stats: {
17212
+ resourcesCreated: result.resourcesCreated,
17213
+ annotationsCreated: result.annotationsCreated,
17214
+ entityTypesAdded: result.entityTypesAdded
17215
+ }
17193
17216
  }
17194
17217
  });
17195
17218
  } catch (err) {
@@ -17214,93 +17237,6 @@ function createResourceRouter() {
17214
17237
  router.use("/resources/*", authMiddleware);
17215
17238
  return router;
17216
17239
  }
17217
-
17218
- // ../../packages/content/dist/index.js
17219
- var MIME_TO_EXTENSION = {
17220
- // Text formats
17221
- "text/plain": ".txt",
17222
- "text/markdown": ".md",
17223
- "text/html": ".html",
17224
- "text/css": ".css",
17225
- "text/csv": ".csv",
17226
- "text/xml": ".xml",
17227
- // Application formats - structured data
17228
- "application/json": ".json",
17229
- "application/xml": ".xml",
17230
- "application/yaml": ".yaml",
17231
- "application/x-yaml": ".yaml",
17232
- // Application formats - documents
17233
- "application/pdf": ".pdf",
17234
- "application/msword": ".doc",
17235
- "application/vnd.openxmlformats-officedocument.wordprocessingml.document": ".docx",
17236
- "application/vnd.ms-excel": ".xls",
17237
- "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": ".xlsx",
17238
- "application/vnd.ms-powerpoint": ".ppt",
17239
- "application/vnd.openxmlformats-officedocument.presentationml.presentation": ".pptx",
17240
- // Application formats - archives
17241
- "application/zip": ".zip",
17242
- "application/gzip": ".gz",
17243
- "application/x-tar": ".tar",
17244
- "application/x-7z-compressed": ".7z",
17245
- // Application formats - executables/binaries
17246
- "application/octet-stream": ".bin",
17247
- "application/wasm": ".wasm",
17248
- // Image formats
17249
- "image/png": ".png",
17250
- "image/jpeg": ".jpg",
17251
- "image/gif": ".gif",
17252
- "image/webp": ".webp",
17253
- "image/svg+xml": ".svg",
17254
- "image/bmp": ".bmp",
17255
- "image/tiff": ".tiff",
17256
- "image/x-icon": ".ico",
17257
- // Audio formats
17258
- "audio/mpeg": ".mp3",
17259
- "audio/wav": ".wav",
17260
- "audio/ogg": ".ogg",
17261
- "audio/webm": ".webm",
17262
- "audio/aac": ".aac",
17263
- "audio/flac": ".flac",
17264
- // Video formats
17265
- "video/mp4": ".mp4",
17266
- "video/mpeg": ".mpeg",
17267
- "video/webm": ".webm",
17268
- "video/ogg": ".ogv",
17269
- "video/quicktime": ".mov",
17270
- "video/x-msvideo": ".avi",
17271
- // Programming languages
17272
- "text/javascript": ".js",
17273
- "application/javascript": ".js",
17274
- "text/x-typescript": ".ts",
17275
- "application/typescript": ".ts",
17276
- "text/x-python": ".py",
17277
- "text/x-java": ".java",
17278
- "text/x-c": ".c",
17279
- "text/x-c++": ".cpp",
17280
- "text/x-csharp": ".cs",
17281
- "text/x-go": ".go",
17282
- "text/x-rust": ".rs",
17283
- "text/x-ruby": ".rb",
17284
- "text/x-php": ".php",
17285
- "text/x-swift": ".swift",
17286
- "text/x-kotlin": ".kt",
17287
- "text/x-shell": ".sh",
17288
- // Font formats
17289
- "font/woff": ".woff",
17290
- "font/woff2": ".woff2",
17291
- "font/ttf": ".ttf",
17292
- "font/otf": ".otf"
17293
- };
17294
- function getExtensionForMimeType(mediaType) {
17295
- const normalized = mediaType.toLowerCase().split(";")[0].trim();
17296
- const extension = MIME_TO_EXTENSION[normalized];
17297
- return extension || ".dat";
17298
- }
17299
- function deriveStorageUri(name, format) {
17300
- const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
17301
- const ext = getExtensionForMimeType(format);
17302
- return `file://${slug}${ext}`;
17303
- }
17304
17240
  function registerCreateResource(router) {
17305
17241
  router.post("/resources", async (c) => {
17306
17242
  const user = c.get("user");
@@ -17320,9 +17256,15 @@ function registerCreateResource(router) {
17320
17256
  const generationPrompt = formData.get("generationPrompt");
17321
17257
  const generatorStr = formData.get("generator");
17322
17258
  const isDraftStr = formData.get("isDraft");
17323
- if (!name || !file || !formatRaw) {
17259
+ if (!name || !file || !formatRaw || !storageUri) {
17260
+ throw new HTTPException(400, {
17261
+ message: "Missing required fields: name, file, format, storageUri"
17262
+ });
17263
+ }
17264
+ const formatBase = baseMediaType(formatRaw);
17265
+ if (!isSupportedMediaType(formatBase)) {
17324
17266
  throw new HTTPException(400, {
17325
- message: "Missing required fields: name, file, format"
17267
+ message: `Unsupported media type: ${formatBase}`
17326
17268
  });
17327
17269
  }
17328
17270
  const format = formatRaw;
@@ -17349,13 +17291,12 @@ function registerCreateResource(router) {
17349
17291
  const arrayBuffer = await file.arrayBuffer();
17350
17292
  const contentBuffer = Buffer.from(arrayBuffer);
17351
17293
  const { knowledgeSystem: { kb } } = c.get("makeMeaning");
17352
- const resolvedUri = storageUri || deriveStorageUri(name, format);
17353
- const stored = await kb.content.store(contentBuffer, resolvedUri);
17294
+ const stored = await kb.content.store(contentBuffer, storageUri);
17354
17295
  const eventBus2 = c.get("eventBus");
17355
17296
  return ResourceOperations.createResource(
17356
17297
  {
17357
17298
  name,
17358
- storageUri: resolvedUri,
17299
+ storageUri,
17359
17300
  contentChecksum: stored.checksum,
17360
17301
  byteSize: stored.byteSize,
17361
17302
  format,
@@ -17410,98 +17351,46 @@ async function eventBusRequest(eventBus2, requestEvent, payload, successEvent, f
17410
17351
  // src/routes/resources/routes/get-uri.ts
17411
17352
  init_logger();
17412
17353
  var getRouteLogger3 = () => getLogger().child({ component: "get-resource-uri" });
17413
- function registerGetResourceUri(router) {
17414
- router.get("/api/resources/:id", async (c) => {
17415
- const { id } = c.req.param();
17416
- let acceptHeader = c.req.header("Accept") || "*/*";
17417
- acceptHeader = acceptHeader.split(",").map((t) => t.trim()).filter((t) => t !== "application/ld+json" && t !== "application/json").join(", ") || "*/*";
17418
- busLog("GET", "content", { resourceId: id, accept: acceptHeader });
17419
- const traceparent = c.req.header("traceparent");
17420
- const tracestate = c.req.header("tracestate");
17421
- const carrier = traceparent ? tracestate ? { traceparent, tracestate } : { traceparent } : void 0;
17422
- return withTraceparent(
17423
- carrier,
17424
- () => withSpan(
17425
- "content.get.server",
17426
- async () => {
17427
- const { knowledgeSystem: { kb } } = c.get("makeMeaning");
17428
- let resource;
17429
- try {
17430
- resource = await ResourceContext.getResourceMetadata(resourceId(id), kb);
17431
- } catch {
17432
- throw new HTTPException(500, { message: "Failed to retrieve resource" });
17433
- }
17434
- if (!resource) throw new HTTPException(404, { message: "Resource not found" });
17435
- if (!resource.storageUri) throw new HTTPException(404, { message: "Resource representation not found" });
17436
- const content = await kb.content.retrieve(resource.storageUri);
17437
- if (!content) throw new HTTPException(404, { message: "Resource representation not found" });
17438
- const mediaType = getPrimaryMediaType(resource);
17439
- c.header("Cache-Control", "public, max-age=31536000, immutable");
17440
- if (mediaType) c.header("Content-Type", mediaType);
17441
- if (mediaType?.startsWith("image/") || mediaType === "application/pdf") {
17442
- return c.newResponse(new Uint8Array(content), 200, { "Content-Type": mediaType });
17443
- }
17444
- return c.text(decodeRepresentation(content, mediaType || "text/plain"));
17445
- },
17446
- {
17447
- kind: SpanKind.SERVER,
17448
- attrs: { "resource.id": id, "http.accept": acceptHeader }
17449
- }
17450
- )
17451
- );
17354
+ function traceCarrier(c) {
17355
+ const traceparent = c.req.header("traceparent");
17356
+ const tracestate = c.req.header("tracestate");
17357
+ return traceparent ? tracestate ? { traceparent, tracestate } : { traceparent } : void 0;
17358
+ }
17359
+ async function loadRepresentation(id, kb) {
17360
+ let resource;
17361
+ try {
17362
+ resource = await ResourceContext.getResourceMetadata(resourceId(id), kb);
17363
+ } catch (error) {
17364
+ getRouteLogger3().error("Failed to get resource metadata", {
17365
+ resourceId: id,
17366
+ error: error instanceof Error ? error.message : String(error),
17367
+ stack: error instanceof Error ? error.stack : void 0
17368
+ });
17369
+ throw new HTTPException(500, { message: "Failed to retrieve resource" });
17370
+ }
17371
+ if (!resource) {
17372
+ throw new HTTPException(404, { message: "Resource not found" });
17373
+ }
17374
+ if (!resource.storageUri) {
17375
+ throw new HTTPException(404, { message: "Resource representation not found" });
17376
+ }
17377
+ const content = await kb.content.retrieve(resource.storageUri);
17378
+ if (!content) {
17379
+ throw new HTTPException(404, { message: "Resource representation not found" });
17380
+ }
17381
+ return { content, mediaType: getPrimaryMediaType(resource) };
17382
+ }
17383
+ function pipeRepresentation(c, content, mediaType) {
17384
+ return c.newResponse(new Uint8Array(content), 200, {
17385
+ "Content-Type": mediaType || "application/octet-stream"
17452
17386
  });
17453
- router.get("/resources/:id", async (c) => {
17387
+ }
17388
+ function describedByLink(id) {
17389
+ return `</resources/${id}/jsonld>; rel="describedby"; type="application/ld+json"`;
17390
+ }
17391
+ function registerGetResourceUri(router) {
17392
+ router.get("/resources/:id/jsonld", async (c) => {
17454
17393
  const { id } = c.req.param();
17455
- const acceptHeader = c.req.header("Accept") || "application/ld+json";
17456
- if (acceptHeader.includes("text/") || acceptHeader.includes("image/") || acceptHeader.includes("application/pdf")) {
17457
- busLog("GET", "content", { resourceId: id, accept: acceptHeader });
17458
- const traceparent = c.req.header("traceparent");
17459
- const tracestate = c.req.header("tracestate");
17460
- const carrier = traceparent ? tracestate ? { traceparent, tracestate } : { traceparent } : void 0;
17461
- return withTraceparent(
17462
- carrier,
17463
- () => withSpan(
17464
- "content.get.server",
17465
- async () => {
17466
- const { knowledgeSystem: { kb } } = c.get("makeMeaning");
17467
- let resource;
17468
- try {
17469
- resource = await ResourceContext.getResourceMetadata(resourceId(id), kb);
17470
- } catch (error) {
17471
- getRouteLogger3().error("Failed to get resource metadata", {
17472
- resourceId: id,
17473
- error: error instanceof Error ? error.message : String(error),
17474
- stack: error instanceof Error ? error.stack : void 0
17475
- });
17476
- throw new HTTPException(500, { message: "Failed to retrieve resource" });
17477
- }
17478
- if (!resource) {
17479
- throw new HTTPException(404, { message: "Resource not found" });
17480
- }
17481
- if (!resource.storageUri) {
17482
- throw new HTTPException(404, { message: "Resource representation not found" });
17483
- }
17484
- const content = await kb.content.retrieve(resource.storageUri);
17485
- if (!content) {
17486
- throw new HTTPException(404, { message: "Resource representation not found" });
17487
- }
17488
- const mediaType = getPrimaryMediaType(resource);
17489
- if (mediaType) {
17490
- c.header("Content-Type", mediaType);
17491
- }
17492
- if (mediaType?.startsWith("image/") || mediaType === "application/pdf") {
17493
- return c.newResponse(new Uint8Array(content), 200, { "Content-Type": mediaType });
17494
- } else {
17495
- return c.text(decodeRepresentation(content, mediaType || "text/plain"));
17496
- }
17497
- },
17498
- {
17499
- kind: SpanKind.SERVER,
17500
- attrs: { "resource.id": id, "http.accept": acceptHeader }
17501
- }
17502
- )
17503
- );
17504
- }
17505
17394
  const eventBus2 = c.get("eventBus");
17506
17395
  const correlationId = crypto.randomUUID();
17507
17396
  try {
@@ -17512,8 +17401,11 @@ function registerGetResourceUri(router) {
17512
17401
  "browse:resource-result",
17513
17402
  "browse:resource-failed"
17514
17403
  );
17515
- c.header("Content-Type", "application/ld+json; charset=utf-8");
17516
- return c.json(response);
17404
+ return c.json(response, 200, {
17405
+ "Content-Type": "application/ld+json; charset=utf-8",
17406
+ // Live data: annotations and inbound references change.
17407
+ "Cache-Control": "no-cache"
17408
+ });
17517
17409
  } catch (error) {
17518
17410
  if (error instanceof Error) {
17519
17411
  if (error.message === "Resource not found") {
@@ -17526,6 +17418,42 @@ function registerGetResourceUri(router) {
17526
17418
  throw error;
17527
17419
  }
17528
17420
  });
17421
+ router.get("/resources/:id", async (c) => {
17422
+ const { id } = c.req.param();
17423
+ busLog("GET", "content", { resourceId: id });
17424
+ return withTraceparent(
17425
+ traceCarrier(c),
17426
+ () => withSpan(
17427
+ "content.get.server",
17428
+ async () => {
17429
+ const { knowledgeSystem: { kb } } = c.get("makeMeaning");
17430
+ const { content, mediaType } = await loadRepresentation(id, kb);
17431
+ c.header("Cache-Control", "private, max-age=31536000, immutable");
17432
+ c.header("Link", describedByLink(id));
17433
+ return pipeRepresentation(c, content, mediaType);
17434
+ },
17435
+ { kind: SpanKind.SERVER, attrs: { "resource.id": id } }
17436
+ )
17437
+ );
17438
+ });
17439
+ router.get("/api/resources/:id", async (c) => {
17440
+ const { id } = c.req.param();
17441
+ busLog("GET", "content", { resourceId: id });
17442
+ return withTraceparent(
17443
+ traceCarrier(c),
17444
+ () => withSpan(
17445
+ "content.get.server",
17446
+ async () => {
17447
+ const { knowledgeSystem: { kb } } = c.get("makeMeaning");
17448
+ const { content, mediaType } = await loadRepresentation(id, kb);
17449
+ c.header("Cache-Control", "public, max-age=31536000, immutable");
17450
+ c.header("Link", describedByLink(id));
17451
+ return pipeRepresentation(c, content, mediaType);
17452
+ },
17453
+ { kind: SpanKind.SERVER, attrs: { "resource.id": id } }
17454
+ )
17455
+ );
17456
+ });
17529
17457
  }
17530
17458
 
17531
17459
  // src/routes/resources/index.ts