Files
multi-simadmin/packages/contracts/src/organization.ts
T
chick f11877f13e feat(contracts): share notification, organization and device capability models
Move the Hub channel table, organization tags and device capability definitions
into the workspace packages so the control plane and the console validate the
same contract, and refresh the frozen upstream evidence for the new paths.
2026-09-05 18:52:54 +08:00

94 lines
2.7 KiB
TypeScript

export interface DeviceGroup {
readonly id: string;
readonly name: string;
readonly description: string;
readonly deviceCount: number;
readonly createdAt: string;
readonly updatedAt: string;
}
export interface DeviceGroupInput {
readonly name: string;
readonly description?: string;
}
export interface DeviceGroupPatch {
readonly name?: string;
readonly description?: string;
}
export interface DeviceTag {
readonly tag: string;
readonly color: string;
readonly deviceCount: number;
readonly createdAt: string;
readonly updatedAt: string;
}
export interface DeviceTagInput {
readonly tag: string;
readonly color?: string;
}
export interface DeviceTagPatch {
readonly color?: string;
}
export const TAG_COLORS = [
'',
'coral',
'turquoise',
'lime',
'yellow',
'pink',
'purple',
'blue',
] as const;
export type TagColor = (typeof TAG_COLORS)[number];
function text(value: unknown, field: string, maximum: number): string {
if (typeof value !== 'string') throw new TypeError(`${field} must be a string`);
const result = value.trim();
if (result.length > maximum)
throw new TypeError(`${field} must be at most ${maximum} characters`);
return result;
}
export function parseDeviceGroupInput(value: unknown): DeviceGroupInput {
const record = value as Record<string, unknown>;
const name = text(record?.name, 'name', 100);
if (!name) throw new TypeError('name must not be empty');
const description =
record?.description === undefined ? '' : text(record.description, 'description', 500);
return { name, description };
}
export function parseDeviceGroupPatch(value: unknown): DeviceGroupPatch {
const record = value as Record<string, unknown>;
const patch: { name?: string; description?: string } = {};
if (record?.name !== undefined) {
const name = text(record.name, 'name', 100);
if (!name) throw new TypeError('name must not be empty');
patch.name = name;
}
if (record?.description !== undefined)
patch.description = text(record.description, 'description', 500);
if (Object.keys(patch).length === 0) throw new TypeError('no group fields to update');
return patch;
}
export function parseDeviceTagInput(value: unknown): DeviceTagInput {
const record = value as Record<string, unknown>;
const tag = text(record?.tag, 'tag', 100);
if (!tag) throw new TypeError('tag must not be empty');
const color = record?.color === undefined ? '' : text(record.color, 'color', 32);
return { tag, color };
}
export function parseDeviceTagPatch(value: unknown): DeviceTagPatch {
const record = value as Record<string, unknown>;
if (record?.color === undefined) throw new TypeError('no tag fields to update');
return { color: text(record.color, 'color', 32) };
}