@happyvertical/smrt-images 0.55.9 → 0.55.15

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/README.md CHANGED
@@ -2,6 +2,71 @@
2
2
 
3
3
  Image asset management with AI-powered categorization, editing, and metadata extraction for the s-m-r-t framework. Extends `Asset` from smrt-assets via cross-package STI -- stored in the same `assets` table with a distinct `_meta_type`.
4
4
 
5
+ ## Photographic character setup
6
+
7
+ The staged setup surface keeps uploaded pixels local during head isolation.
8
+ The optional SDK browser segmenter runs its pinned CPU model on the device,
9
+ selects hair and face-skin confidence masks, and exports a tightly cropped
10
+ transparent PNG while preserving the original RGB bytes. After the user reviews
11
+ the head and explicitly continues, the vision request receives only that crop
12
+ and returns mouth corners and chin. The s-m-r-t package derives the Canadian
13
+ horizontal split rig deterministically, while the animation package owns
14
+ contour validation and motion. This is semantic segmentation, not fine-hair
15
+ matting; future motion styles are separate.
16
+ The split has no artificial mouth-interior fill: opening it reveals the host's
17
+ transparent background through the actual photograph seam.
18
+
19
+ The expressive preview keeps the neutral rig unchanged. While an approved
20
+ speech envelope is playing, the host maps it through a bounded gain to the jaw
21
+ gap and small head/jaw tilts; silence, cancellation, replacement, completion,
22
+ and reduced-motion all restore the neutral pose. The Canadian split's vertical
23
+ travel is derived from the measured mouth-to-chin span and the canvas receives
24
+ matching transparent bottom padding, so a larger opening never clips the PNG.
25
+
26
+ The mouth split is derived locally from MediaPipe FaceLandmarker lip-corner and
27
+ chin landmarks on the isolated browser image. The pinned model is integrity
28
+ checked before CPU inference; one clear face is required, cancellation is
29
+ honored between loading and detection, and no image bytes are sent to a vision
30
+ provider for this stage.
31
+
32
+ `PhotoCutoutProfileStore` is the persistence seam. A consuming server supplies
33
+ its authenticated profile, tenant, `AssetRuntime`, profile resolver, and
34
+ authorization callback; the store saves PNG bytes as an owner-bound canonical
35
+ asset, links it to the profile as `character_cutout`, and stores a versioned
36
+ rig manifest that refers to the stable `character_cutout` asset key. The host
37
+ maps that key to returned durable bytes; UUIDs and expiring URLs are never
38
+ baked into the rig. It rejects another tenant, declined authorization, malformed
39
+ or unsupported selected manifests, invalid PNG dimensions, and missing saved
40
+ bytes. The store compensates only the just-created owned asset if an unlinked
41
+ profile write fails. Authorized readers can list stable saved-setup summaries
42
+ without loading PNG bytes; malformed linked manifests are omitted from that
43
+ gallery so valid offerings remain recoverable. Readers can then select only an
44
+ existing owner- and tenant-bound
45
+ `character_cutout` asset; an omitted asset id still loads the latest setup.
46
+ `PhotoCutoutSetup` accepts host-provided save/load callbacks and never
47
+ uses browser storage as an asset catalog. The chat workbench's local callback
48
+ bridge is deliberately loopback-only and fixed to an externally provisioned
49
+ temporary profile; it demonstrates canonical AssetRuntime/Profile storage but
50
+ is not a production authorization mechanism.
51
+
52
+ Persistence test design: focused unit coverage exercises authorized and denied
53
+ actor cases, tenant mismatch, PNG/frame rejection, malformed/versioned manifest
54
+ omission from galleries and rejection on selection, selected/latest gallery
55
+ loads, failed-link compensation, and an adapter that links before throwing.
56
+ The SQLite integration test creates the `AssetRuntime`, Profile, and
57
+ `ProfileAsset` link inside one transaction executor, then reloads after commit.
58
+ The service is adapter-neutral; PostgreSQL execution belongs to the consuming
59
+ application's database suite because this package owns no deployment database.
60
+
61
+ For mouth landmark analysis, configure a vision-capable model through the
62
+ server-only AI configuration (for example `SMRT_CHAT_DEV_MODEL`). Review the
63
+ result against the actual crop: valid geometry does not guarantee accurate
64
+ mouth placement. The UI never exposes provider credentials or model selection.
65
+
66
+ The helper release consumes the published SDK family through the workspace catalog.
67
+ s-m-r-t keeps the SDK catalog and overrides aligned atomically; the
68
+ `bash scripts/check-sdk-versions.sh` release gate enforces that boundary.
69
+
5
70
  ## Installation
6
71
 
