Monorepo for wisp.place. A static site hosting service built on top of the AT Protocol. wisp.place

update place.wisp.fs lexicon to support subfs nodes

Adds 'subfs' as a new node type in directory entries. A subfs node
contains a subject URI pointing to a place.wisp.subfs record that
holds the actual directory content.

This allows the main manifest to reference external records instead
of embedding large directory trees directly.

Changed files
+350 -17
hosting-service
src
lexicon
types
place
wisp
lexicons
src
lexicons
types
place
wisp
+135 -2
hosting-service/src/lexicon/lexicons.ts
···
blob: {
type: 'blob',
accept: ['*/*'],
-
maxSize: 1000000,
+
maxSize: 1000000000,
description: 'Content blob ref',
},
encoding: {
···
},
node: {
type: 'union',
-
refs: ['lex:place.wisp.fs#file', 'lex:place.wisp.fs#directory'],
+
refs: [
+
'lex:place.wisp.fs#file',
+
'lex:place.wisp.fs#directory',
+
'lex:place.wisp.fs#subfs',
+
],
+
},
+
},
+
},
+
subfs: {
+
type: 'object',
+
required: ['type', 'subject'],
+
properties: {
+
type: {
+
type: 'string',
+
const: 'subfs',
+
},
+
subject: {
+
type: 'string',
+
format: 'at-uri',
+
description:
+
'AT-URI pointing to a place.wisp.subfs record containing this subtree',
+
},
+
},
+
},
+
},
+
},
+
PlaceWispSubfs: {
+
lexicon: 1,
+
id: 'place.wisp.subfs',
+
defs: {
+
main: {
+
type: 'record',
+
description:
+
'Virtual filesystem manifest within a place.wisp.fs record',
+
record: {
+
type: 'object',
+
required: ['root', 'createdAt'],
+
properties: {
+
root: {
+
type: 'ref',
+
ref: 'lex:place.wisp.subfs#directory',
+
},
+
fileCount: {
+
type: 'integer',
+
minimum: 0,
+
maximum: 1000,
+
},
+
createdAt: {
+
type: 'string',
+
format: 'datetime',
+
},
+
},
+
},
+
},
+
file: {
+
type: 'object',
+
required: ['type', 'blob'],
+
properties: {
+
type: {
+
type: 'string',
+
const: 'file',
+
},
+
blob: {
+
type: 'blob',
+
accept: ['*/*'],
+
maxSize: 1000000000,
+
description: 'Content blob ref',
+
},
+
encoding: {
+
type: 'string',
+
enum: ['gzip'],
+
description: 'Content encoding (e.g., gzip for compressed files)',
+
},
+
mimeType: {
+
type: 'string',
+
description: 'Original MIME type before compression',
+
},
+
base64: {
+
type: 'boolean',
+
description:
+
'True if blob content is base64-encoded (used to bypass PDS content sniffing)',
+
},
+
},
+
},
+
directory: {
+
type: 'object',
+
required: ['type', 'entries'],
+
properties: {
+
type: {
+
type: 'string',
+
const: 'directory',
+
},
+
entries: {
+
type: 'array',
+
maxLength: 500,
+
items: {
+
type: 'ref',
+
ref: 'lex:place.wisp.subfs#entry',
+
},
+
},
+
},
+
},
+
entry: {
+
type: 'object',
+
required: ['name', 'node'],
+
properties: {
+
name: {
+
type: 'string',
+
maxLength: 255,
+
},
+
node: {
+
type: 'union',
+
refs: [
+
'lex:place.wisp.subfs#file',
+
'lex:place.wisp.subfs#directory',
+
'lex:place.wisp.subfs#subfs',
+
],
+
},
+
},
+
},
+
subfs: {
+
type: 'object',
+
required: ['type', 'subject'],
+
properties: {
+
type: {
+
type: 'string',
+
const: 'subfs',
+
},
+
subject: {
+
type: 'string',
+
format: 'at-uri',
+
description:
+
'AT-URI pointing to another place.wisp.subfs record for nested subtrees',
},
},
},
···
export const ids = {
PlaceWispFs: 'place.wisp.fs',
+
PlaceWispSubfs: 'place.wisp.subfs',
} as const
+31 -8
hosting-service/src/lexicon/types/place/wisp/fs.ts
···
* GENERATED CODE - DO NOT MODIFY
*/
import { type ValidationResult, BlobRef } from '@atproto/lexicon'
-
import { CID } from 'multiformats'
+
import { CID } from 'multiformats/cid'
import { validate as _validate } from '../../../lexicons'
import { type $Typed, is$typed as _is$typed, type OmitKey } from '../../../util'
···
validate = _validate
const id = 'place.wisp.fs'
-
export interface Record {
+
export interface Main {
$type: 'place.wisp.fs'
site: string
root: Directory
···
[k: string]: unknown
}
-
const hashRecord = 'main'
+
const hashMain = 'main'
-
export function isRecord<V>(v: V) {
-
return is$typed(v, id, hashRecord)
+
export function isMain<V>(v: V) {
+
return is$typed(v, id, hashMain)
}
-
export function validateRecord<V>(v: V) {
-
return validate<Record & V>(v, id, hashRecord, true)
+
export function validateMain<V>(v: V) {
+
return validate<Main & V>(v, id, hashMain, true)
+
}
+
+
export {
+
type Main as Record,
+
isMain as isRecord,
+
validateMain as validateRecord,
}
export interface File {
···
export interface Entry {
$type?: 'place.wisp.fs#entry'
name: string
-
node: $Typed<File> | $Typed<Directory> | { $type: string }
+
node: $Typed<File> | $Typed<Directory> | $Typed<Subfs> | { $type: string }
}
const hashEntry = 'entry'
···
export function validateEntry<V>(v: V) {
return validate<Entry & V>(v, id, hashEntry)
}
+
+
export interface Subfs {
+
$type?: 'place.wisp.fs#subfs'
+
type: 'subfs'
+
/** AT-URI pointing to a place.wisp.subfs record containing this subtree */
+
subject: string
+
}
+
+
const hashSubfs = 'subfs'
+
+
export function isSubfs<V>(v: V) {
+
return is$typed(v, id, hashSubfs)
+
}
+
+
export function validateSubfs<V>(v: V) {
+
return validate<Subfs & V>(v, id, hashSubfs)
+
}
+11 -4
lexicons/fs.json
···
"required": ["type", "blob"],
"properties": {
"type": { "type": "string", "const": "file" },
-
"blob": { "type": "blob", "accept": ["*/*"], "maxSize": 1000000, "description": "Content blob ref" },
+
"blob": { "type": "blob", "accept": ["*/*"], "maxSize": 1000000000, "description": "Content blob ref" },
"encoding": { "type": "string", "enum": ["gzip"], "description": "Content encoding (e.g., gzip for compressed files)" },
"mimeType": { "type": "string", "description": "Original MIME type before compression" },
-
"base64": { "type": "boolean", "description": "True if blob content is base64-encoded (used to bypass PDS content sniffing)" }
-
}
+
"base64": { "type": "boolean", "description": "True if blob content is base64-encoded (used to bypass PDS content sniffing)" } }
},
"directory": {
"type": "object",
···
"required": ["name", "node"],
"properties": {
"name": { "type": "string", "maxLength": 255 },
-
"node": { "type": "union", "refs": ["#file", "#directory"] }
+
"node": { "type": "union", "refs": ["#file", "#directory", "#subfs"] }
+
}
+
},
+
"subfs": {
+
"type": "object",
+
"required": ["type", "subject"],
+
"properties": {
+
"type": { "type": "string", "const": "subfs" },
+
"subject": { "type": "string", "format": "at-uri", "description": "AT-URI pointing to a place.wisp.subfs record containing this subtree" }
}
}
}
+149 -2
src/lexicons/lexicons.ts
···
blob: {
type: 'blob',
accept: ['*/*'],
-
maxSize: 1000000,
+
maxSize: 1000000000,
description: 'Content blob ref',
+
},
+
encoding: {
+
type: 'string',
+
enum: ['gzip'],
+
description: 'Content encoding (e.g., gzip for compressed files)',
+
},
+
mimeType: {
+
type: 'string',
+
description: 'Original MIME type before compression',
+
},
+
base64: {
+
type: 'boolean',
+
description:
+
'True if blob content is base64-encoded (used to bypass PDS content sniffing)',
},
},
},
···
},
node: {
type: 'union',
-
refs: ['lex:place.wisp.fs#file', 'lex:place.wisp.fs#directory'],
+
refs: [
+
'lex:place.wisp.fs#file',
+
'lex:place.wisp.fs#directory',
+
'lex:place.wisp.fs#subfs',
+
],
+
},
+
},
+
},
+
subfs: {
+
type: 'object',
+
required: ['type', 'subject'],
+
properties: {
+
type: {
+
type: 'string',
+
const: 'subfs',
+
},
+
subject: {
+
type: 'string',
+
format: 'at-uri',
+
description:
+
'AT-URI pointing to a place.wisp.subfs record containing this subtree',
+
},
+
},
+
},
+
},
+
},
+
PlaceWispSubfs: {
+
lexicon: 1,
+
id: 'place.wisp.subfs',
+
defs: {
+
main: {
+
type: 'record',
+
description:
+
'Virtual filesystem manifest within a place.wisp.fs record',
+
record: {
+
type: 'object',
+
required: ['root', 'createdAt'],
+
properties: {
+
root: {
+
type: 'ref',
+
ref: 'lex:place.wisp.subfs#directory',
+
},
+
fileCount: {
+
type: 'integer',
+
minimum: 0,
+
maximum: 1000,
+
},
+
createdAt: {
+
type: 'string',
+
format: 'datetime',
+
},
+
},
+
},
+
},
+
file: {
+
type: 'object',
+
required: ['type', 'blob'],
+
properties: {
+
type: {
+
type: 'string',
+
const: 'file',
+
},
+
blob: {
+
type: 'blob',
+
accept: ['*/*'],
+
maxSize: 1000000000,
+
description: 'Content blob ref',
+
},
+
encoding: {
+
type: 'string',
+
enum: ['gzip'],
+
description: 'Content encoding (e.g., gzip for compressed files)',
+
},
+
mimeType: {
+
type: 'string',
+
description: 'Original MIME type before compression',
+
},
+
base64: {
+
type: 'boolean',
+
description:
+
'True if blob content is base64-encoded (used to bypass PDS content sniffing)',
+
},
+
},
+
},
+
directory: {
+
type: 'object',
+
required: ['type', 'entries'],
+
properties: {
+
type: {
+
type: 'string',
+
const: 'directory',
+
},
+
entries: {
+
type: 'array',
+
maxLength: 500,
+
items: {
+
type: 'ref',
+
ref: 'lex:place.wisp.subfs#entry',
+
},
+
},
+
},
+
},
+
entry: {
+
type: 'object',
+
required: ['name', 'node'],
+
properties: {
+
name: {
+
type: 'string',
+
maxLength: 255,
+
},
+
node: {
+
type: 'union',
+
refs: [
+
'lex:place.wisp.subfs#file',
+
'lex:place.wisp.subfs#directory',
+
'lex:place.wisp.subfs#subfs',
+
],
+
},
+
},
+
},
+
subfs: {
+
type: 'object',
+
required: ['type', 'subject'],
+
properties: {
+
type: {
+
type: 'string',
+
const: 'subfs',
+
},
+
subject: {
+
type: 'string',
+
format: 'at-uri',
+
description:
+
'AT-URI pointing to another place.wisp.subfs record for nested subtrees',
},
},
},
···
export const ids = {
PlaceWispFs: 'place.wisp.fs',
+
PlaceWispSubfs: 'place.wisp.subfs',
} as const
+24 -1
src/lexicons/types/place/wisp/fs.ts
···
type: 'file'
/** Content blob ref */
blob: BlobRef
+
/** Content encoding (e.g., gzip for compressed files) */
+
encoding?: 'gzip'
+
/** Original MIME type before compression */
+
mimeType?: string
+
/** True if blob content is base64-encoded (used to bypass PDS content sniffing) */
+
base64?: boolean
}
const hashFile = 'file'
···
export interface Entry {
$type?: 'place.wisp.fs#entry'
name: string
-
node: $Typed<File> | $Typed<Directory> | { $type: string }
+
node: $Typed<File> | $Typed<Directory> | $Typed<Subfs> | { $type: string }
}
const hashEntry = 'entry'
···
export function validateEntry<V>(v: V) {
return validate<Entry & V>(v, id, hashEntry)
}
+
+
export interface Subfs {
+
$type?: 'place.wisp.fs#subfs'
+
type: 'subfs'
+
/** AT-URI pointing to a place.wisp.subfs record containing this subtree */
+
subject: string
+
}
+
+
const hashSubfs = 'subfs'
+
+
export function isSubfs<V>(v: V) {
+
return is$typed(v, id, hashSubfs)
+
}
+
+
export function validateSubfs<V>(v: V) {
+
return validate<Subfs & V>(v, id, hashSubfs)
+
}