@@ -10,7 +10,8 @@ import {
1010 getGitDir ,
1111 isDirty as isDirtyFunc ,
1212} from "./buildInfo" ;
13- import { Metadata , Post } from "@/lib/posts" ;
13+ import { PostMetadata , Post } from "@/lib/posts" ;
14+ import { parseTimestamp , unwrap } from "@/lib/utils"
1415
1516export function getCommitInfoForFileOrFallback ( filepath : string ) {
1617 const firstCommit = getCommitEntryForFile ( filepath , false ) ;
@@ -20,14 +21,14 @@ export function getCommitInfoForFileOrFallback(filepath: string) {
2021 return { isDirty, firstCommit, currentCommit } ;
2122 }
2223
23- const nowSeconds = Math . floor ( Date . now ( ) / 1000 ) . toString ( ) ;
24+ const now = new Date ( )
2425 const fallbackCommit = {
2526 commitHash : "UNCOMMITTED" ,
2627 get shortCommitHash ( ) {
2728 return this . commitHash . slice ( 0 , 7 ) ;
2829 } ,
2930 author : "Michael Fortunato (unverified)" ,
30- timestamp : nowSeconds ,
31+ timestamp : now ,
3132 message : "Uncommited/non-existant file" ,
3233 } ;
3334
@@ -46,11 +47,34 @@ function isRecord(v: unknown): v is Record<string, unknown> {
4647 return typeof v === "object" && v !== null ;
4748}
4849
49- function asString ( v : unknown ) : string | undefined {
50+ function asString ( v : unknown ) : string | null {
5051 if ( typeof v === "string" ) return v ;
5152 if ( isRecord ( v ) && typeof v . value === "string" ) return v . value ;
5253 if ( isRecord ( v ) && typeof v . text === "string" ) return v . text ;
53- return undefined ;
54+ return null ;
55+ }
56+
57+ function asNumber ( v : unknown ) : number | null {
58+ if ( typeof v === "number" ) return Number ( v ) ;
59+ return null ;
60+ }
61+
62+ function asBoolean ( v : unknown ) : boolean | null {
63+ if ( typeof v === "boolean" ) return v ;
64+ // There must be a better way than this...
65+ const vs = asString ( v )
66+ if ( vs === null ) {
67+ return null
68+ }
69+ const t = vs . trim ( ) . toLowerCase ( ) ;
70+ if ( t === "true" ) return true ;
71+ if ( t === "false" ) return false ;
72+ return null ;
73+ }
74+
75+ function asTimestamp ( v : unknown ) : Date | null {
76+ const ms_since_unix_epoch = asNumber ( v )
77+ return parseTimestamp ( ms_since_unix_epoch )
5478}
5579
5680function asStringArray ( v : unknown ) : string [ ] {
@@ -89,7 +113,10 @@ function findLabelValue(items: unknown[], ...labels: string[]): unknown {
89113// --- typst query + parse ---
90114export async function _typstQuery ( typstFilepath : string ) : Promise < unknown [ ] > {
91115 const TYPST_QUERY =
92- "selector(<KEYWORDS>).or(<keywords>).or(<tags>).or(<TITLE>).or(<title>)" ;
116+ "selector(<KEYWORDS>).or(<keywords>)"
117+ + ".or(<tags>)"
118+ + ".or(<TITLE>).or(<title>)"
119+ + ".or(<CREATED_TIMESTAMP>).or(<MODIFIED_TIMESTAMP>)" ;
93120 const root = await getGitDir ( ) ;
94121 const { stdout } = await execFileAsync ( "typst" , [
95122 "query" ,
@@ -154,6 +181,8 @@ export async function _typstFileToMetadata(typstFilepath: string) {
154181 id : ( await idFromPostPath ( typstFilepath ) ) || "<UNKNOWN ID>" ,
155182 title : asString ( findLabelValue ( items , "TITLE" ) ) || "<UNKNOWN TITLE>" ,
156183 tags : asStringArray ( findLabelValue ( items , "KEYWORDS" , "TAGS" ) ) ,
184+ createdTimestamp : asTimestamp ( findLabelValue ( items , "CREATED_TIMESTAMP" ) ) ,
185+ modifiedTimestamp : asTimestamp ( findLabelValue ( items , "MODIFIED_TIMESTAMP" ) ) ,
157186 } ;
158187}
159188function splitHeadBody ( html : string ) {
@@ -207,15 +236,17 @@ export async function listPostIds(): Promise<string[]> {
207236 return Promise . all ( files . map ( ( file ) => idFromPostPath ( file ) ) ) ;
208237}
209238
210- export async function listPosts ( ) : Promise < Metadata [ ] > {
239+ export async function listPosts ( ) : Promise < PostMetadata [ ] > {
211240 const postFiles = await listPostFiles ( ) ;
212241 const postsWithMetadata = await Promise . all (
213242 postFiles . map ( async ( postFile ) => {
214- const { id, title, tags } = await _typstFileToMetadata ( postFile ) ;
243+ const { id, title, tags, createdTimestamp , modifiedTimestamp } = await _typstFileToMetadata ( postFile ) ;
215244 const buildInfo = getCommitInfoForFileOrFallback ( postFile ) ;
216- return new Metadata ( {
245+ return new PostMetadata ( {
217246 id,
218247 title,
248+ createdTimestamp,
249+ modifiedTimestamp,
219250 tags,
220251 buildInfo,
221252 } ) ;
@@ -227,9 +258,9 @@ export async function listPosts(): Promise<Metadata[]> {
227258export async function buildPost ( inputFilepath : string ) : Promise < Post > {
228259 const htmlString = await _typstFileToHTMLFile ( inputFilepath ) ;
229260 const headAndBody = splitHeadBody ( htmlString ) ;
230- const { id, title, tags } = await _typstFileToMetadata ( inputFilepath ) ;
261+ const { id, title, tags, createdTimestamp , modifiedTimestamp } = await _typstFileToMetadata ( inputFilepath ) ;
231262 const buildInfo = getCommitInfoForFileOrFallback ( inputFilepath ) ;
232- const metadata = new Metadata ( { id, title, tags, buildInfo } ) ;
263+ const metadata = new PostMetadata ( { id, title, createdTimestamp , modifiedTimestamp , tags, buildInfo } ) ;
233264 return {
234265 content : headAndBody ,
235266 metadata,
0 commit comments