7
72
  ```bash
@@ -117,3 +182,12 @@ A supported pixel/vision feature requires a separately scoped API.
117
182
 
118
183
  See [`AGENTS.md`](./AGENTS.md) for package architecture, invariants, validation,
119
184
  and contributor guidance.
185
+
186
+
187
+ ### Local photographic head isolation
188
+
189
+ `PhotoCutoutSetup` isolates hair and facial skin on the user's device through the optional SDK `@happyvertical/images/segmentation` entry point. Original RGB values are preserved in the transparent PNG; alpha derives from a semantic pixel mask rather than a model-authored polygon. The component accepts `segmentationAssets` for a same-origin prepared asset directory (default `/api/dev-image-segmentation`). Prepare pinned runtime/model assets with SDK images `prepare:segmentation`; the chat workbench has a dev-only allowlisted asset route. Hair/face policy and review/retry live in s-m-r-t, generic inference and PNG encoding in SDK. Photographs are not uploaded during isolation. Mouth analysis remains the separately requested next stage.
190
+
191
+ The setup flow presents four explicit stages: choose a photo, isolate the head, set the mouth from local face landmarks, then save. Each stage exposes one next action; redo and saved-character loading remain secondary actions. On wide screens the isolated head and animated preview share a compact row, and they stack on narrow screens. Loading, cancellation, and failures preserve the recoverable prior stage.
192
+
193
+ Saving adds the reviewed character to the authorized saved gallery. It does not select or activate a helper; the consuming surface handles that explicit follow-up. The 256×256 model can soften fine strands and low-confidence boundaries. Users review the head before continuing; loading errors and cancellation retain the selected photo. Local development uses the explicitly authorized SDK #1370 sibling link, to be replaced by released family pins at coordinated delivery.
package/dist/index.d.ts CHANGED
@@ -16,6 +16,9 @@ export { Image } from "./image.js";
16
16
  export { ImageCollection } from "./images.js";
17
17
  export { type ImageMediaBundleFileDescriptor, type ImageMediaBundleGpsTrackPoint, type ImageMediaBundleInspection, type ImageMediaBundleInspectionLike, type ImageMediaBundleNormalizedMetadata, type ImageMediaBundleSupportFileInspection, type MediaSupportFileVisibility, type PersistImageMediaBundleAssetInput, type PersistImageMediaBundleAssociationInput, type PersistImageMediaBundleInspectionOptions, type PersistImageMediaBundleInspectionResult, type PersistImageMediaBundleMetadataArtifactInput, persistImageMediaBundleInspection, type SmrtImageMediaBundlePersistenceAdapter, } from "./media-bundle-persistence.js";
18
18
  export { ImageMetadataExtractor } from "./metadata.js";
19
+ export { createPhotoCutoutCoordinateGuide } from './photo-cutout-coordinate-guide.js';
20
+ export { type LoadPhotoCutoutProfileSetupInput, type PersistedPhotoCutoutSetup, PHOTO_CUTOUT_PERSISTED_ASSET_REF, type PhotoCutoutProfileOwner, PhotoCutoutProfileStore, type PhotoCutoutProfileStoreAuthorization, type PhotoCutoutProfileStoreOptions, type SavedPhotoCutoutProfileSetup, type SavePhotoCutoutProfileSetupInput, } from './photo-cutout-profile-store.js';
21
+ export { assembleCanadianSplitRig, type FaceOutline, faceOutlinePrompt, type MouthLandmarks, mouthLandmarksPrompt, type PhotoCutoutSetupInput, parseFaceOutline, parseMouthLandmarks, parsePhotoCutoutSetup, photoCutoutSetupPrompt, } from "./photo-cutout-setup.js";
19
22
  export { smrtImagesGenerateAltTextPrompt } from "./prompts.js";
20
23
  export { ImageSearch } from "./search.js";
21
24
  export type { CategorizedImageLabel, CategoryResult, DeriveOptions, DetailedCategoryResult, ImageCategorizationDecisionDetail, ImageCategorizerOptions, ImageLabelVocabulary, ImageLabelVocabularyEntry, ImageLabelVocabularyResolver, ImageMetadataResult, ImageOptions, ImageSearchOptions, } from "./types.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,wBAAwB,CAAC;AAIhC,OAAO,cAAc,CAAC;AAGtB,OAAO,EACL,KAAK,uBAAuB,EAC5B,qBAAqB,EACrB,sBAAsB,EACtB,wBAAwB,EACxB,sBAAsB,EACtB,uBAAuB,EACvB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,uBAAuB,EAC5B,KAAK,kBAAkB,EACvB,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,mBAAmB,EACnB,uBAAuB,EACvB,sBAAsB,EACtB,YAAY,EACZ,yBAAyB,EACzB,cAAc,EACd,cAAc,GACf,MAAM,aAAU,CAAC;AAClB,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAe,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAW,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAU,CAAC;AAEvC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAS,CAAC;AAChC,OAAO,EAAE,eAAe,EAAE,MAAM,aAAU,CAAC;AAC3C,OAAO,EACL,KAAK,8BAA8B,EACnC,KAAK,6BAA6B,EAClC,KAAK,0BAA0B,EAC/B,KAAK,8BAA8B,EACnC,KAAK,kCAAkC,EACvC,KAAK,qCAAqC,EAC1C,KAAK,0BAA0B,EAC/B,KAAK,iCAAiC,EACtC,KAAK,uCAAuC,EAC5C,KAAK,wCAAwC,EAC7C,KAAK,uCAAuC,EAC5C,KAAK,4CAA4C,EACjD,iCAAiC,EACjC,KAAK,sCAAsC,GAC5C,MAAM,+BAA4B,CAAC;AACpC,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAY,CAAC;AACpD,OAAO,EAAE,+BAA+B,EAAE,MAAM,cAAW,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,MAAM,aAAU,CAAC;AAEvC,YAAY,EACV,qBAAqB,EACrB,cAAc,EACd,aAAa,EACb,sBAAsB,EACtB,iCAAiC,EACjC,uBAAuB,EACvB,oBAAoB,EACpB,yBAAyB,EACzB,4BAA4B,EAC5B,mBAAmB,EACnB,YAAY,EACZ,kBAAkB,GACnB,MAAM,YAAS,CAAC;AACjB,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,WAAW,EAChB,KAAK,mBAAmB,EACxB,eAAe,GAChB,MAAM,eAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,wBAAwB,CAAC;AAIhC,OAAO,cAAc,CAAC;AAEtB,OAAO,EACL,KAAK,uBAAuB,EAC5B,qBAAqB,EACrB,sBAAsB,EACtB,wBAAwB,EACxB,sBAAsB,EACtB,uBAAuB,EACvB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,uBAAuB,EAC5B,KAAK,kBAAkB,EACvB,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,mBAAmB,EACnB,uBAAuB,EACvB,sBAAsB,EACtB,YAAY,EACZ,yBAAyB,EACzB,cAAc,EACd,cAAc,GACf,MAAM,aAAU,CAAC;AAClB,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAe,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAW,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAU,CAAC;AAEvC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAS,CAAC;AAChC,OAAO,EAAE,eAAe,EAAE,MAAM,aAAU,CAAC;AAC3C,OAAO,EACL,KAAK,8BAA8B,EACnC,KAAK,6BAA6B,EAClC,KAAK,0BAA0B,EAC/B,KAAK,8BAA8B,EACnC,KAAK,kCAAkC,EACvC,KAAK,qCAAqC,EAC1C,KAAK,0BAA0B,EAC/B,KAAK,iCAAiC,EACtC,KAAK,uCAAuC,EAC5C,KAAK,wCAAwC,EAC7C,KAAK,uCAAuC,EAC5C,KAAK,4CAA4C,EACjD,iCAAiC,EACjC,KAAK,sCAAsC,GAC5C,MAAM,+BAA4B,CAAC;AACpC,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAY,CAAC;AAEpD,OAAO,EAAE,gCAAgC,EAAE,MAAM,oCAAoC,CAAC;AACtF,OAAO,EACL,KAAK,gCAAgC,EACrC,KAAK,yBAAyB,EAC9B,gCAAgC,EAChC,KAAK,uBAAuB,EAC5B,uBAAuB,EACvB,KAAK,oCAAoC,EACzC,KAAK,8BAA8B,EACnC,KAAK,4BAA4B,EACjC,KAAK,gCAAgC,GACtC,MAAM,iCAAiC,CAAC;AACzC,OAAO,EACL,wBAAwB,EACxB,KAAK,WAAW,EAChB,iBAAiB,EACjB,KAAK,cAAc,EACnB,oBAAoB,EACpB,KAAK,qBAAqB,EAC1B,gBAAgB,EAChB,mBAAmB,EACnB,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,yBAAsB,CAAC;AAC9B,OAAO,EAAE,+BAA+B,EAAE,MAAM,cAAW,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,MAAM,aAAU,CAAC;AAEvC,YAAY,EACV,qBAAqB,EACrB,cAAc,EACd,aAAa,EACb,sBAAsB,EACtB,iCAAiC,EACjC,uBAAuB,EACvB,oBAAoB,EACpB,yBAAyB,EACzB,4BAA4B,EAC5B,mBAAmB,EACnB,YAAY,EACZ,kBAAkB,GACnB,MAAM,YAAS,CAAC;AACjB,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,WAAW,EAChB,KAAK,mBAAmB,EACxB,eAAe,GAChB,MAAM,eAAY,CAAC"}
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { assembleCanadianSplitRig, faceOutlinePrompt, mouthLandmarksPrompt, parseFaceOutline, parseMouthLandmarks, parsePhotoCutoutSetup, photoCutoutSetupPrompt } from "./photo-cutout-setup.js";
1
2
  import { ObjectRegistry, SmrtCollection, executeDecision, field, smrt } from "@happyvertical/smrt-core";
2
3
  import { definePrompt, resolvePrompt } from "@happyvertical/smrt-prompts";
3
4
  import { randomUUID } from "node:crypto";
@@ -6,8 +7,10 @@ import { tmpdir } from "node:os";
6
7
  import { join } from "node:path";
7
8
  import { Asset, persistMediaBundleInspection as persistImageMediaBundleInspection } from "@happyvertical/smrt-assets";
8
9
  import { TenantIsolationError, TenantScoped, getCurrentTenant, isSuperAdminBypass } from "@happyvertical/smrt-tenancy";
10
+ import { validatePhotoCutoutRig } from "@happyvertical/animation";
11
+ import sharp from "sharp";
9
12
  //#region src/__smrt-register__.ts
10
- ObjectRegistry.registerPackageManifest(JSON.parse(`{"version":"1.0.0","timestamp":0,"packageName":"@happyvertical/smrt-images","packageVersion":"0.55.9","objects":{"@happyvertical/smrt-images:Image":{"name":"image","className":"Image","qualifiedName":"@happyvertical/smrt-images:Image","collection":"assets","filePath":"/home/runner/work/smrt/smrt/packages/images/src/image.ts","packageName":"@happyvertical/smrt-images","fields":{"created_at":{"type":"datetime","required":false},"updated_at":{"type":"datetime","required":false},"tenantId":{"type":"text","required":false,"_meta":{"generated":true,"source":"tenantScoped_decorator","sqlType":"UUID","__tenancy":{"isTenantIdField":true,"mode":"optional","field":"tenantId","autoFilter":true,"autoPopulate":true,"allowSuperAdminBypass":false}}},"name":{"type":"text","required":false},"sourceUri":{"type":"text","required":false},"mimeType":{"type":"text","required":false},"description":{"type":"text","required":false},"metadata":{"type":"text","required":false},"version":{"type":"integer","required":true,"default":1},"primaryVersionId":{"type":"foreignKey","required":false,"related":"Asset"},"typeSlug":{"type":"text","required":false},"statusSlug":{"type":"text","required":false},"ownerProfileId":{"type":"crossPackageRef","required":false,"related":"@happyvertical/smrt-profiles:Profile"},"sourceAssetId":{"type":"foreignKey","required":false,"related":"Asset"},"folderId":{"type":"foreignKey","required":false,"related":"Folder"},"sourceType":{"type":"text","required":false},"externalId":{"type":"text","required":false},"externalRefs":{"type":"text","required":false},"createdAt":{"type":"text","required":false},"updatedAt":{"type":"text","required":false},"width":{"type":"integer","required":false,"default":0},"height":{"type":"integer","required":false,"default":0},"alt":{"type":"text","required":false,"default":""}},"methods":{"getSlug":{"name":"getSlug","async":true,"parameters":[],"returnType":"Promise<string | null | undefined>","isStatic":false,"isPublic":true},"getMetadata":{"name":"getMetadata","async":false,"parameters":[],"returnType":"Record<string, unknown>","isStatic":false,"isPublic":true},"setMetadata":{"name":"setMetadata","async":false,"parameters":[{"name":"metadata","type":"Record<string, unknown>","optional":false}],"returnType":"void","isStatic":false,"isPublic":true},"mergeMetadata":{"name":"mergeMetadata","async":false,"parameters":[{"name":"metadata","type":"Record<string, unknown>","optional":false}],"returnType":"void","isStatic":false,"isPublic":true},"getExternalRefs":{"name":"getExternalRefs","async":false,"parameters":[],"returnType":"Record<string, AssetExternalReference>","isStatic":false,"isPublic":true},"getExternalRef":{"name":"getExternalRef","async":false,"parameters":[{"name":"provider","type":"string","optional":false}],"returnType":"AssetExternalReference | null","isStatic":false,"isPublic":true},"setExternalRef":{"name":"setExternalRef","async":false,"parameters":[{"name":"provider","type":"string","optional":false},{"name":"reference","type":"any","optional":false,"typeUnresolved":true}],"returnType":"void","isStatic":false,"isPublic":true},"getTags":{"name":"getTags","async":true,"parameters":[],"returnType":"Promise<Tag[]>","isStatic":false,"isPublic":true},"hasTag":{"name":"hasTag","async":true,"parameters":[{"name":"tagSlug","type":"string","optional":false},{"name":"context","type":"string","optional":true}],"returnType":"Promise<boolean>","isStatic":false,"isPublic":true},"getSource":{"name":"getSource","async":true,"parameters":[],"returnType":"Promise<Asset | null>","isStatic":false,"isPublic":true},"getDerivatives":{"name":"getDerivatives","async":true,"parameters":[],"returnType":"Promise<Asset[]>","isStatic":false,"isPublic":true},"getType":{"name":"getType","async":true,"parameters":[],"returnType":"Promise<AssetType | null>","isStatic":false,"isPublic":true},"getStatus":{"name":"getStatus","async":true,"parameters":[],"returnType":"Promise<AssetStatus | null>","isStatic":false,"isPublic":true},"getAssociations":{"name":"getAssociations","async":true,"parameters":[],"returnType":"Promise<AssetAssociation[]>","isStatic":false,"isPublic":true},"associateWith":{"name":"associateWith","async":true,"parameters":[{"name":"metaType","type":"string","optional":false},{"name":"metaId","type":"string","optional":false},{"name":"role","type":"any","optional":true,"default":"default"}],"returnType":"Promise<AssetAssociation>","isStatic":false,"isPublic":true},"getBySlug":{"name":"getBySlug","async":true,"parameters":[{"name":"_slug","type":"string","optional":false}],"returnType":"Promise<Asset | null>","isStatic":true,"isPublic":true},"isValidImageFormat":{"name":"isValidImageFormat","async":false,"parameters":[],"returnType":"boolean","isStatic":false,"isPublic":true},"isHighResolution":{"name":"isHighResolution","async":false,"parameters":[],"returnType":"boolean","isStatic":false,"isPublic":true},"generateAltText":{"name":"generateAltText","async":true,"parameters":[],"returnType":"Promise<string>","isStatic":false,"isPublic":true}},"decoratorConfig":{"api":{"include":["list","get","create","update","delete"]},"mcp":{"include":["list","get","create","update","generateAltText"]},"cli":{"skipApiCheck":true},"tenantScoped":{"mode":"optional"},"tableName":"assets","tableStrategy":"sti"},"extends":"Asset","exportName":"Image","collectionExportName":"ImageCollection","displayLabelField":"name","validationRules":[{"field":"version","rule":"required","fieldType":"integer"}],"schema":{"tableName":"assets","ddl":"CREATE TABLE IF NOT EXISTS \\"assets\\" (\\n \\"id\\" UUID PRIMARY KEY NOT NULL,\\n \\"slug\\" TEXT NOT NULL,\\n \\"context\\" TEXT NOT NULL DEFAULT '',\\n \\"_meta_type\\" TEXT NOT NULL,\\n \\"_meta_data\\" JSON,\\n \\"created_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"updated_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"tenant_id\\" UUID,\\n \\"name\\" TEXT,\\n \\"source_uri\\" TEXT,\\n \\"mime_type\\" TEXT,\\n \\"description\\" TEXT,\\n \\"metadata\\" TEXT,\\n \\"version\\" INTEGER DEFAULT 1,\\n \\"primary_version_id\\" UUID,\\n \\"type_slug\\" TEXT,\\n \\"status_slug\\" TEXT,\\n \\"owner_profile_id\\" UUID,\\n \\"source_asset_id\\" UUID,\\n \\"folder_id\\" UUID,\\n \\"source_type\\" TEXT,\\n \\"external_id\\" TEXT,\\n \\"external_refs\\" TEXT,\\n \\"width\\" INTEGER DEFAULT 0,\\n \\"height\\" INTEGER DEFAULT 0,\\n \\"alt\\" TEXT DEFAULT ''\\n);","columns":{"id":{"type":"UUID","primaryKey":true,"referenceKind":"id","notNull":true},"slug":{"type":"TEXT","notNull":true},"context":{"type":"TEXT","notNull":true,"default":""},"_meta_type":{"type":"TEXT","notNull":true},"_meta_data":{"type":"JSON","notNull":false},"created_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"updated_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"tenant_id":{"type":"UUID","referenceKind":"tenantId","notNull":false},"name":{"type":"TEXT","notNull":false},"source_uri":{"type":"TEXT","notNull":false},"mime_type":{"type":"TEXT","notNull":false},"description":{"type":"TEXT","notNull":false},"metadata":{"type":"TEXT","notNull":false},"version":{"type":"INTEGER","notNull":false,"default":1},"primary_version_id":{"type":"UUID","referenceKind":"foreignKey","notNull":false,"foreignKey":{"table":"assets","column":"id","onDelete":"NO ACTION","onUpdate":"CASCADE"}},"type_slug":{"type":"TEXT","notNull":false},"status_slug":{"type":"TEXT","notNull":false},"owner_profile_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":false},"source_asset_id":{"type":"UUID","referenceKind":"foreignKey","notNull":false,"foreignKey":{"table":"assets","column":"id","onDelete":"NO ACTION","onUpdate":"CASCADE"}},"folder_id":{"type":"UUID","referenceKind":"foreignKey","notNull":false},"source_type":{"type":"TEXT","notNull":false},"external_id":{"type":"TEXT","notNull":false},"external_refs":{"type":"TEXT","notNull":false},"width":{"type":"INTEGER","notNull":false,"default":0},"height":{"type":"INTEGER","notNull":false,"default":0},"alt":{"type":"TEXT","notNull":false,"default":""}},"indexes":[{"name":"assets_slug_context_meta_type_idx","columns":["tenant_id","slug","context","_meta_type"],"unique":true,"nullsNotDistinct":true},{"name":"assets_meta_type_idx","columns":["_meta_type"]},{"name":"assets_tenant_id_created_at_idx","columns":["tenant_id","created_at"]},{"name":"assets_primary_version_id_idx","columns":["primary_version_id"]},{"name":"assets_owner_profile_id_idx","columns":["owner_profile_id"]},{"name":"assets_source_asset_id_idx","columns":["source_asset_id"]},{"name":"assets_folder_id_idx","columns":["folder_id"]}],"version":"dd79a8b6"},"importPath":"@happyvertical/smrt-images"},"@happyvertical/smrt-images:ImageCollection":{"name":"imagecollection","className":"ImageCollection","qualifiedName":"@happyvertical/smrt-images:ImageCollection","collection":"assets","filePath":"/home/runner/work/smrt/smrt/packages/images/src/images.ts","packageName":"@happyvertical/smrt-images","fields":{},"methods":{"findByTenant":{"name":"findByTenant","async":true,"parameters":[{"name":"tenantId","type":"string","optional":false}],"returnType":"Promise<Image[]>","isStatic":false,"isPublic":true},"findGlobal":{"name":"findGlobal","async":true,"parameters":[],"returnType":"Promise<Image[]>","isStatic":false,"isPublic":true},"findWithGlobals":{"name":"findWithGlobals","async":true,"parameters":[{"name":"tenantId","type":"string","optional":false}],"returnType":"Promise<Image[]>","isStatic":false,"isPublic":true},"getByMinDimensions":{"name":"getByMinDimensions","async":true,"parameters":[{"name":"minWidth","type":"number","optional":false},{"name":"minHeight","type":"number","optional":false}],"returnType":"Promise<Image[]>","isStatic":false,"isPublic":true},"getByMaxDimensions":{"name":"getByMaxDimensions","async":true,"parameters":[{"name":"maxWidth","type":"number","optional":false},{"name":"maxHeight","type":"number","optional":false}],"returnType":"Promise<Image[]>","isStatic":false,"isPublic":true},"getLandscape":{"name":"getLandscape","async":true,"parameters":[],"returnType":"Promise<Image[]>","isStatic":false,"isPublic":true},"getPortrait":{"name":"getPortrait","async":true,"parameters":[],"returnType":"Promise<Image[]>","isStatic":false,"isPublic":true},"getSquare":{"name":"getSquare","async":true,"parameters":[],"returnType":"Promise<Image[]>","isStatic":false,"isPublic":true},"getMissingAltText":{"name":"getMissingAltText","async":true,"parameters":[],"returnType":"Promise<Image[]>","isStatic":false,"isPublic":true},"getHighResolution":{"name":"getHighResolution","async":true,"parameters":[],"returnType":"Promise<Image[]>","isStatic":false,"isPublic":true},"getByAspectRatio":{"name":"getByAspectRatio","async":true,"parameters":[{"name":"minRatio","type":"number","optional":false},{"name":"maxRatio","type":"number","optional":false}],"returnType":"Promise<Image[]>","isStatic":false,"isPublic":true}},"decoratorConfig":{"tableName":"assets"},"extends":"SmrtCollection","extendsTypeArg":"Image","exportName":"ImageCollection","collectionExportName":"ImageCollectionCollection","schema":{"tableName":"assets","ddl":"CREATE TABLE IF NOT EXISTS \\"assets\\" (\\n \\"id\\" UUID PRIMARY KEY NOT NULL,\\n \\"slug\\" TEXT NOT NULL,\\n \\"context\\" TEXT NOT NULL DEFAULT '',\\n \\"_meta_type\\" TEXT NOT NULL,\\n \\"_meta_data\\" JSON,\\n \\"created_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"updated_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"tenant_id\\" UUID,\\n \\"name\\" TEXT,\\n \\"source_uri\\" TEXT,\\n \\"mime_type\\" TEXT,\\n \\"description\\" TEXT,\\n \\"metadata\\" TEXT,\\n \\"version\\" INTEGER DEFAULT 1,\\n \\"primary_version_id\\" UUID,\\n \\"type_slug\\" TEXT,\\n \\"status_slug\\" TEXT,\\n \\"owner_profile_id\\" UUID,\\n \\"source_asset_id\\" UUID,\\n \\"folder_id\\" UUID,\\n \\"source_type\\" TEXT,\\n \\"external_id\\" TEXT,\\n \\"external_refs\\" TEXT,\\n \\"width\\" INTEGER DEFAULT 0,\\n \\"height\\" INTEGER DEFAULT 0,\\n \\"alt\\" TEXT DEFAULT ''\\n);","columns":{"id":{"type":"UUID","primaryKey":true,"referenceKind":"id","notNull":true},"slug":{"type":"TEXT","notNull":true},"context":{"type":"TEXT","notNull":true,"default":""},"_meta_type":{"type":"TEXT","notNull":true},"_meta_data":{"type":"JSON","notNull":false},"created_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"updated_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"tenant_id":{"type":"UUID","referenceKind":"tenantId","notNull":false},"name":{"type":"TEXT","notNull":false},"source_uri":{"type":"TEXT","notNull":false},"mime_type":{"type":"TEXT","notNull":false},"description":{"type":"TEXT","notNull":false},"metadata":{"type":"TEXT","notNull":false},"version":{"type":"INTEGER","notNull":false,"default":1},"primary_version_id":{"type":"UUID","referenceKind":"foreignKey","notNull":false,"foreignKey":{"table":"assets","column":"id","onDelete":"NO ACTION","onUpdate":"CASCADE"}},"type_slug":{"type":"TEXT","notNull":false},"status_slug":{"type":"TEXT","notNull":false},"owner_profile_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":false},"source_asset_id":{"type":"UUID","referenceKind":"foreignKey","notNull":false,"foreignKey":{"table":"assets","column":"id","onDelete":"NO ACTION","onUpdate":"CASCADE"}},"folder_id":{"type":"UUID","referenceKind":"foreignKey","notNull":false},"source_type":{"type":"TEXT","notNull":false},"external_id":{"type":"TEXT","notNull":false},"external_refs":{"type":"TEXT","notNull":false},"width":{"type":"INTEGER","notNull":false,"default":0},"height":{"type":"INTEGER","notNull":false,"default":0},"alt":{"type":"TEXT","notNull":false,"default":""}},"indexes":[{"name":"assets_slug_context_meta_type_idx","columns":["tenant_id","slug","context","_meta_type"],"unique":true,"nullsNotDistinct":true},{"name":"assets_meta_type_idx","columns":["_meta_type"]},{"name":"assets_tenant_id_created_at_idx","columns":["tenant_id","created_at"]},{"name":"assets_primary_version_id_idx","columns":["primary_version_id"]},{"name":"assets_owner_profile_id_idx","columns":["owner_profile_id"]},{"name":"assets_source_asset_id_idx","columns":["source_asset_id"]},{"name":"assets_folder_id_idx","columns":["folder_id"]}],"version":"dd79a8b6"},"importPath":"@happyvertical/smrt-images"}},"moduleType":"smrt","smrtDependencies":["@happyvertical/smrt-assets","@happyvertical/smrt-core","@happyvertical/smrt-prompts","@happyvertical/smrt-tenancy"],"browser":{"status":"server-only","issues":["#3624","#3628"],"reason":"sharp, node:fs/promises, node:crypto, resvg native addon","via":["@happyvertical/smrt-tenancy"]},"stampsConstructors":true}`));
13
+ ObjectRegistry.registerPackageManifest(JSON.parse(`{"version":"1.0.0","timestamp":0,"packageName":"@happyvertical/smrt-images","packageVersion":"0.55.15","objects":{"@happyvertical/smrt-images:Image":{"name":"image","className":"Image","qualifiedName":"@happyvertical/smrt-images:Image","collection":"assets","filePath":"/home/runner/work/smrt/smrt/packages/images/src/image.ts","packageName":"@happyvertical/smrt-images","fields":{"created_at":{"type":"datetime","required":false},"updated_at":{"type":"datetime","required":false},"tenantId":{"type":"text","required":false,"_meta":{"generated":true,"source":"tenantScoped_decorator","sqlType":"UUID","__tenancy":{"isTenantIdField":true,"mode":"optional","field":"tenantId","autoFilter":true,"autoPopulate":true,"allowSuperAdminBypass":false}}},"name":{"type":"text","required":false,"description":"A recognizable name for this file or media item.","_meta":{"description":"A recognizable name for this file or media item."}},"sourceUri":{"type":"text","required":false,"description":"Where the original file is stored or can be retrieved.","_meta":{"description":"Where the original file is stored or can be retrieved."}},"mimeType":{"type":"text","required":false,"description":"The file format, such as image/jpeg or application/pdf.","_meta":{"description":"The file format, such as image/jpeg or application/pdf."}},"description":{"type":"text","required":false,"description":"Explain what the file contains and when it should be used.","_meta":{"description":"Explain what the file contains and when it should be used."}},"metadata":{"type":"text","required":false,"description":"Additional details recorded while processing this file.","_meta":{"description":"Additional details recorded while processing this file."}},"version":{"type":"integer","required":true,"default":1,"description":"The revision number of this file in its version history.","_meta":{"description":"The revision number of this file in its version history."}},"primaryVersionId":{"type":"foreignKey","required":false,"related":"Asset","description":"The first version of this file, linking its revision history.","_meta":{"description":"The first version of this file, linking its revision history."}},"typeSlug":{"type":"text","required":false,"description":"The category of asset, such as an image, video, or document.","_meta":{"description":"The category of asset, such as an image, video, or document."}},"statusSlug":{"type":"text","required":false,"description":"The stage of this asset, such as draft, published, or archived.","_meta":{"description":"The stage of this asset, such as draft, published, or archived."}},"ownerProfileId":{"type":"crossPackageRef","required":false,"related":"@happyvertical/smrt-profiles:Profile","description":"The person or organization responsible for this asset.","_meta":{"description":"The person or organization responsible for this asset."}},"sourceAssetId":{"type":"foreignKey","required":false,"related":"Asset","description":"The original asset used to produce this file or variation.","_meta":{"description":"The original asset used to produce this file or variation."}},"folderId":{"type":"foreignKey","required":false,"related":"Folder","description":"The folder that organizes this file in the asset library.","_meta":{"description":"The folder that organizes this file in the asset library."}},"sourceType":{"type":"text","required":false,"description":"The service or source from which this file was obtained.","_meta":{"description":"The service or source from which this file was obtained."}},"externalId":{"type":"text","required":false,"description":"The reference used to identify this file at its original source.","_meta":{"description":"The reference used to identify this file at its original source."}},"externalRefs":{"type":"text","required":false,"description":"References that connect this file to copies held by other services.","_meta":{"description":"References that connect this file to copies held by other services."}},"createdAt":{"type":"text","required":false},"updatedAt":{"type":"text","required":false},"width":{"type":"integer","required":false,"default":0},"height":{"type":"integer","required":false,"default":0},"alt":{"type":"text","required":false,"default":""}},"methods":{"getSlug":{"name":"getSlug","async":true,"parameters":[],"returnType":"Promise<string | null | undefined>","isStatic":false,"isPublic":true},"getMetadata":{"name":"getMetadata","async":false,"parameters":[],"returnType":"Record<string, unknown>","isStatic":false,"isPublic":true},"setMetadata":{"name":"setMetadata","async":false,"parameters":[{"name":"metadata","type":"Record<string, unknown>","optional":false}],"returnType":"void","isStatic":false,"isPublic":true},"mergeMetadata":{"name":"mergeMetadata","async":false,"parameters":[{"name":"metadata","type":"Record<string, unknown>","optional":false}],"returnType":"void","isStatic":false,"isPublic":true},"getExternalRefs":{"name":"getExternalRefs","async":false,"parameters":[],"returnType":"Record<string, AssetExternalReference>","isStatic":false,"isPublic":true},"getExternalRef":{"name":"getExternalRef","async":false,"parameters":[{"name":"provider","type":"string","optional":false}],"returnType":"AssetExternalReference | null","isStatic":false,"isPublic":true},"setExternalRef":{"name":"setExternalRef","async":false,"parameters":[{"name":"provider","type":"string","optional":false},{"name":"reference","type":"any","optional":false,"typeUnresolved":true}],"returnType":"void","isStatic":false,"isPublic":true},"getTags":{"name":"getTags","async":true,"parameters":[],"returnType":"Promise<Tag[]>","isStatic":false,"isPublic":true},"hasTag":{"name":"hasTag","async":true,"parameters":[{"name":"tagSlug","type":"string","optional":false},{"name":"context","type":"string","optional":true}],"returnType":"Promise<boolean>","isStatic":false,"isPublic":true},"getSource":{"name":"getSource","async":true,"parameters":[],"returnType":"Promise<Asset | null>","isStatic":false,"isPublic":true},"getDerivatives":{"name":"getDerivatives","async":true,"parameters":[],"returnType":"Promise<Asset[]>","isStatic":false,"isPublic":true},"getType":{"name":"getType","async":true,"parameters":[],"returnType":"Promise<AssetType | null>","isStatic":false,"isPublic":true},"getStatus":{"name":"getStatus","async":true,"parameters":[],"returnType":"Promise<AssetStatus | null>","isStatic":false,"isPublic":true},"getAssociations":{"name":"getAssociations","async":true,"parameters":[],"returnType":"Promise<AssetAssociation[]>","isStatic":false,"isPublic":true},"associateWith":{"name":"associateWith","async":true,"parameters":[{"name":"metaType","type":"string","optional":false},{"name":"metaId","type":"string","optional":false},{"name":"role","type":"any","optional":true,"default":"default"}],"returnType":"Promise<AssetAssociation>","isStatic":false,"isPublic":true},"getBySlug":{"name":"getBySlug","async":true,"parameters":[{"name":"_slug","type":"string","optional":false}],"returnType":"Promise<Asset | null>","isStatic":true,"isPublic":true},"isValidImageFormat":{"name":"isValidImageFormat","async":false,"parameters":[],"returnType":"boolean","isStatic":false,"isPublic":true},"isHighResolution":{"name":"isHighResolution","async":false,"parameters":[],"returnType":"boolean","isStatic":false,"isPublic":true},"generateAltText":{"name":"generateAltText","async":true,"parameters":[],"returnType":"Promise<string>","isStatic":false,"isPublic":true}},"decoratorConfig":{"api":{"include":["list","get","create","update","delete"]},"mcp":{"include":["list","get","create","update","generateAltText"]},"cli":{"skipApiCheck":true},"tenantScoped":{"mode":"optional"},"tableName":"assets","tableStrategy":"sti"},"extends":"Asset","exportName":"Image","collectionExportName":"ImageCollection","displayLabelField":"name","validationRules":[{"field":"version","rule":"required","fieldType":"integer"}],"schema":{"tableName":"assets","ddl":"CREATE TABLE IF NOT EXISTS \\"assets\\" (\\n \\"id\\" UUID PRIMARY KEY NOT NULL,\\n \\"slug\\" TEXT NOT NULL,\\n \\"context\\" TEXT NOT NULL DEFAULT '',\\n \\"_meta_type\\" TEXT NOT NULL,\\n \\"_meta_data\\" JSON,\\n \\"created_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"updated_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"tenant_id\\" UUID,\\n \\"name\\" TEXT,\\n \\"source_uri\\" TEXT,\\n \\"mime_type\\" TEXT,\\n \\"description\\" TEXT,\\n \\"metadata\\" TEXT,\\n \\"version\\" INTEGER DEFAULT 1,\\n \\"primary_version_id\\" UUID,\\n \\"type_slug\\" TEXT,\\n \\"status_slug\\" TEXT,\\n \\"owner_profile_id\\" UUID,\\n \\"source_asset_id\\" UUID,\\n \\"folder_id\\" UUID,\\n \\"source_type\\" TEXT,\\n \\"external_id\\" TEXT,\\n \\"external_refs\\" TEXT,\\n \\"width\\" INTEGER DEFAULT 0,\\n \\"height\\" INTEGER DEFAULT 0,\\n \\"alt\\" TEXT DEFAULT ''\\n);","columns":{"id":{"type":"UUID","primaryKey":true,"referenceKind":"id","notNull":true},"slug":{"type":"TEXT","notNull":true},"context":{"type":"TEXT","notNull":true,"default":""},"_meta_type":{"type":"TEXT","notNull":true},"_meta_data":{"type":"JSON","notNull":false},"created_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"updated_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"tenant_id":{"type":"UUID","referenceKind":"tenantId","notNull":false},"name":{"type":"TEXT","notNull":false},"source_uri":{"type":"TEXT","notNull":false},"mime_type":{"type":"TEXT","notNull":false},"description":{"type":"TEXT","notNull":false},"metadata":{"type":"TEXT","notNull":false},"version":{"type":"INTEGER","notNull":false,"default":1},"primary_version_id":{"type":"UUID","referenceKind":"foreignKey","notNull":false,"foreignKey":{"table":"assets","column":"id","onDelete":"NO ACTION","onUpdate":"CASCADE"}},"type_slug":{"type":"TEXT","notNull":false},"status_slug":{"type":"TEXT","notNull":false},"owner_profile_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":false},"source_asset_id":{"type":"UUID","referenceKind":"foreignKey","notNull":false,"foreignKey":{"table":"assets","column":"id","onDelete":"NO ACTION","onUpdate":"CASCADE"}},"folder_id":{"type":"UUID","referenceKind":"foreignKey","notNull":false},"source_type":{"type":"TEXT","notNull":false},"external_id":{"type":"TEXT","notNull":false},"external_refs":{"type":"TEXT","notNull":false},"width":{"type":"INTEGER","notNull":false,"default":0},"height":{"type":"INTEGER","notNull":false,"default":0},"alt":{"type":"TEXT","notNull":false,"default":""}},"indexes":[{"name":"assets_slug_context_meta_type_idx","columns":["tenant_id","slug","context","_meta_type"],"unique":true,"nullsNotDistinct":true},{"name":"assets_meta_type_idx","columns":["_meta_type"]},{"name":"assets_tenant_id_created_at_idx","columns":["tenant_id","created_at"]},{"name":"assets_primary_version_id_idx","columns":["primary_version_id"]},{"name":"assets_owner_profile_id_idx","columns":["owner_profile_id"]},{"name":"assets_source_asset_id_idx","columns":["source_asset_id"]},{"name":"assets_folder_id_idx","columns":["folder_id"]}],"version":"dd79a8b6"},"importPath":"@happyvertical/smrt-images"},"@happyvertical/smrt-images:ImageCollection":{"name":"imagecollection","className":"ImageCollection","qualifiedName":"@happyvertical/smrt-images:ImageCollection","collection":"assets","filePath":"/home/runner/work/smrt/smrt/packages/images/src/images.ts","packageName":"@happyvertical/smrt-images","fields":{},"methods":{"findByTenant":{"name":"findByTenant","async":true,"parameters":[{"name":"tenantId","type":"string","optional":false}],"returnType":"Promise<Image[]>","isStatic":false,"isPublic":true},"findGlobal":{"name":"findGlobal","async":true,"parameters":[],"returnType":"Promise<Image[]>","isStatic":false,"isPublic":true},"findWithGlobals":{"name":"findWithGlobals","async":true,"parameters":[{"name":"tenantId","type":"string","optional":false}],"returnType":"Promise<Image[]>","isStatic":false,"isPublic":true},"getByMinDimensions":{"name":"getByMinDimensions","async":true,"parameters":[{"name":"minWidth","type":"number","optional":false},{"name":"minHeight","type":"number","optional":false}],"returnType":"Promise<Image[]>","isStatic":false,"isPublic":true},"getByMaxDimensions":{"name":"getByMaxDimensions","async":true,"parameters":[{"name":"maxWidth","type":"number","optional":false},{"name":"maxHeight","type":"number","optional":false}],"returnType":"Promise<Image[]>","isStatic":false,"isPublic":true},"getLandscape":{"name":"getLandscape","async":true,"parameters":[],"returnType":"Promise<Image[]>","isStatic":false,"isPublic":true},"getPortrait":{"name":"getPortrait","async":true,"parameters":[],"returnType":"Promise<Image[]>","isStatic":false,"isPublic":true},"getSquare":{"name":"getSquare","async":true,"parameters":[],"returnType":"Promise<Image[]>","isStatic":false,"isPublic":true},"getMissingAltText":{"name":"getMissingAltText","async":true,"parameters":[],"returnType":"Promise<Image[]>","isStatic":false,"isPublic":true},"getHighResolution":{"name":"getHighResolution","async":true,"parameters":[],"returnType":"Promise<Image[]>","isStatic":false,"isPublic":true},"getByAspectRatio":{"name":"getByAspectRatio","async":true,"parameters":[{"name":"minRatio","type":"number","optional":false},{"name":"maxRatio","type":"number","optional":false}],"returnType":"Promise<Image[]>","isStatic":false,"isPublic":true}},"decoratorConfig":{"tableName":"assets"},"extends":"SmrtCollection","extendsTypeArg":"Image","exportName":"ImageCollection","collectionExportName":"ImageCollectionCollection","schema":{"tableName":"assets","ddl":"CREATE TABLE IF NOT EXISTS \\"assets\\" (\\n \\"id\\" UUID PRIMARY KEY NOT NULL,\\n \\"slug\\" TEXT NOT NULL,\\n \\"context\\" TEXT NOT NULL DEFAULT '',\\n \\"_meta_type\\" TEXT NOT NULL,\\n \\"_meta_data\\" JSON,\\n \\"created_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"updated_at\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\"tenant_id\\" UUID,\\n \\"name\\" TEXT,\\n \\"source_uri\\" TEXT,\\n \\"mime_type\\" TEXT,\\n \\"description\\" TEXT,\\n \\"metadata\\" TEXT,\\n \\"version\\" INTEGER DEFAULT 1,\\n \\"primary_version_id\\" UUID,\\n \\"type_slug\\" TEXT,\\n \\"status_slug\\" TEXT,\\n \\"owner_profile_id\\" UUID,\\n \\"source_asset_id\\" UUID,\\n \\"folder_id\\" UUID,\\n \\"source_type\\" TEXT,\\n \\"external_id\\" TEXT,\\n \\"external_refs\\" TEXT,\\n \\"width\\" INTEGER DEFAULT 0,\\n \\"height\\" INTEGER DEFAULT 0,\\n \\"alt\\" TEXT DEFAULT ''\\n);","columns":{"id":{"type":"UUID","primaryKey":true,"referenceKind":"id","notNull":true},"slug":{"type":"TEXT","notNull":true},"context":{"type":"TEXT","notNull":true,"default":""},"_meta_type":{"type":"TEXT","notNull":true},"_meta_data":{"type":"JSON","notNull":false},"created_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"updated_at":{"type":"TIMESTAMP","notNull":true,"default":"current_timestamp"},"tenant_id":{"type":"UUID","referenceKind":"tenantId","notNull":false},"name":{"type":"TEXT","notNull":false},"source_uri":{"type":"TEXT","notNull":false},"mime_type":{"type":"TEXT","notNull":false},"description":{"type":"TEXT","notNull":false},"metadata":{"type":"TEXT","notNull":false},"version":{"type":"INTEGER","notNull":false,"default":1},"primary_version_id":{"type":"UUID","referenceKind":"foreignKey","notNull":false,"foreignKey":{"table":"assets","column":"id","onDelete":"NO ACTION","onUpdate":"CASCADE"}},"type_slug":{"type":"TEXT","notNull":false},"status_slug":{"type":"TEXT","notNull":false},"owner_profile_id":{"type":"UUID","referenceKind":"crossPackageRef","notNull":false},"source_asset_id":{"type":"UUID","referenceKind":"foreignKey","notNull":false,"foreignKey":{"table":"assets","column":"id","onDelete":"NO ACTION","onUpdate":"CASCADE"}},"folder_id":{"type":"UUID","referenceKind":"foreignKey","notNull":false},"source_type":{"type":"TEXT","notNull":false},"external_id":{"type":"TEXT","notNull":false},"external_refs":{"type":"TEXT","notNull":false},"width":{"type":"INTEGER","notNull":false,"default":0},"height":{"type":"INTEGER","notNull":false,"default":0},"alt":{"type":"TEXT","notNull":false,"default":""}},"indexes":[{"name":"assets_slug_context_meta_type_idx","columns":["tenant_id","slug","context","_meta_type"],"unique":true,"nullsNotDistinct":true},{"name":"assets_meta_type_idx","columns":["_meta_type"]},{"name":"assets_tenant_id_created_at_idx","columns":["tenant_id","created_at"]},{"name":"assets_primary_version_id_idx","columns":["primary_version_id"]},{"name":"assets_owner_profile_id_idx","columns":["owner_profile_id"]},{"name":"assets_source_asset_id_idx","columns":["source_asset_id"]},{"name":"assets_folder_id_idx","columns":["folder_id"]}],"version":"dd79a8b6"},"importPath":"@happyvertical/smrt-images"}},"moduleType":"smrt","smrtDependencies":["@happyvertical/smrt-assets","@happyvertical/smrt-core","@happyvertical/smrt-profiles","@happyvertical/smrt-prompts","@happyvertical/smrt-tenancy"],"browser":{"status":"server-only","issues":["#3624","#3628"],"reason":"sharp, node:fs/promises, node:crypto, resvg native addon","via":["@happyvertical/smrt-tenancy"]},"stampsConstructors":true}`));
11
14
  //#endregion
12
15
  //#region src/prompts.ts
13
16
  var smrtImagesGenerateAltTextPrompt = definePrompt({
@@ -1235,6 +1238,225 @@ var ImageMetadataExtractor = class {
1235
1238
  }
1236
1239
  };
1237
1240
  //#endregion
1241
+ //#region src/photo-cutout-coordinate-guide.ts
1242
+ async function createPhotoCutoutCoordinateGuide(sourceDataUrl, width, height) {
1243
+ const match = /^data:image\/(?:png|jpeg|webp);base64,([A-Za-z0-9+/]+={0,2})$/.exec(sourceDataUrl);
1244
+ if (!match) throw new Error("A PNG, JPEG, or WebP source image is required.");
1245
+ if (!Number.isInteger(width) || !Number.isInteger(height) || width < 1 || height < 1) throw new Error("Coordinate guide dimensions are invalid.");
1246
+ const { default: sharp } = await import("sharp");
1247
+ const source = Buffer.from(match[1], "base64");
1248
+ const metadata = await sharp(source).metadata();
1249
+ if (!metadata.width || !metadata.height || metadata.width * metadata.height > 16e6) throw new Error("Coordinate guide source dimensions are invalid.");
1250
+ const oriented = await sharp(source).rotate().toBuffer({ resolveWithObject: true });
1251
+ if (oriented.info.width !== width || oriented.info.height !== height) throw new Error("Coordinate guide dimensions do not match the source.");
1252
+ const grid = coordinateGridSvg(width, height);
1253
+ return `data:image/png;base64,${(await sharp(oriented.data).composite([{ input: Buffer.from(grid) }]).png().toBuffer()).toString("base64")}`;
1254
+ }
1255
+ function coordinateGridSvg(width, height) {
1256
+ const line = (fraction) => {
1257
+ return `<path d="M${Math.round(width * fraction)} 0V${height}M0 ${Math.round(height * fraction)}H${width}"/>`;
1258
+ };
1259
+ const label = (fraction) => {
1260
+ const x = Math.round(width * fraction);
1261
+ const y = Math.round(height * fraction);
1262
+ const value = Math.round(fraction * 1e3);
1263
+ return `<text x="${Math.min(x + 4, width - 30)}" y="16">${value}</text><text x="4" y="${Math.min(y - 4, height - 4)}">${value}</text>`;
1264
+ };
1265
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">
1266
+ <g fill="none" stroke="#00e5ff" stroke-opacity=".58" stroke-width="1">${Array.from({ length: 9 }, (_, index) => line((index + 1) / 10)).join("")}</g>
1267
+ <rect x=".5" y=".5" width="${width - 1}" height="${height - 1}" fill="none" stroke="#00e5ff" stroke-width="1"/>
1268
+ <g fill="#ffffff" stroke="#00151a" stroke-width="2" paint-order="stroke" font-family="monospace" font-size="12">${[
1269
+ 0,
1270
+ .25,
1271
+ .5,
1272
+ .75,
1273
+ 1
1274
+ ].map(label).join("")}</g>
1275
+ </svg>`;
1276
+ }
1277
+ //#endregion
1278
+ //#region src/photo-cutout-profile-store.ts
1279
+ var RELATIONSHIP = "character_cutout";
1280
+ var MANIFEST_VERSION = 1;
1281
+ var PHOTO_CUTOUT_PERSISTED_ASSET_REF = "character_cutout";
1282
+ var PhotoCutoutManifestError = class extends Error {};
1283
+ var PhotoCutoutProfileStore = class {
1284
+ constructor(options) {
1285
+ this.options = options;
1286
+ this.now = options.now ?? (() => /* @__PURE__ */ new Date());
1287
+ }
1288
+ options;
1289
+ now;
1290
+ async save(input) {
1291
+ await assertPngMatchesRig(input.png, input.rig);
1292
+ const profile = await this.requireAuthorizedProfile(input, "save");
1293
+ validatePhotoCutoutRig(input.rig);
1294
+ const linkedBefore = new Set((await profile.getAssets(RELATIONSHIP)).flatMap((asset2) => asset2.id ? [asset2.id] : []));
1295
+ const persistenceId = randomUUID();
1296
+ const asset = await this.options.runtime.storeSourceAsset(input.name ?? "character-cutout.png", input.png, {
1297
+ mimeType: "image/png",
1298
+ typeSlug: "photo-cutout",
1299
+ tenantId: input.tenantId,
1300
+ ownerProfileId: input.profileId,
1301
+ metadata: { photoCutoutPersistenceId: persistenceId }
1302
+ });
1303
+ const assetId = asset.id ? String(asset.id) : null;
1304
+ const createdByCall = Boolean(assetId) && !linkedBefore.has(assetId) && readRecord(asset.metadata).photoCutoutPersistenceId === persistenceId;
1305
+ try {
1306
+ if (!assetId) throw new Error("Stored photo cutout is missing an asset id");
1307
+ if (!createdByCall) throw new Error("Asset runtime did not create a new photo cutout asset");
1308
+ asset.tenantId = input.tenantId;
1309
+ asset.ownerProfileId = input.profileId;
1310
+ const rig = bindRigToAsset(input.rig);
1311
+ const savedAt = this.now().toISOString();
1312
+ asset.metadata = JSON.stringify({ photoCutout: {
1313
+ version: MANIFEST_VERSION,
1314
+ savedAt,
1315
+ rig
1316
+ } });
1317
+ await asset.save();
1318
+ await profile.addAsset(asset, RELATIONSHIP, 0);
1319
+ return {
1320
+ assetId,
1321
+ rig,
1322
+ png: input.png,
1323
+ savedAt
1324
+ };
1325
+ } catch (cause) {
1326
+ if (!(await profile.getAssets(RELATIONSHIP)).some((linked) => String(linked.id) === assetId) && createdByCall) await this.options.runtime.store.remove(asset);
1327
+ throw cause;
1328
+ }
1329
+ }
1330
+ async load(input) {
1331
+ const profile = await this.requireAuthorizedProfile(input, "load");
1332
+ if (input.assetId) {
1333
+ const selected2 = (await this.readLinkedCandidates(profile, input)).find((candidate) => candidate.assetId === input.assetId);
1334
+ if (!selected2) return null;
1335
+ return this.loadSelected(selected2.assetId, readManifest(selected2.asset));
1336
+ }
1337
+ const selected = (await this.readSaved(profile, input))[0];
1338
+ if (!selected) return null;
1339
+ return this.loadSelected(selected.assetId, selected.manifest);
1340
+ }
1341
+ async loadSelected(assetId, manifest) {
1342
+ const read = await this.options.runtime.store.readById(assetId);
1343
+ if (!read) throw new Error("Saved photo cutout bytes are unavailable");
1344
+ await assertPngMatchesRig(read.data, manifest.rig);
1345
+ return {
1346
+ assetId,
1347
+ rig: manifest.rig,
1348
+ png: read.data,
1349
+ savedAt: manifest.savedAt
1350
+ };
1351
+ }
1352
+ /**
1353
+ * Lists valid, profile-linked saved setups without fetching their image
1354
+ * bytes. Authorization deliberately follows `load`: gallery visibility is
1355
+ * the same protected read as selecting a setup.
1356
+ */
1357
+ async list(input) {
1358
+ const profile = await this.requireAuthorizedProfile(input, "load");
1359
+ return (await this.readSaved(profile, input, { omitInvalidManifests: true })).map(({ asset, assetId, manifest }) => ({
1360
+ assetId,
1361
+ savedAt: manifest.savedAt,
1362
+ ...asset.name ? { name: asset.name } : {}
1363
+ }));
1364
+ }
1365
+ async readSaved(profile, input, options = {}) {
1366
+ return (await this.readLinkedCandidates(profile, input)).flatMap(({ asset, assetId }) => {
1367
+ try {
1368
+ return [{
1369
+ asset,
1370
+ assetId,
1371
+ manifest: readManifest(asset)
1372
+ }];
1373
+ } catch (cause) {
1374
+ if (options.omitInvalidManifests && cause instanceof PhotoCutoutManifestError) return [];
1375
+ throw cause;
1376
+ }
1377
+ }).sort((a, b) => b.manifest.savedAt.localeCompare(a.manifest.savedAt) || b.assetId.localeCompare(a.assetId));
1378
+ }
1379
+ readLinkedCandidates(profile, input) {
1380
+ return profile.getAssets(RELATIONSHIP).then((candidates) => candidates.flatMap((asset) => {
1381
+ if (!asset.id || asset.ownerProfileId !== input.profileId || asset.tenantId !== input.tenantId || asset.typeSlug !== "photo-cutout") return [];
1382
+ return [{
1383
+ asset,
1384
+ assetId: asset.id
1385
+ }];
1386
+ }));
1387
+ }
1388
+ async requireAuthorizedProfile(input, operation) {
1389
+ const profile = await this.options.getProfile(input.profileId);
1390
+ if (!profile?.id) throw new Error("Profile was not found");
1391
+ if (profile.id !== input.profileId || profile.tenantId !== input.tenantId) throw new Error("Profile does not belong to the active tenant");
1392
+ if (!await this.options.authorize({
1393
+ actorProfileId: input.actorProfileId,
1394
+ profile,
1395
+ tenantId: input.tenantId,
1396
+ operation
1397
+ })) throw new Error("Character setup authorization denied");
1398
+ return profile;
1399
+ }
1400
+ };
1401
+ function bindRigToAsset(rig) {
1402
+ const persisted = structuredClone(rig);
1403
+ for (const layer of persisted.layers) if ("assetId" in layer) layer.assetId = PHOTO_CUTOUT_PERSISTED_ASSET_REF;
1404
+ validatePhotoCutoutRig(persisted);
1405
+ return persisted;
1406
+ }
1407
+ function readManifest(asset) {
1408
+ let parsed;
1409
+ try {
1410
+ parsed = JSON.parse(asset.metadata);
1411
+ } catch {
1412
+ throw new PhotoCutoutManifestError("Saved photo cutout manifest is malformed");
1413
+ }
1414
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new PhotoCutoutManifestError("Saved photo cutout manifest is malformed");
1415
+ const manifest = parsed.photoCutout;
1416
+ if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) throw new PhotoCutoutManifestError("Saved photo cutout manifest is malformed");
1417
+ const value = manifest;
1418
+ if (value.version !== MANIFEST_VERSION || typeof value.savedAt !== "string" || Number.isNaN(Date.parse(value.savedAt)) || !value.rig) throw new PhotoCutoutManifestError("Saved photo cutout manifest has an unsupported version");
1419
+ try {
1420
+ validatePhotoCutoutRig(value.rig);
1421
+ } catch {
1422
+ throw new PhotoCutoutManifestError("Saved photo cutout manifest rig is invalid");
1423
+ }
1424
+ if (!asset.id || value.rig.layers.some((layer) => "assetId" in layer && layer.assetId !== "character_cutout")) throw new PhotoCutoutManifestError("Saved photo cutout manifest does not match its asset");
1425
+ return {
1426
+ version: MANIFEST_VERSION,
1427
+ savedAt: value.savedAt,
1428
+ rig: value.rig
1429
+ };
1430
+ }
1431
+ function readRecord(value) {
1432
+ try {
1433
+ const parsed = JSON.parse(value);
1434
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
1435
+ } catch {
1436
+ return {};
1437
+ }
1438
+ }
1439
+ async function assertPngMatchesRig(png, rig) {
1440
+ if (!Buffer.isBuffer(png) || png.byteLength < 24 || !png.subarray(0, 8).equals(Buffer.from([
1441
+ 137,
1442
+ 80,
1443
+ 78,
1444
+ 71,
1445
+ 13,
1446
+ 10,
1447
+ 26,
1448
+ 10
1449
+ ]))) throw new Error("Photo cutout must be a PNG");
1450
+ const metadata = await sharp(png).metadata();
1451
+ if (!metadata.width || !metadata.height || metadata.format !== "png") throw new Error("Photo cutout PNG dimensions are invalid");
1452
+ if (rig.canvas.width !== metadata.width || rig.canvas.height < metadata.height) throw new Error("Photo cutout rig canvas does not match PNG dimensions");
1453
+ for (const layer of rig.layers) {
1454
+ if (layer.role === "mouth-interior") continue;
1455
+ const frame = layer.imageFrame;
1456
+ if (!frame || frame.x !== 0 || frame.y !== 0 || frame.width !== metadata.width || frame.height !== metadata.height) throw new Error("Photo cutout rig image frame does not match PNG dimensions");
1457
+ }
1458
+ }
1459
+ //#endregion
1238
1460
  //#region src/search.ts
