···22use jacquard_common::IntoStatic;
33use std::collections::HashMap;
4455-use crate::place_wisp::fs::{Directory, EntryNode};
55+use wisp_lexicons::place_wisp::fs::{Directory, EntryNode};
6677/// Extract blob information from a directory tree
88/// Returns a map of file paths to their blob refs and CIDs
···11+extern crate alloc;
22+13// @generated by jacquard-lexicon. DO NOT EDIT.
24//
35// This file was automatically generated from Lexicon schemas.
+13-15
cli/src/main.rs
···11-mod builder_types;
22-mod place_wisp;
31mod cid;
42mod blob_map;
53mod metadata;
···2826use futures::stream::{self, StreamExt};
2927use indicatif::{ProgressBar, ProgressStyle, MultiProgress};
30283131-use place_wisp::fs::*;
3232-use place_wisp::settings::*;
2929+use wisp_lexicons::place_wisp::fs::*;
3030+use wisp_lexicons::place_wisp::settings::*;
33313432/// Maximum number of concurrent file uploads to the PDS
3533const MAX_CONCURRENT_UPLOADS: usize = 2;
···512510 let chunk_file_count = subfs_utils::count_files_in_directory(chunk);
513511 let chunk_size = subfs_utils::estimate_directory_size(chunk);
514512515515- let chunk_manifest = crate::place_wisp::subfs::SubfsRecord::new()
513513+ let chunk_manifest = wisp_lexicons::place_wisp::subfs::SubfsRecord::new()
516514 .root(convert_fs_dir_to_subfs_dir(chunk.clone()))
517515 .file_count(Some(chunk_file_count as i64))
518516 .created_at(Datetime::now())
···535533 // Each chunk reference MUST have flat: true to merge chunk contents
536534 println!(" → Creating parent subfs with {} chunk references...", chunk_uris.len());
537535 use jacquard_common::CowStr;
538538- use crate::place_wisp::fs::{Subfs};
536536+ use wisp_lexicons::place_wisp::fs::{Subfs};
539537540538 // Convert to fs::Subfs (which has the 'flat' field) instead of subfs::Subfs
541539 let parent_entries_fs: Vec<Entry> = chunk_uris.iter().enumerate().map(|(i, (uri, _))| {
···565563 let parent_tid = Tid::now_0();
566564 let parent_rkey = parent_tid.to_string();
567565568568- let parent_manifest = crate::place_wisp::subfs::SubfsRecord::new()
566566+ let parent_manifest = wisp_lexicons::place_wisp::subfs::SubfsRecord::new()
569567 .root(parent_root_subfs)
570568 .file_count(Some(largest_dir.file_count as i64))
571569 .created_at(Datetime::now())
···584582 let subfs_tid = Tid::now_0();
585583 let subfs_rkey = subfs_tid.to_string();
586584587587- let subfs_manifest = crate::place_wisp::subfs::SubfsRecord::new()
585585+ let subfs_manifest = wisp_lexicons::place_wisp::subfs::SubfsRecord::new()
588586 .root(convert_fs_dir_to_subfs_dir(largest_dir.directory.clone()))
589587 .file_count(Some(largest_dir.file_count as i64))
590588 .created_at(Datetime::now())
···952950953951/// Convert fs::Directory to subfs::Directory
954952/// They have the same structure, but different types
955955-fn convert_fs_dir_to_subfs_dir(fs_dir: place_wisp::fs::Directory<'static>) -> place_wisp::subfs::Directory<'static> {
956956- use place_wisp::subfs::{Directory as SubfsDirectory, Entry as SubfsEntry, EntryNode as SubfsEntryNode, File as SubfsFile};
953953+fn convert_fs_dir_to_subfs_dir(fs_dir: wisp_lexicons::place_wisp::fs::Directory<'static>) -> wisp_lexicons::place_wisp::subfs::Directory<'static> {
954954+ use wisp_lexicons::place_wisp::subfs::{Directory as SubfsDirectory, Entry as SubfsEntry, EntryNode as SubfsEntryNode, File as SubfsFile};
957955958956 let subfs_entries: Vec<SubfsEntry> = fs_dir.entries.into_iter().map(|entry| {
959957 let node = match entry.node {
960960- place_wisp::fs::EntryNode::File(file) => {
958958+ wisp_lexicons::place_wisp::fs::EntryNode::File(file) => {
961959 SubfsEntryNode::File(Box::new(SubfsFile::new()
962960 .r#type(file.r#type)
963961 .blob(file.blob)
···966964 .base64(file.base64)
967965 .build()))
968966 }
969969- place_wisp::fs::EntryNode::Directory(dir) => {
967967+ wisp_lexicons::place_wisp::fs::EntryNode::Directory(dir) => {
970968 SubfsEntryNode::Directory(Box::new(convert_fs_dir_to_subfs_dir(*dir)))
971969 }
972972- place_wisp::fs::EntryNode::Subfs(subfs) => {
970970+ wisp_lexicons::place_wisp::fs::EntryNode::Subfs(subfs) => {
973971 // Nested subfs in the directory we're converting
974972 // Note: subfs::Subfs doesn't have the 'flat' field - that's only in fs::Subfs
975975- SubfsEntryNode::Subfs(Box::new(place_wisp::subfs::Subfs::new()
973973+ SubfsEntryNode::Subfs(Box::new(wisp_lexicons::place_wisp::subfs::Subfs::new()
976974 .r#type(subfs.r#type)
977975 .subject(subfs.subject)
978976 .build()))
979977 }
980980- place_wisp::fs::EntryNode::Unknown(unknown) => {
978978+ wisp_lexicons::place_wisp::fs::EntryNode::Unknown(unknown) => {
981979 SubfsEntryNode::Unknown(unknown)
982980 }
983981 };
-9
cli/src/mod.rs
···11-// @generated by jacquard-lexicon. DO NOT EDIT.
22-//
33-// This file was automatically generated from Lexicon schemas.
44-// Any manual changes will be overwritten on the next regeneration.
55-66-pub mod builder_types;
77-88-#[cfg(feature = "place_wisp")]
99-pub mod place_wisp;
···554554 }
555555 /// State trait tracking which required fields have been set
556556 pub trait State: sealed::Sealed {
557557- type Name;
558557 type Node;
558558+ type Name;
559559 }
560560 /// Empty state - all required fields are unset
561561 pub struct Empty(());
562562 impl sealed::Sealed for Empty {}
563563 impl State for Empty {
564564- type Name = Unset;
565564 type Node = Unset;
566566- }
567567- ///State transition - sets the `name` field to Set
568568- pub struct SetName<S: State = Empty>(PhantomData<fn() -> S>);
569569- impl<S: State> sealed::Sealed for SetName<S> {}
570570- impl<S: State> State for SetName<S> {
571571- type Name = Set<members::name>;
572572- type Node = S::Node;
565565+ type Name = Unset;
573566 }
574567 ///State transition - sets the `node` field to Set
575568 pub struct SetNode<S: State = Empty>(PhantomData<fn() -> S>);
576569 impl<S: State> sealed::Sealed for SetNode<S> {}
577570 impl<S: State> State for SetNode<S> {
578578- type Name = S::Name;
579571 type Node = Set<members::node>;
572572+ type Name = S::Name;
573573+ }
574574+ ///State transition - sets the `name` field to Set
575575+ pub struct SetName<S: State = Empty>(PhantomData<fn() -> S>);
576576+ impl<S: State> sealed::Sealed for SetName<S> {}
577577+ impl<S: State> State for SetName<S> {
578578+ type Node = S::Node;
579579+ type Name = Set<members::name>;
580580 }
581581 /// Marker types for field names
582582 #[allow(non_camel_case_types)]
583583 pub mod members {
584584+ ///Marker type for the `node` field
585585+ pub struct node(());
584586 ///Marker type for the `name` field
585587 pub struct name(());
586586- ///Marker type for the `node` field
587587- pub struct node(());
588588 }
589589}
590590···657657impl<'a, S> EntryBuilder<'a, S>
658658where
659659 S: entry_state::State,
660660- S::Name: entry_state::IsSet,
661660 S::Node: entry_state::IsSet,
661661+ S::Name: entry_state::IsSet,
662662{
663663 /// Build the final struct
664664 pub fn build(self) -> Entry<'a> {
···749749pub struct File<'a> {
750750 /// True if blob content is base64-encoded (used to bypass PDS content sniffing)
751751 #[serde(skip_serializing_if = "std::option::Option::is_none")]
752752- pub base64: Option<bool>,
752752+ pub base64: std::option::Option<bool>,
753753 /// Content blob ref
754754 #[serde(borrow)]
755755 pub blob: jacquard_common::types::blob::BlobRef<'a>,
756756 /// Content encoding (e.g., gzip for compressed files)
757757 #[serde(skip_serializing_if = "std::option::Option::is_none")]
758758 #[serde(borrow)]
759759- pub encoding: Option<jacquard_common::CowStr<'a>>,
759759+ pub encoding: std::option::Option<jacquard_common::CowStr<'a>>,
760760 /// Original MIME type before compression
761761 #[serde(skip_serializing_if = "std::option::Option::is_none")]
762762 #[serde(borrow)]
763763- pub mime_type: Option<jacquard_common::CowStr<'a>>,
763763+ pub mime_type: std::option::Option<jacquard_common::CowStr<'a>>,
764764 #[serde(borrow)]
765765 pub r#type: jacquard_common::CowStr<'a>,
766766}
···994994pub struct Fs<'a> {
995995 pub created_at: jacquard_common::types::string::Datetime,
996996 #[serde(skip_serializing_if = "std::option::Option::is_none")]
997997- pub file_count: Option<i64>,
997997+ pub file_count: std::option::Option<i64>,
998998 #[serde(borrow)]
999999 pub root: crate::place_wisp::fs::Directory<'a>,
10001000 #[serde(borrow)]
···10111011 }
10121012 /// State trait tracking which required fields have been set
10131013 pub trait State: sealed::Sealed {
10141014+ type CreatedAt;
10141015 type Site;
10151016 type Root;
10161016- type CreatedAt;
10171017 }
10181018 /// Empty state - all required fields are unset
10191019 pub struct Empty(());
10201020 impl sealed::Sealed for Empty {}
10211021 impl State for Empty {
10221022+ type CreatedAt = Unset;
10221023 type Site = Unset;
10231024 type Root = Unset;
10241024- type CreatedAt = Unset;
10251025+ }
10261026+ ///State transition - sets the `created_at` field to Set
10271027+ pub struct SetCreatedAt<S: State = Empty>(PhantomData<fn() -> S>);
10281028+ impl<S: State> sealed::Sealed for SetCreatedAt<S> {}
10291029+ impl<S: State> State for SetCreatedAt<S> {
10301030+ type CreatedAt = Set<members::created_at>;
10311031+ type Site = S::Site;
10321032+ type Root = S::Root;
10251033 }
10261034 ///State transition - sets the `site` field to Set
10271035 pub struct SetSite<S: State = Empty>(PhantomData<fn() -> S>);
10281036 impl<S: State> sealed::Sealed for SetSite<S> {}
10291037 impl<S: State> State for SetSite<S> {
10381038+ type CreatedAt = S::CreatedAt;
10301039 type Site = Set<members::site>;
10311040 type Root = S::Root;
10321032- type CreatedAt = S::CreatedAt;
10331041 }
10341042 ///State transition - sets the `root` field to Set
10351043 pub struct SetRoot<S: State = Empty>(PhantomData<fn() -> S>);
10361044 impl<S: State> sealed::Sealed for SetRoot<S> {}
10371045 impl<S: State> State for SetRoot<S> {
10381038- type Site = S::Site;
10391039- type Root = Set<members::root>;
10401046 type CreatedAt = S::CreatedAt;
10411041- }
10421042- ///State transition - sets the `created_at` field to Set
10431043- pub struct SetCreatedAt<S: State = Empty>(PhantomData<fn() -> S>);
10441044- impl<S: State> sealed::Sealed for SetCreatedAt<S> {}
10451045- impl<S: State> State for SetCreatedAt<S> {
10461047 type Site = S::Site;
10471047- type Root = S::Root;
10481048- type CreatedAt = Set<members::created_at>;
10481048+ type Root = Set<members::root>;
10491049 }
10501050 /// Marker types for field names
10511051 #[allow(non_camel_case_types)]
10521052 pub mod members {
10531053+ ///Marker type for the `created_at` field
10541054+ pub struct created_at(());
10531055 ///Marker type for the `site` field
10541056 pub struct site(());
10551057 ///Marker type for the `root` field
10561058 pub struct root(());
10571057- ///Marker type for the `created_at` field
10581058- pub struct created_at(());
10591059 }
10601060}
10611061···11621162impl<'a, S> FsBuilder<'a, S>
11631163where
11641164 S: fs_state::State,
11651165+ S::CreatedAt: fs_state::IsSet,
11651166 S::Site: fs_state::IsSet,
11661167 S::Root: fs_state::IsSet,
11671167- S::CreatedAt: fs_state::IsSet,
11681168{
11691169 /// Build the final struct
11701170 pub fn build(self) -> Fs<'a> {
···13061306)]
13071307#[serde(rename_all = "camelCase")]
13081308pub struct Subfs<'a> {
13091309- /// If true, the subfs record's root entries are merged (flattened) into the parent directory, replacing the subfs entry. If false (default), the subfs entries are placed in a subdirectory with the subfs entry's name. Flat merging is useful for splitting large directories across multiple records while maintaining a flat structure.
13091309+ /// If true (default), the subfs record's root entries are merged (flattened) into the parent directory, replacing the subfs entry. If false, the subfs entries are placed in a subdirectory with the subfs entry's name. Flat merging is useful for splitting large directories across multiple records while maintaining a flat structure.
13101310 #[serde(skip_serializing_if = "std::option::Option::is_none")]
13111311- pub flat: Option<bool>,
13111311+ pub flat: std::option::Option<bool>,
13121312 /// AT-URI pointing to a place.wisp.subfs record containing this subtree.
13131313 #[serde(borrow)]
13141314 pub subject: jacquard_common::types::string::AtUri<'a>,
···725725pub struct File<'a> {
726726 /// True if blob content is base64-encoded (used to bypass PDS content sniffing)
727727 #[serde(skip_serializing_if = "std::option::Option::is_none")]
728728- pub base64: Option<bool>,
728728+ pub base64: std::option::Option<bool>,
729729 /// Content blob ref
730730 #[serde(borrow)]
731731 pub blob: jacquard_common::types::blob::BlobRef<'a>,
732732 /// Content encoding (e.g., gzip for compressed files)
733733 #[serde(skip_serializing_if = "std::option::Option::is_none")]
734734 #[serde(borrow)]
735735- pub encoding: Option<jacquard_common::CowStr<'a>>,
735735+ pub encoding: std::option::Option<jacquard_common::CowStr<'a>>,
736736 /// Original MIME type before compression
737737 #[serde(skip_serializing_if = "std::option::Option::is_none")]
738738 #[serde(borrow)]
739739- pub mime_type: Option<jacquard_common::CowStr<'a>>,
739739+ pub mime_type: std::option::Option<jacquard_common::CowStr<'a>>,
740740 #[serde(borrow)]
741741 pub r#type: jacquard_common::CowStr<'a>,
742742}
···751751 }
752752 /// State trait tracking which required fields have been set
753753 pub trait State: sealed::Sealed {
754754- type Type;
755754 type Blob;
755755+ type Type;
756756 }
757757 /// Empty state - all required fields are unset
758758 pub struct Empty(());
759759 impl sealed::Sealed for Empty {}
760760 impl State for Empty {
761761- type Type = Unset;
762761 type Blob = Unset;
763763- }
764764- ///State transition - sets the `type` field to Set
765765- pub struct SetType<S: State = Empty>(PhantomData<fn() -> S>);
766766- impl<S: State> sealed::Sealed for SetType<S> {}
767767- impl<S: State> State for SetType<S> {
768768- type Type = Set<members::r#type>;
769769- type Blob = S::Blob;
762762+ type Type = Unset;
770763 }
771764 ///State transition - sets the `blob` field to Set
772765 pub struct SetBlob<S: State = Empty>(PhantomData<fn() -> S>);
773766 impl<S: State> sealed::Sealed for SetBlob<S> {}
774767 impl<S: State> State for SetBlob<S> {
768768+ type Blob = Set<members::blob>;
775769 type Type = S::Type;
776776- type Blob = Set<members::blob>;
770770+ }
771771+ ///State transition - sets the `type` field to Set
772772+ pub struct SetType<S: State = Empty>(PhantomData<fn() -> S>);
773773+ impl<S: State> sealed::Sealed for SetType<S> {}
774774+ impl<S: State> State for SetType<S> {
775775+ type Blob = S::Blob;
776776+ type Type = Set<members::r#type>;
777777 }
778778 /// Marker types for field names
779779 #[allow(non_camel_case_types)]
780780 pub mod members {
781781+ ///Marker type for the `blob` field
782782+ pub struct blob(());
781783 ///Marker type for the `type` field
782784 pub struct r#type(());
783783- ///Marker type for the `blob` field
784784- pub struct blob(());
785785 }
786786}
787787···905905impl<'a, S> FileBuilder<'a, S>
906906where
907907 S: file_state::State,
908908- S::Type: file_state::IsSet,
909908 S::Blob: file_state::IsSet,
909909+ S::Type: file_state::IsSet,
910910{
911911 /// Build the final struct
912912 pub fn build(self) -> File<'a> {
···970970pub struct SubfsRecord<'a> {
971971 pub created_at: jacquard_common::types::string::Datetime,
972972 #[serde(skip_serializing_if = "std::option::Option::is_none")]
973973- pub file_count: Option<i64>,
973973+ pub file_count: std::option::Option<i64>,
974974 #[serde(borrow)]
975975 pub root: crate::place_wisp::subfs::Directory<'a>,
976976}
···12601260 }
12611261 /// State trait tracking which required fields have been set
12621262 pub trait State: sealed::Sealed {
12631263- type Type;
12641263 type Subject;
12641264+ type Type;
12651265 }
12661266 /// Empty state - all required fields are unset
12671267 pub struct Empty(());
12681268 impl sealed::Sealed for Empty {}
12691269 impl State for Empty {
12701270- type Type = Unset;
12711270 type Subject = Unset;
12721272- }
12731273- ///State transition - sets the `type` field to Set
12741274- pub struct SetType<S: State = Empty>(PhantomData<fn() -> S>);
12751275- impl<S: State> sealed::Sealed for SetType<S> {}
12761276- impl<S: State> State for SetType<S> {
12771277- type Type = Set<members::r#type>;
12781278- type Subject = S::Subject;
12711271+ type Type = Unset;
12791272 }
12801273 ///State transition - sets the `subject` field to Set
12811274 pub struct SetSubject<S: State = Empty>(PhantomData<fn() -> S>);
12821275 impl<S: State> sealed::Sealed for SetSubject<S> {}
12831276 impl<S: State> State for SetSubject<S> {
12841284- type Type = S::Type;
12851277 type Subject = Set<members::subject>;
12781278+ type Type = S::Type;
12791279+ }
12801280+ ///State transition - sets the `type` field to Set
12811281+ pub struct SetType<S: State = Empty>(PhantomData<fn() -> S>);
12821282+ impl<S: State> sealed::Sealed for SetType<S> {}
12831283+ impl<S: State> State for SetType<S> {
12841284+ type Subject = S::Subject;
12851285+ type Type = Set<members::r#type>;
12861286 }
12871287 /// Marker types for field names
12881288 #[allow(non_camel_case_types)]
12891289 pub mod members {
12901290- ///Marker type for the `type` field
12911291- pub struct r#type(());
12921290 ///Marker type for the `subject` field
12931291 pub struct subject(());
12921292+ ///Marker type for the `type` field
12931293+ pub struct r#type(());
12941294 }
12951295}
12961296···13631363impl<'a, S> SubfsBuilder<'a, S>
13641364where
13651365 S: subfs_state::State,
13661366- S::Type: subfs_state::IsSet,
13671366 S::Subject: subfs_state::IsSet,
13671367+ S::Type: subfs_state::IsSet,
13681368{
13691369 /// Build the final struct
13701370 pub fn build(self) -> Subfs<'a> {
+12-12
cli/src/pull.rs
···11use crate::blob_map;
22use crate::download;
33use crate::metadata::SiteMetadata;
44-use crate::place_wisp::fs::*;
44+use wisp_lexicons::place_wisp::fs::*;
55use crate::subfs_utils;
66use jacquard::CowStr;
77use jacquard::prelude::IdentityResolver;
···410410) -> miette::Result<Directory<'static>> {
411411 use jacquard_common::IntoStatic;
412412 use jacquard_common::types::value::from_data;
413413- use crate::place_wisp::subfs::SubfsRecord;
413413+ use wisp_lexicons::place_wisp::subfs::SubfsRecord;
414414415415- let mut all_subfs_map: HashMap<String, crate::place_wisp::subfs::Directory> = HashMap::new();
415415+ let mut all_subfs_map: HashMap<String, wisp_lexicons::place_wisp::subfs::Directory> = HashMap::new();
416416 let mut to_fetch = subfs_utils::extract_subfs_uris(directory, String::new());
417417418418 if to_fetch.is_empty() {
···516516517517/// Extract subfs URIs from a subfs::Directory (helper for pull)
518518fn extract_subfs_uris_from_subfs_dir(
519519- directory: &crate::place_wisp::subfs::Directory,
519519+ directory: &wisp_lexicons::place_wisp::subfs::Directory,
520520 current_path: String,
521521) -> Vec<(String, String)> {
522522 let mut uris = Vec::new();
···529529 };
530530531531 match &entry.node {
532532- crate::place_wisp::subfs::EntryNode::Subfs(subfs_node) => {
532532+ wisp_lexicons::place_wisp::subfs::EntryNode::Subfs(subfs_node) => {
533533 uris.push((subfs_node.subject.to_string(), full_path.clone()));
534534 }
535535- crate::place_wisp::subfs::EntryNode::Directory(subdir) => {
535535+ wisp_lexicons::place_wisp::subfs::EntryNode::Directory(subdir) => {
536536 let nested = extract_subfs_uris_from_subfs_dir(subdir, full_path);
537537 uris.extend(nested);
538538 }
···546546/// Recursively replace subfs nodes with their actual content
547547fn replace_subfs_with_content(
548548 directory: Directory,
549549- subfs_map: &HashMap<String, crate::place_wisp::subfs::Directory>,
549549+ subfs_map: &HashMap<String, wisp_lexicons::place_wisp::subfs::Directory>,
550550 current_path: String,
551551) -> Directory<'static> {
552552 use jacquard_common::IntoStatic;
···628628}
629629630630/// Convert a subfs entry to a fs entry (they have the same structure but different types)
631631-fn convert_subfs_entry_to_fs(subfs_entry: crate::place_wisp::subfs::Entry<'static>) -> Entry<'static> {
631631+fn convert_subfs_entry_to_fs(subfs_entry: wisp_lexicons::place_wisp::subfs::Entry<'static>) -> Entry<'static> {
632632 use jacquard_common::IntoStatic;
633633634634 let node = match subfs_entry.node {
635635- crate::place_wisp::subfs::EntryNode::File(file) => {
635635+ wisp_lexicons::place_wisp::subfs::EntryNode::File(file) => {
636636 EntryNode::File(Box::new(
637637 File::new()
638638 .r#type(file.r#type.into_static())
···643643 .build()
644644 ))
645645 }
646646- crate::place_wisp::subfs::EntryNode::Directory(dir) => {
646646+ wisp_lexicons::place_wisp::subfs::EntryNode::Directory(dir) => {
647647 let converted_entries: Vec<Entry<'static>> = dir
648648 .entries
649649 .into_iter()
···657657 .build()
658658 ))
659659 }
660660- crate::place_wisp::subfs::EntryNode::Subfs(_nested_subfs) => {
660660+ wisp_lexicons::place_wisp::subfs::EntryNode::Subfs(_nested_subfs) => {
661661 // Nested subfs should have been expanded already - if we get here, it means expansion failed
662662 // Treat it like a directory reference that should have been expanded
663663 eprintln!(" ⚠️ Warning: unexpanded nested subfs at path, treating as empty directory");
···668668 .build()
669669 ))
670670 }
671671- crate::place_wisp::subfs::EntryNode::Unknown(unknown) => {
671671+ wisp_lexicons::place_wisp::subfs::EntryNode::Unknown(unknown) => {
672672 EntryNode::Unknown(unknown)
673673 }
674674 };
···99 type MethodConfigOrHandler,
1010 createServer as createXrpcServer,
1111} from '@atproto/xrpc-server'
1212-import { schemas } from './lexicons'
1212+import { schemas } from './lexicons.js'
13131414export function createServer(options?: XrpcOptions): Server {
1515 return new Server(options)
+1-1
packages/@wisp/lexicons/src/lexicons.ts
···77 ValidationError,
88 type ValidationResult,
99} from '@atproto/lexicon'
1010-import { type $Typed, is$typed, maybe$typed } from './util'
1010+import { type $Typed, is$typed, maybe$typed } from './util.js'
11111212export const schemaDict = {
1313 PlaceWispFs: {