@struktoai/mirage-core
Version:
A Unified Virtual Filesystem For AI Agents
88 lines • 3.58 kB
JavaScript
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
import { z } from 'zod';
import { ContentType, FileType } from "../../types.js";
import { parseConfigWithSchema, refuseRepeatedFields } from "../../vfs/secrets.js";
export const LookupStatus = Object.freeze({
EXPIRED: 'expired',
NOT_FOUND: 'not_found',
});
export const IndexType = Object.freeze({
RAM: 'ram',
REDIS: 'redis',
});
/**
* The wire form of a stat row: what pydantic writes for the Python
* `FileStat`, which `JSON.stringify` of a `FileStat` already matches field
* for field and in order.
*/
export const FileStatWireSchema = z.object({
name: z.string(),
size: z.number().int().nullable().default(null),
modified: z.string().nullable().default(null),
fingerprint: z.string().nullable().default(null),
revision: z.string().nullable().default(null),
type: z.enum(Object.values(FileType)),
content: z
.enum(Object.values(ContentType))
.nullable()
.default(null),
mode: z.number().int().nullable().default(null),
uid: z.union([z.number(), z.string()]).nullable().default(null),
gid: z.union([z.number(), z.string()]).nullable().default(null),
atime: z.string().nullable().default(null),
ctime: z.string().nullable().default(null),
birthtime: z.string().nullable().default(null),
dev: z.number().int().nullable().default(null),
extra: z.record(z.string(), z.unknown()).default({}),
});
/** The directory row, the twin of the pydantic `IndexDirectory`. */
export const IndexDirectory = z.object({
entries: z.array(z.string()),
folders: z.array(z.number().int()),
expires_at: z.number(),
generation: z.string(),
partial: z.boolean().default(false),
version: z.string().nullable().default(null),
window: z.boolean().default(false),
});
const IndexConfig = z.object({
type: z.enum(IndexType).optional(),
ttl: z.number().optional(),
});
const RedisIndexConfig = IndexConfig.extend({
url: z.string().optional(),
// Absent scopes the keys to the workspace; a prefix shares them with
// every workspace that names it.
keyPrefix: z.string().optional(),
});
/**
* Check an index config the way python's `IndexConfig` checks one on
* construction, and camelize its keys.
*
* The fields are picked by `type`, where python picks them by class, since
* a TS config has no class to pick by. One field named in both spellings is
* refused, as python refuses the camelCase one.
*
* @param config the index config as the caller passed it.
* @returns the checked config with every key in its camelCase spelling.
*/
export function normalizeIndexConfig(config) {
const input = config;
refuseRepeatedFields(input);
return (input.type ?? IndexType.RAM) === IndexType.RAM
? parseConfigWithSchema(IndexConfig, input)
: parseConfigWithSchema(RedisIndexConfig, input);
}
//# sourceMappingURL=config.js.map