1239
1461
  var ImageSearch = class {
1240
1462
  constructor(collection, _options = {}) {
@@ -1357,6 +1579,6 @@ var UpstreamManager = class {
1357
1579
  }
1358
1580
  };
1359
1581
  //#endregion
1360
- export { IMAGE_ADJUST_OPERATIONS, Image, ImageCategorizer, ImageCollection, ImageDeriver, ImageEditor, ImageMetadataExtractor, ImageSearch, UpstreamManager, applyImageAdjustments, decodeImageAdjustments, describeImageAdjustments, encodeImageAdjustments, imageAdjustVariants, isEmptyImageAdjustments, isImageAdjustOperation, isImageFocus, normalizeImageAdjustments, persistImageMediaBundleInspection, regionForFocus, regionToPixels, smrtImagesGenerateAltTextPrompt };
1582
+ export { IMAGE_ADJUST_OPERATIONS, Image, ImageCategorizer, ImageCollection, ImageDeriver, ImageEditor, ImageMetadataExtractor, ImageSearch, PHOTO_CUTOUT_PERSISTED_ASSET_REF, PhotoCutoutProfileStore, UpstreamManager, applyImageAdjustments, assembleCanadianSplitRig, createPhotoCutoutCoordinateGuide, decodeImageAdjustments, describeImageAdjustments, encodeImageAdjustments, faceOutlinePrompt, imageAdjustVariants, isEmptyImageAdjustments, isImageAdjustOperation, isImageFocus, mouthLandmarksPrompt, normalizeImageAdjustments, parseFaceOutline, parseMouthLandmarks, parsePhotoCutoutSetup, persistImageMediaBundleInspection, photoCutoutSetupPrompt, regionForFocus, regionToPixels, smrtImagesGenerateAltTextPrompt };
1361
1583
 
1362
1584
  //# sourceMappingURL=index.js.map