@happyvertical/smrt-images 0.55.9 → 0.55.11
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 +74 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +224 -2
- package/dist/index.js.map +1 -1
- package/dist/manifest.json +2 -1
- package/dist/photo-cutout-coordinate-guide.d.ts +7 -0
- package/dist/photo-cutout-coordinate-guide.d.ts.map +1 -0
- package/dist/photo-cutout-profile-store.d.ts +76 -0
- package/dist/photo-cutout-profile-store.d.ts.map +1 -0
- package/dist/photo-cutout-setup.d.ts +48 -0
- package/dist/photo-cutout-setup.d.ts.map +1 -0
- package/dist/photo-cutout-setup.js +145 -0
- package/dist/photo-cutout-setup.js.map +1 -0
- package/dist/smrt-knowledge.json +6 -3
- package/dist/svelte/components/PhotoCutoutSetup.svelte +432 -0
- package/dist/svelte/components/PhotoCutoutSetup.svelte.d.ts +24 -0
- package/dist/svelte/components/PhotoCutoutSetup.svelte.d.ts.map +1 -0
- package/dist/svelte/head-isolation.d.ts +13 -0
- package/dist/svelte/head-isolation.d.ts.map +1 -0
- package/dist/svelte/head-isolation.js +83 -0
- package/dist/svelte/i18n.d.ts +18 -0
- package/dist/svelte/i18n.d.ts.map +1 -1
- package/dist/svelte/i18n.js +19 -0
- package/dist/svelte/index.d.ts +2 -1
- package/dist/svelte/index.d.ts.map +1 -1
- package/dist/svelte/index.js +2 -1
- package/package.json +11 -9
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";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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;
|
|
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.11","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-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
|