@mymehq/sdk 5.0.0 → 5.2.0

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.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { MergeStrategy, ConflictSnapshot, MergePolicy, ItemState, Tier, Item, CreateItemInput, PaginatedResult, ItemWithMetadata, Version, Edge, Metadata, SearchResult, CreateEdgeInput, EdgeTypeSchema, TypeSchema, CreateKeyInput, ApiKey, UpdateKeyInput, CreateWebhookInput, Webhook, UpdateWebhookInput, WebhookDelivery, ConnectionInstallResult, ConnectionUninstallResult, PreviewEventRequest, PreviewEventResult, TenantConfig, TenantQuota, Profile, UpdateProfileInput } from '@mymehq/shared';
1
+ import { MergeStrategy, ConflictSnapshot, MergePolicy, ItemState, Tier, CreateItemInput, Item, PaginatedResult, ItemWithMetadata, Version, Edge, Metadata, SearchResult, CreateEdgeInput, EdgeTypeSchema, TypeSchema, CreateKeyInput, ApiKey, UpdateKeyInput, CreateWebhookInput, Webhook, UpdateWebhookInput, WebhookDelivery, ConnectionInstallResult, ConnectionUninstallResult, PreviewEventRequest, PreviewEventResult, TenantConfig, TenantQuota, Profile, UpdateProfileInput } from '@mymehq/shared';
2
2
  export { ApiKey, ConflictSnapshot, CreateItemInput, CreateKeyInput, Item, ItemState, MergePolicy, MergeStrategy, Metadata, PaginatedResult, Profile, SearchResult, TypeSchema, UpdateKeyInput, UpdateProfileInput, Version } from '@mymehq/shared';
3
3
 
4
4
  /**
@@ -199,6 +199,57 @@ interface BulkItemInput {
199
199
  edges?: Record<string, string[]>;
200
200
  }
201
201
  type BulkMode = "upsert" | "create_only";
202
+ /**
203
+ * Input to `client.items.createWithAttachments(...)` (T-100). Wraps the
204
+ * existing blob-upload + `items.bulk` pattern: upload each attachment's
205
+ * blob, then issue one atomic bulk call containing the host item and the
206
+ * `core.file.*` items with inline `attached-to` edges from each
207
+ * attachment back to the host.
208
+ */
209
+ interface CreateWithAttachmentsInput {
210
+ /** The host item — the thing the attachments are attached *to* (note,
211
+ * message, etc.). May carry an explicit `id` for client-minted UUIDs
212
+ * and arbitrary `edges` of its own (passed through unchanged into the
213
+ * bulk payload). */
214
+ item: CreateItemInput & {
215
+ id?: string;
216
+ edges?: Record<string, string[]>;
217
+ };
218
+ /** Attachments in caller-passed order. The returned `attachments`
219
+ * array preserves the same order. Empty arrays are valid: the helper
220
+ * still issues one `items.bulk` call with just the host item. */
221
+ attachments: CreateWithAttachmentsAttachment[];
222
+ /** Edge type from each attachment back to the host. Defaults to
223
+ * `"attached-to"` (the canonical attachment edge — T-108). Override
224
+ * for app-specific semantics like `"cover-image"`. */
225
+ edgeType?: string;
226
+ }
227
+ interface CreateWithAttachmentsAttachment {
228
+ /** Type id, e.g. `"core.file.image"`. */
229
+ type: string;
230
+ /** Raw blob bytes. Uploaded via `POST /blobs`. */
231
+ blob: Uint8Array | ArrayBuffer;
232
+ /** Sent as the `Content-Type` of the blob upload. Stamped onto the
233
+ * attachment item's `mime_type` property automatically. */
234
+ mimeType: string;
235
+ /** Caller-supplied properties for this attachment item (e.g. `width`
236
+ * and `height` for `core.file.image`). The helper auto-fills
237
+ * `blob_ref` (= upload hash) and `mime_type` after upload; do not
238
+ * pre-populate them. */
239
+ properties?: Record<string, unknown>;
240
+ /** Optional additional edges on the attachment item. The helper
241
+ * appends its own `[edgeType]: [hostId]` entry; if you pass a value
242
+ * for the same `edgeType`, your ids are merged with the host id
243
+ * (helper-added edges are additive, never stripping). */
244
+ edges?: Record<string, string[]>;
245
+ /** Explicit attachment id (defaults to a client-minted UUIDv7). */
246
+ id?: string;
247
+ }
248
+ interface CreateWithAttachmentsResult {
249
+ host: Item;
250
+ /** Attachment items in the same order as `input.attachments`. */
251
+ attachments: Item[];
252
+ }
202
253
  interface BulkInput {
203
254
  items: BulkItemInput[];
204
255
  /** Default: `"upsert"`. */
@@ -404,6 +455,34 @@ declare class MymeClient {
404
455
  * `atomic: true` (default) rolls back the whole batch on any failure.
405
456
  */
406
457
  bulk: (input: BulkInput) => Promise<BulkResult>;
458
+ /**
459
+ * Create a host item plus a set of attached `core.file.*` items in
460
+ * one call (T-100). Wraps the existing `blobs.upload` + `items.bulk`
461
+ * primitives: each attachment's blob is uploaded concurrently, then a
462
+ * single atomic bulk call writes the host and the attachments
463
+ * together with inline `attached-to` edges from each attachment back
464
+ * to the host (matching T-108's spec direction).
465
+ *
466
+ * **Partial-failure contract.** Blob uploads run concurrently via
467
+ * `Promise.all`. On upload failure, throws with the failing
468
+ * attachment's index in the message. Successfully-uploaded blobs are
469
+ * not cleaned up; the server's CAS dedupe (`blob_ref` is the
470
+ * content hash) makes orphaned uploads cost-trivial — the same bytes
471
+ * uploaded later resolve to the same hash. Callers are expected to
472
+ * retry the whole call rather than reason about partial state.
473
+ *
474
+ * **Empty `attachments`** is valid and supported: the helper still
475
+ * issues one `items.bulk` call with just the host item, no blob
476
+ * uploads, no auto-edges. Lets callers use this method as a uniform
477
+ * entry point regardless of whether attachments are present.
478
+ *
479
+ * **Caller-provided edges co-exist with the auto-`attached-to`
480
+ * edges.** If the caller passes edges on the host item, they're
481
+ * passed through unchanged. If the caller passes edges on an
482
+ * attachment item with the same `edgeType` key, the helper's host id
483
+ * is appended to that array (additive, never strip-and-replace).
484
+ */
485
+ createWithAttachments: (input: CreateWithAttachmentsInput) => Promise<CreateWithAttachmentsResult>;
407
486
  /**
408
487
  * Apply one action to every item matching a filter. Six actions
409
488
  * discriminated on `action`. Purge is admin-only and requires
@@ -608,15 +687,17 @@ declare class MymeClient {
608
687
  */
609
688
  previewEvent: (input: PreviewEventRequest) => Promise<PreviewEventResult>;
610
689
  };
611
- /** Tenant-scoped configuration. Controls feed-tier retention per type
612
- * and the three optional schema-enforcement levers (TSC42 §5). All
613
- * endpoints are admin-only. */
690
+ /** Tenant-scoped configuration. Carries the three optional schema-
691
+ * enforcement levers (TSC42 §5: `strict_mode`, `source_allowlist`,
692
+ * `source_filter`) and the per-tenant cleanup-job overrides
693
+ * (`audit_retention_days`, `event_log_retention_hours`,
694
+ * `trash_retention_days`). All endpoints are admin-only. */
614
695
  readonly tenants: {
615
696
  /** Returns the current tenant's config. Empty object when nothing
616
697
  * is configured. */
617
698
  getConfig: () => Promise<TenantConfig>;
618
- /** Replaces the current tenant's config. Server validates that any
619
- * type IDs in retention overrides resolve in the registry. */
699
+ /** Replaces the current tenant's config (PUT semantics full
700
+ * replacement, not merge). */
620
701
  setConfig: (config: TenantConfig) => Promise<TenantConfig>;
621
702
  /** Per-tenant resource quotas (T-052). Empty / missing limits fall
622
703
  * back to the instance defaults from env. Quotas are platform-
@@ -813,4 +894,4 @@ interface VerifyWebhookSignatureInput {
813
894
  */
814
895
  declare function verifyWebhookSignature(input: VerifyWebhookSignatureInput): WebhookVerifyResult;
815
896
 
816
- export { type BulkActionErrorEntry, type BulkActionFilter, type BulkActionInput, type BulkActionResult, type BulkEdgeInput, type BulkEdgeInputItem, type BulkEdgeResult, type BulkEdgeResultEntry, type BulkInput, type BulkItemInput, type BulkMode, type BulkOutcome, type BulkResult, type BulkResultEntry, type ClientConfig, type ConflictAutoMergeListener, type ConflictAutoMergedEvent, type ConflictData, ConflictError, type ConflictResolver, type ConflictStrategy, ForbiddenError, type ItemWithExtensions, type ListFilters, type MetadataInput, MymeClient, MymeError, NotFoundError, type SearchFilters, UnauthorizedError, type UpdateOptions, ValidationError, type VerifyWebhookSignatureInput, type WebhookVerifyReason, type WebhookVerifyResult, defineType, verifyWebhookSignature };
897
+ export { type BulkActionErrorEntry, type BulkActionFilter, type BulkActionInput, type BulkActionResult, type BulkEdgeInput, type BulkEdgeInputItem, type BulkEdgeResult, type BulkEdgeResultEntry, type BulkInput, type BulkItemInput, type BulkMode, type BulkOutcome, type BulkResult, type BulkResultEntry, type ClientConfig, type ConflictAutoMergeListener, type ConflictAutoMergedEvent, type ConflictData, ConflictError, type ConflictResolver, type ConflictStrategy, type CreateWithAttachmentsAttachment, type CreateWithAttachmentsInput, type CreateWithAttachmentsResult, ForbiddenError, type ItemWithExtensions, type ListFilters, type MetadataInput, MymeClient, MymeError, NotFoundError, type SearchFilters, UnauthorizedError, type UpdateOptions, ValidationError, type VerifyWebhookSignatureInput, type WebhookVerifyReason, type WebhookVerifyResult, defineType, verifyWebhookSignature };
package/dist/index.js CHANGED
@@ -1,3 +1,6 @@
1
+ // src/client.ts
2
+ import { generateId } from "@mymehq/shared";
3
+
1
4
  // src/errors.ts
2
5
  var MymeError = class extends Error {
3
6
  code;
@@ -525,6 +528,118 @@ var MymeClient = class {
525
528
  body: input
526
529
  });
527
530
  },
531
+ /**
532
+ * Create a host item plus a set of attached `core.file.*` items in
533
+ * one call (T-100). Wraps the existing `blobs.upload` + `items.bulk`
534
+ * primitives: each attachment's blob is uploaded concurrently, then a
535
+ * single atomic bulk call writes the host and the attachments
536
+ * together with inline `attached-to` edges from each attachment back
537
+ * to the host (matching T-108's spec direction).
538
+ *
539
+ * **Partial-failure contract.** Blob uploads run concurrently via
540
+ * `Promise.all`. On upload failure, throws with the failing
541
+ * attachment's index in the message. Successfully-uploaded blobs are
542
+ * not cleaned up; the server's CAS dedupe (`blob_ref` is the
543
+ * content hash) makes orphaned uploads cost-trivial — the same bytes
544
+ * uploaded later resolve to the same hash. Callers are expected to
545
+ * retry the whole call rather than reason about partial state.
546
+ *
547
+ * **Empty `attachments`** is valid and supported: the helper still
548
+ * issues one `items.bulk` call with just the host item, no blob
549
+ * uploads, no auto-edges. Lets callers use this method as a uniform
550
+ * entry point regardless of whether attachments are present.
551
+ *
552
+ * **Caller-provided edges co-exist with the auto-`attached-to`
553
+ * edges.** If the caller passes edges on the host item, they're
554
+ * passed through unchanged. If the caller passes edges on an
555
+ * attachment item with the same `edgeType` key, the helper's host id
556
+ * is appended to that array (additive, never strip-and-replace).
557
+ */
558
+ createWithAttachments: async (input) => {
559
+ const edgeType = input.edgeType ?? "attached-to";
560
+ const hostId = input.item.id ?? generateId();
561
+ const uploadResults = await Promise.all(
562
+ input.attachments.map(async (att, idx) => {
563
+ try {
564
+ return await this.blobs.upload(att.blob, att.mimeType);
565
+ } catch (err) {
566
+ throw new MymeError(
567
+ "blob_upload_failed",
568
+ `createWithAttachments: blob upload failed for attachments[${String(idx)}] (type=${att.type}): ${err instanceof Error ? err.message : String(err)}`,
569
+ 502
570
+ );
571
+ }
572
+ })
573
+ );
574
+ const hostBulkItem = {
575
+ ...input.item,
576
+ id: hostId
577
+ };
578
+ const attachmentIds = [];
579
+ const attachmentBulkItems = input.attachments.map(
580
+ (att, idx) => {
581
+ const attachmentId = att.id ?? generateId();
582
+ attachmentIds.push(attachmentId);
583
+ const upload = uploadResults[idx];
584
+ if (!upload) {
585
+ throw new MymeError(
586
+ "internal_error",
587
+ `createWithAttachments: upload result missing for attachments[${String(idx)}]`,
588
+ 500
589
+ );
590
+ }
591
+ const callerEdges = att.edges ?? {};
592
+ const callerSameType = callerEdges[edgeType] ?? [];
593
+ const mergedEdges = {
594
+ ...callerEdges,
595
+ [edgeType]: [...callerSameType, hostId]
596
+ };
597
+ return {
598
+ id: attachmentId,
599
+ type: att.type,
600
+ properties: {
601
+ ...att.properties ?? {},
602
+ blob_ref: upload.hash,
603
+ mime_type: att.mimeType
604
+ },
605
+ edges: mergedEdges
606
+ };
607
+ }
608
+ );
609
+ const bulkResult = await this.items.bulk({
610
+ items: [hostBulkItem, ...attachmentBulkItems],
611
+ mode: "create_only",
612
+ atomic: true
613
+ });
614
+ const errored = bulkResult.results.find((r) => r.outcome === "errored");
615
+ if (errored) {
616
+ throw new MymeError(
617
+ errored.error?.code ?? "bulk_failed",
618
+ `createWithAttachments: bulk write failed at index ${String(errored.index)}: ${errored.error?.message ?? errored.reason ?? "unknown"}`,
619
+ 400
620
+ );
621
+ }
622
+ const skipped = bulkResult.results.find((r) => r.outcome === "skipped");
623
+ if (skipped) {
624
+ throw new MymeError(
625
+ "duplicate_id",
626
+ `createWithAttachments: bulk write skipped at index ${String(skipped.index)} (reason: ${skipped.reason ?? "unknown"}). The helper requires fresh ids \u2014 if you passed an explicit \`item.id\`, it must not already exist.`,
627
+ 409
628
+ );
629
+ }
630
+ const hydrated = await Promise.all(
631
+ [hostId, ...attachmentIds].map((id) => this.items.get(id))
632
+ );
633
+ const [host, ...attachments] = hydrated;
634
+ if (!host) {
635
+ throw new MymeError(
636
+ "internal_error",
637
+ "createWithAttachments: host hydration returned no item",
638
+ 500
639
+ );
640
+ }
641
+ return { host, attachments };
642
+ },
528
643
  /**
529
644
  * Apply one action to every item matching a filter. Six actions
530
645
  * discriminated on `action`. Purge is admin-only and requires
@@ -956,9 +1071,11 @@ var MymeClient = class {
956
1071
  }
957
1072
  };
958
1073
  // ---- Tenants (admin) ----
959
- /** Tenant-scoped configuration. Controls feed-tier retention per type
960
- * and the three optional schema-enforcement levers (TSC42 §5). All
961
- * endpoints are admin-only. */
1074
+ /** Tenant-scoped configuration. Carries the three optional schema-
1075
+ * enforcement levers (TSC42 §5: `strict_mode`, `source_allowlist`,
1076
+ * `source_filter`) and the per-tenant cleanup-job overrides
1077
+ * (`audit_retention_days`, `event_log_retention_hours`,
1078
+ * `trash_retention_days`). All endpoints are admin-only. */
962
1079
  tenants = {
963
1080
  /** Returns the current tenant's config. Empty object when nothing
964
1081
  * is configured. */
@@ -968,8 +1085,8 @@ var MymeClient = class {
968
1085
  "/tenants/current/config"
969
1086
  );
970
1087
  },
971
- /** Replaces the current tenant's config. Server validates that any
972
- * type IDs in retention overrides resolve in the registry. */
1088
+ /** Replaces the current tenant's config (PUT semantics full
1089
+ * replacement, not merge). */
973
1090
  setConfig: async (config) => {
974
1091
  return this.transport.request(
975
1092
  "PUT",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mymehq/sdk",
3
- "version": "5.0.0",
3
+ "version": "5.2.0",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org",
@@ -20,7 +20,7 @@
20
20
  "dist"
21
21
  ],
22
22
  "dependencies": {
23
- "@mymehq/shared": "5.0.0"
23
+ "@mymehq/shared": "5.1.0"
24
24
  },
25
25
  "devDependencies": {
26
26
  "@types/node": "^22.0.0",