@arqel-dev/types 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Arqel Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,40 @@
1
+ # @arqel-dev/types
2
+
3
+ [![License](https://img.shields.io/badge/license-MIT-blue.svg)](../../LICENSE)
4
+ [![TypeScript](https://img.shields.io/badge/typescript-%5E5.6-3178c6.svg)](https://www.typescriptlang.org)
5
+ [![Status](https://img.shields.io/badge/status-pre--alpha-orange.svg)](#)
6
+
7
+ Shared TypeScript types for the [Arqel](https://arqel.dev) ecosystem — fields, resources, tables, forms, actions, and Inertia shared props.
8
+
9
+ ## Status
10
+
11
+ 🚧 **Pre-alpha** — covers all serialiser shapes from the PHP packages.
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ pnpm add -D @arqel-dev/types
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ ```ts
22
+ // Tree-shake-friendly subpath imports
23
+ import type { FieldSchema, isFieldType } from '@arqel-dev/types/fields';
24
+ import type { ResourceIndexProps, RecordType } from '@arqel-dev/types/resources';
25
+ import type { SharedProps } from '@arqel-dev/types/inertia';
26
+
27
+ // Or barrel
28
+ import type { FieldSchema, ColumnSchema, FormSchema } from '@arqel-dev/types';
29
+ ```
30
+
31
+ ## Convenções
32
+
33
+ - Zero runtime — só types + 4 type guards (`isFieldType`, `isFieldEntry`, `isLayoutEntry`, `resolveFieldEntry`)
34
+ - `sideEffects: false` para máximo tree-shaking
35
+ - Synced com `Arqel\Core\Support\FieldSchemaSerializer` PHP
36
+
37
+ ## Links
38
+
39
+ - [Documentação](https://arqel.dev/docs/types) — em construção
40
+ - [PLANNING](../../PLANNING/08-fase-1-mvp.md) — tickets `TYPES-*`
package/SKILL.md ADDED
@@ -0,0 +1,121 @@
1
+ # SKILL.md — @arqel-dev/types
2
+
3
+ > Contexto canónico para AI agents. Estrutura conforme `PLANNING/04-repo-structure.md` §11.
4
+
5
+ ## Purpose
6
+
7
+ `@arqel-dev/types` é o pacote base de TypeScript types compartilhados por todos os outros pacotes JS do ecossistema Arqel. Materializa em TS o que o PHP serializa (Fields, Tables, Forms, Actions, Resource payloads, Inertia shared props) — um único source of truth para o React side consumir sem reshape.
8
+
9
+ **Zero runtime** — apenas types + 4 type guards:
10
+ - `isFieldType(field, type)` — narrow `FieldSchema` por tipo discriminator
11
+ - `isFieldEntry(entry)` / `isLayoutEntry(entry)` — narrow `FormSchema` schema entries
12
+ - `resolveFieldEntry(entry, fields)` — lookup de `FieldEntry` no array flat de Fields
13
+
14
+ ## Status
15
+
16
+ **Entregue (TYPES-001..002, 004 parcial):**
17
+
18
+ - Pacote npm `@arqel-dev/types` com 7 entry points (`./`, `./fields`, `./resources`, `./tables`, `./forms`, `./actions`, `./inertia`)
19
+ - `tsup` build com `dts: true`, ESM, sourcemaps, tree-shake friendly (`sideEffects: false`)
20
+ - 21 Field types em discriminated union sobre `type`
21
+ - 9 Column types + 6 Filter types em discriminated unions
22
+ - 7 Layout components (Section/Fieldset/Grid/Columns/Group/Tabs/Tab)
23
+ - ResourceMeta, PaginationMeta, ResourceIndex/Create/Edit/Detail props (genéricos sobre `RecordType`)
24
+ - ActionSchema com discriminated `type`, ConfirmationConfig, ActionFormField
25
+ - SharedProps (Inertia) com auth/panel/tenant/flash/translations/arqel
26
+ - Vitest + expect-type para type-level assertions
27
+ - 19+ type-level assertions passando
28
+
29
+ **Por chegar:**
30
+
31
+ - Integração documentada com `spatie/laravel-typescript-transformer` (TYPES-003)
32
+
33
+ ## Key Contracts
34
+
35
+ ### FieldSchema (discriminated union)
36
+
37
+ ```ts
38
+ import { type FieldSchema, isFieldType } from '@arqel-dev/types/fields';
39
+
40
+ function render(field: FieldSchema) {
41
+ if (isFieldType(field, 'belongsTo')) {
42
+ // field.props.relatedResource é string (narrowed)
43
+ return <BelongsToInput resource={field.props.relatedResource} />;
44
+ }
45
+
46
+ if (isFieldType(field, 'select')) {
47
+ // field.props.options é SelectOption[] | Record<string, string>
48
+ return <SelectInput options={field.props.options} />;
49
+ }
50
+
51
+ return <TextInput field={field} />;
52
+ }
53
+ ```
54
+
55
+ Cada campo carrega `validation`, `visibility`, `dependsOn` e `props` (type-specific).
56
+
57
+ ### FormSchema + SchemaEntry
58
+
59
+ ```ts
60
+ import { isLayoutEntry, isFieldEntry, type FormSchema } from '@arqel-dev/types/forms';
61
+
62
+ function renderSchema(form: FormSchema, fields: FieldSchema[]) {
63
+ return form.schema.map((entry) => {
64
+ if (isLayoutEntry(entry)) {
65
+ // entry.type narrows entre 'section'|'fieldset'|...
66
+ return <LayoutComponent {...entry} />;
67
+ }
68
+
69
+ const resolved = resolveFieldEntry(entry, fields);
70
+ return resolved ? <FieldRenderer field={resolved} /> : null;
71
+ });
72
+ }
73
+ ```
74
+
75
+ ### Resource page props
76
+
77
+ ```ts
78
+ import type { ResourceIndexProps, RecordType } from '@arqel-dev/types/resources';
79
+
80
+ interface User extends RecordType {
81
+ id: number;
82
+ email: string;
83
+ }
84
+
85
+ export default function UsersIndex({ resource, records, columns }: ResourceIndexProps<User>) {
86
+ // records é User[]; columns é ColumnSchema[]
87
+ }
88
+ ```
89
+
90
+ ### Inertia SharedProps
91
+
92
+ ```ts
93
+ import type { SharedProps } from '@arqel-dev/types/inertia';
94
+ import { usePage } from '@inertiajs/react';
95
+
96
+ const { auth, flash } = usePage<SharedProps>().props;
97
+ // auth.user: AuthUserPayload | null
98
+ // auth.can.viewAdminPanel: boolean | undefined
99
+ ```
100
+
101
+ ## Conventions
102
+
103
+ - **Subpath imports** preferidos sobre o barrel para tree-shaking (`@arqel-dev/types/fields` em vez de `@arqel-dev/types`)
104
+ - Discriminated unions usam `type` como discriminator — usa `isFieldType` etc. para narrow
105
+ - `RecordType` é loose por defeito — apps com `spatie/laravel-typescript-transformer` (TYPES-003) substituem por interfaces strict
106
+ - Mantém-se sync com `Arqel\Core\Support\FieldSchemaSerializer` PHP — divergência causa bugs de runtime no React
107
+
108
+ ## Anti-patterns
109
+
110
+ - ❌ **Casts (`as FieldSchema`)** — usa type guards. Se o PHP emitir um shape inválido, prefere falhar cedo
111
+ - ❌ **Manual reshape** dos payloads no React — o serializer já produz o shape canônico
112
+ - ❌ **`any` em vez de `unknown`** para `defaultValue` / `tenant` — mantém safety, força narrow
113
+ - ❌ **Duplicar Field types** em `@arqel-dev/fields` (UI side) — aquele pacote consome estes types
114
+ - ❌ **Importar `dist/`** diretamente — usa apenas exports `./*` declarados em `package.json`
115
+
116
+ ## Related
117
+
118
+ - Tickets: [`PLANNING/08-fase-1-mvp.md`](../../PLANNING/08-fase-1-mvp.md) §TYPES-001..004
119
+ - API: [`PLANNING/06-api-react.md`](../../PLANNING/06-api-react.md) §3-7
120
+ - Source: [`packages-js/types/src/`](src/)
121
+ - Tests: [`packages-js/types/tests/`](tests/)
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Action schema mirroring `arqel-dev/actions` PHP serialisation.
3
+ *
4
+ * The shape is the output of `Action::toArray()` after PHP's
5
+ * `array_filter(... !== null)` — so optional fields are simply
6
+ * absent from the payload rather than `null`.
7
+ */
8
+ type ActionType = 'row' | 'bulk' | 'toolbar' | 'header';
9
+ type ActionColor = 'primary' | 'secondary' | 'destructive' | 'success' | 'warning' | 'info';
10
+ type ActionVariant = 'default' | 'outline' | 'ghost' | 'destructive';
11
+ type ActionMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
12
+ type ConfirmationColor = 'destructive' | 'warning' | 'info';
13
+ /**
14
+ * Modal config emitted when an Action requires confirmation.
15
+ */
16
+ interface ConfirmationConfig {
17
+ /** Modal heading. */
18
+ heading?: string;
19
+ /** Optional explanation rendered under the heading. */
20
+ description?: string;
21
+ /** Heroicon name. */
22
+ icon?: string;
23
+ /** Visual variant. Defaults to `destructive`. */
24
+ color?: ConfirmationColor;
25
+ /** Force the user to type this exact text before confirming. */
26
+ requiresText?: string;
27
+ /** Override for the submit button label. */
28
+ submitLabel?: string;
29
+ /** Override for the cancel button label. */
30
+ cancelLabel?: string;
31
+ }
32
+ type ModalSize = 'sm' | 'md' | 'lg' | 'xl' | 'full';
33
+ /**
34
+ * Field schema entry used by Action form modals (see `arqel-dev/actions`
35
+ * `HasForm`). Action form fields are intentionally a flat list —
36
+ * layout components live in regular `Form` schemas.
37
+ */
38
+ interface ActionFormField {
39
+ name: string;
40
+ type: string;
41
+ }
42
+ /**
43
+ * Action shape consumed by the React renderer (`<ActionButton>`,
44
+ * `<ActionMenu>`, `<ActionFormModal>`).
45
+ */
46
+ interface ActionSchema {
47
+ name: string;
48
+ type: ActionType;
49
+ label: string;
50
+ icon?: string;
51
+ color: ActionColor;
52
+ variant: ActionVariant;
53
+ method: ActionMethod;
54
+ /** Resolved URL for link-mode actions; absent for callbacks. */
55
+ url?: string;
56
+ tooltip?: string;
57
+ /** True when the action is disabled per-record. */
58
+ disabled?: true;
59
+ /** True when the action requires confirmation. */
60
+ requiresConfirmation?: true;
61
+ /** Modal config; absent when confirmation is not required. */
62
+ confirmation?: ConfirmationConfig;
63
+ /** Form modal field schema; absent when the action has no form. */
64
+ form?: ActionFormField[];
65
+ /** Modal size for form actions. */
66
+ modalSize?: ModalSize;
67
+ /** Flash message shown on successful execution. */
68
+ successNotification?: string;
69
+ /** Flash message shown when the callback throws. */
70
+ failureNotification?: string;
71
+ }
72
+ /**
73
+ * Server-side action collections for a Resource (`Resource::actions`,
74
+ * `bulkActions`, `toolbarActions`, `headerActions`).
75
+ */
76
+ interface ResourceActions {
77
+ row: ActionSchema[];
78
+ bulk: ActionSchema[];
79
+ toolbar: ActionSchema[];
80
+ }
81
+
82
+ export type { ActionColor, ActionFormField, ActionMethod, ActionSchema, ActionType, ActionVariant, ConfirmationColor, ConfirmationConfig, ModalSize, ResourceActions };
@@ -0,0 +1,3 @@
1
+
2
+ //# sourceMappingURL=actions.js.map
3
+ //# sourceMappingURL=actions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"actions.js"}
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Field schema mirroring `Arqel\Core\Support\FieldSchemaSerializer`
3
+ * output. The discriminated union narrows on `type`; per-type
4
+ * `props` carry the type-specific metadata emitted by each Field's
5
+ * `getTypeSpecificProps()`.
6
+ */
7
+ type FieldType = 'text' | 'textarea' | 'email' | 'url' | 'password' | 'slug' | 'number' | 'currency' | 'boolean' | 'toggle' | 'select' | 'multiSelect' | 'radio' | 'belongsTo' | 'hasMany' | 'date' | 'dateTime' | 'file' | 'image' | 'color' | 'hidden';
8
+ interface FieldValidation {
9
+ rules: string[];
10
+ messages: Record<string, string>;
11
+ attribute: string | null;
12
+ }
13
+ /**
14
+ * Per-context visibility flags (mirrors `HasVisibility::isVisibleIn`).
15
+ * `canSee` is the per-user/record gate from `HasAuthorization`.
16
+ */
17
+ interface FieldVisibility {
18
+ create: boolean;
19
+ edit: boolean;
20
+ detail: boolean;
21
+ table: boolean;
22
+ canSee: boolean;
23
+ }
24
+ /**
25
+ * Common shape every Field carries before the discriminated `props`.
26
+ */
27
+ interface FieldBase<TType extends FieldType, TProps> {
28
+ type: TType;
29
+ name: string;
30
+ label: string | null;
31
+ component: string | null;
32
+ required: boolean;
33
+ readonly: boolean;
34
+ disabled: boolean;
35
+ placeholder: string | null;
36
+ helperText: string | null;
37
+ defaultValue: unknown;
38
+ columnSpan: number | string;
39
+ live: boolean;
40
+ liveDebounce: number | null;
41
+ validation: FieldValidation;
42
+ visibility: FieldVisibility;
43
+ dependsOn: string[];
44
+ props: TProps;
45
+ }
46
+ interface TextFieldProps {
47
+ maxLength?: number;
48
+ minLength?: number;
49
+ pattern?: string;
50
+ autocomplete?: string;
51
+ mask?: string;
52
+ }
53
+ interface NumberFieldProps {
54
+ min?: number;
55
+ max?: number;
56
+ step?: number | string;
57
+ integer?: boolean;
58
+ decimals?: number;
59
+ }
60
+ interface CurrencyFieldProps extends NumberFieldProps {
61
+ prefix: string;
62
+ suffix?: string;
63
+ thousandsSeparator: string;
64
+ decimalSeparator: string;
65
+ }
66
+ interface BooleanFieldProps {
67
+ inline?: boolean;
68
+ }
69
+ interface ToggleFieldProps {
70
+ onLabel?: string;
71
+ offLabel?: string;
72
+ }
73
+ interface SelectOption {
74
+ value: string | number;
75
+ label: string;
76
+ }
77
+ interface SelectFieldProps {
78
+ options: SelectOption[] | Record<string, string>;
79
+ searchable?: boolean;
80
+ multiple?: boolean;
81
+ placeholder?: string;
82
+ }
83
+ interface MultiSelectFieldProps extends SelectFieldProps {
84
+ multiple: true;
85
+ }
86
+ interface RadioFieldProps {
87
+ options: SelectOption[];
88
+ inline?: boolean;
89
+ }
90
+ interface BelongsToFieldProps {
91
+ relatedResource: string;
92
+ relationship: string;
93
+ searchable: boolean;
94
+ searchColumns: string[];
95
+ preload: boolean;
96
+ /** Set client-side from the panel routes. */
97
+ searchRoute?: string;
98
+ }
99
+ interface HasManyFieldProps {
100
+ relatedResource: string;
101
+ relationship: string;
102
+ canAddRecords?: boolean;
103
+ canEditRecords?: boolean;
104
+ }
105
+ interface DateFieldProps {
106
+ format: string;
107
+ displayFormat: string;
108
+ minDate?: string;
109
+ maxDate?: string;
110
+ closeOnDateSelection?: boolean;
111
+ timezone?: string;
112
+ }
113
+ interface DateTimeFieldProps extends DateFieldProps {
114
+ seconds?: boolean;
115
+ }
116
+ interface FileFieldProps {
117
+ disk: string;
118
+ directory?: string;
119
+ visibility?: 'public' | 'private';
120
+ maxSize?: number;
121
+ acceptedFileTypes?: string[];
122
+ multiple?: boolean;
123
+ reorderable?: boolean;
124
+ strategy?: string;
125
+ /** Set client-side from the panel routes. */
126
+ uploadRoute?: string;
127
+ }
128
+ interface ImageFieldProps extends FileFieldProps {
129
+ aspectRatio?: number;
130
+ crop?: boolean;
131
+ }
132
+ type ColorFormat = 'hex' | 'rgb' | 'hsl';
133
+ interface ColorFieldProps {
134
+ presets?: string[];
135
+ format?: ColorFormat;
136
+ alpha?: boolean;
137
+ }
138
+ interface SlugFieldProps extends TextFieldProps {
139
+ reservedSlugs?: string[];
140
+ }
141
+ type TextFieldSchema = FieldBase<'text', TextFieldProps>;
142
+ type TextareaFieldSchema = FieldBase<'textarea', TextFieldProps>;
143
+ type EmailFieldSchema = FieldBase<'email', TextFieldProps>;
144
+ type UrlFieldSchema = FieldBase<'url', TextFieldProps>;
145
+ type PasswordFieldSchema = FieldBase<'password', TextFieldProps>;
146
+ type SlugFieldSchema = FieldBase<'slug', SlugFieldProps>;
147
+ type NumberFieldSchema = FieldBase<'number', NumberFieldProps>;
148
+ type CurrencyFieldSchema = FieldBase<'currency', CurrencyFieldProps>;
149
+ type BooleanFieldSchema = FieldBase<'boolean', BooleanFieldProps>;
150
+ type ToggleFieldSchema = FieldBase<'toggle', ToggleFieldProps>;
151
+ type SelectFieldSchema = FieldBase<'select', SelectFieldProps>;
152
+ type MultiSelectFieldSchema = FieldBase<'multiSelect', MultiSelectFieldProps>;
153
+ type RadioFieldSchema = FieldBase<'radio', RadioFieldProps>;
154
+ type BelongsToFieldSchema = FieldBase<'belongsTo', BelongsToFieldProps>;
155
+ type HasManyFieldSchema = FieldBase<'hasMany', HasManyFieldProps>;
156
+ type DateFieldSchema = FieldBase<'date', DateFieldProps>;
157
+ type DateTimeFieldSchema = FieldBase<'dateTime', DateTimeFieldProps>;
158
+ type FileFieldSchema = FieldBase<'file', FileFieldProps>;
159
+ type ImageFieldSchema = FieldBase<'image', ImageFieldProps>;
160
+ type ColorFieldSchema = FieldBase<'color', ColorFieldProps>;
161
+ type HiddenFieldSchema = FieldBase<'hidden', Record<string, never>>;
162
+ type FieldSchema = TextFieldSchema | TextareaFieldSchema | EmailFieldSchema | UrlFieldSchema | PasswordFieldSchema | SlugFieldSchema | NumberFieldSchema | CurrencyFieldSchema | BooleanFieldSchema | ToggleFieldSchema | SelectFieldSchema | MultiSelectFieldSchema | RadioFieldSchema | BelongsToFieldSchema | HasManyFieldSchema | DateFieldSchema | DateTimeFieldSchema | FileFieldSchema | ImageFieldSchema | ColorFieldSchema | HiddenFieldSchema;
163
+ declare function isFieldType<T extends FieldType>(field: FieldSchema, type: T): field is Extract<FieldSchema, {
164
+ type: T;
165
+ }>;
166
+
167
+ export { type BelongsToFieldProps, type BelongsToFieldSchema, type BooleanFieldProps, type BooleanFieldSchema, type ColorFieldProps, type ColorFieldSchema, type ColorFormat, type CurrencyFieldProps, type CurrencyFieldSchema, type DateFieldProps, type DateFieldSchema, type DateTimeFieldProps, type DateTimeFieldSchema, type EmailFieldSchema, type FieldSchema, type FieldType, type FieldValidation, type FieldVisibility, type FileFieldProps, type FileFieldSchema, type HasManyFieldProps, type HasManyFieldSchema, type HiddenFieldSchema, type ImageFieldProps, type ImageFieldSchema, type MultiSelectFieldProps, type MultiSelectFieldSchema, type NumberFieldProps, type NumberFieldSchema, type PasswordFieldSchema, type RadioFieldProps, type RadioFieldSchema, type SelectFieldProps, type SelectFieldSchema, type SelectOption, type SlugFieldProps, type SlugFieldSchema, type TextFieldProps, type TextFieldSchema, type TextareaFieldSchema, type ToggleFieldProps, type ToggleFieldSchema, type UrlFieldSchema, isFieldType };
package/dist/fields.js ADDED
@@ -0,0 +1,8 @@
1
+ // src/fields.ts
2
+ function isFieldType(field, type) {
3
+ return field.type === type;
4
+ }
5
+
6
+ export { isFieldType };
7
+ //# sourceMappingURL=fields.js.map
8
+ //# sourceMappingURL=fields.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/fields.ts"],"names":[],"mappings":";AA2OO,SAAS,WAAA,CACd,OACA,IAAA,EAC4C;AAC5C,EAAA,OAAO,MAAM,IAAA,KAAS,IAAA;AACxB","file":"fields.js","sourcesContent":["/**\n * Field schema mirroring `Arqel\\Core\\Support\\FieldSchemaSerializer`\n * output. The discriminated union narrows on `type`; per-type\n * `props` carry the type-specific metadata emitted by each Field's\n * `getTypeSpecificProps()`.\n */\n\nexport type FieldType =\n | 'text'\n | 'textarea'\n | 'email'\n | 'url'\n | 'password'\n | 'slug'\n | 'number'\n | 'currency'\n | 'boolean'\n | 'toggle'\n | 'select'\n | 'multiSelect'\n | 'radio'\n | 'belongsTo'\n | 'hasMany'\n | 'date'\n | 'dateTime'\n | 'file'\n | 'image'\n | 'color'\n | 'hidden';\n\nexport interface FieldValidation {\n rules: string[];\n messages: Record<string, string>;\n attribute: string | null;\n}\n\n/**\n * Per-context visibility flags (mirrors `HasVisibility::isVisibleIn`).\n * `canSee` is the per-user/record gate from `HasAuthorization`.\n */\nexport interface FieldVisibility {\n create: boolean;\n edit: boolean;\n detail: boolean;\n table: boolean;\n canSee: boolean;\n}\n\n/**\n * Common shape every Field carries before the discriminated `props`.\n */\ninterface FieldBase<TType extends FieldType, TProps> {\n type: TType;\n name: string;\n label: string | null;\n component: string | null;\n required: boolean;\n readonly: boolean;\n disabled: boolean;\n placeholder: string | null;\n helperText: string | null;\n defaultValue: unknown;\n columnSpan: number | string;\n live: boolean;\n liveDebounce: number | null;\n validation: FieldValidation;\n visibility: FieldVisibility;\n dependsOn: string[];\n props: TProps;\n}\n\n/* ─── Per-type props ────────────────────────────────────────────── */\n\nexport interface TextFieldProps {\n maxLength?: number;\n minLength?: number;\n pattern?: string;\n autocomplete?: string;\n mask?: string;\n}\n\nexport interface NumberFieldProps {\n min?: number;\n max?: number;\n step?: number | string;\n integer?: boolean;\n decimals?: number;\n}\n\nexport interface CurrencyFieldProps extends NumberFieldProps {\n prefix: string;\n suffix?: string;\n thousandsSeparator: string;\n decimalSeparator: string;\n}\n\nexport interface BooleanFieldProps {\n inline?: boolean;\n}\n\nexport interface ToggleFieldProps {\n onLabel?: string;\n offLabel?: string;\n}\n\nexport interface SelectOption {\n value: string | number;\n label: string;\n}\n\nexport interface SelectFieldProps {\n options: SelectOption[] | Record<string, string>;\n searchable?: boolean;\n multiple?: boolean;\n placeholder?: string;\n}\n\nexport interface MultiSelectFieldProps extends SelectFieldProps {\n multiple: true;\n}\n\nexport interface RadioFieldProps {\n options: SelectOption[];\n inline?: boolean;\n}\n\nexport interface BelongsToFieldProps {\n relatedResource: string;\n relationship: string;\n searchable: boolean;\n searchColumns: string[];\n preload: boolean;\n /** Set client-side from the panel routes. */\n searchRoute?: string;\n}\n\nexport interface HasManyFieldProps {\n relatedResource: string;\n relationship: string;\n canAddRecords?: boolean;\n canEditRecords?: boolean;\n}\n\nexport interface DateFieldProps {\n format: string;\n displayFormat: string;\n minDate?: string;\n maxDate?: string;\n closeOnDateSelection?: boolean;\n timezone?: string;\n}\n\nexport interface DateTimeFieldProps extends DateFieldProps {\n seconds?: boolean;\n}\n\nexport interface FileFieldProps {\n disk: string;\n directory?: string;\n visibility?: 'public' | 'private';\n maxSize?: number;\n acceptedFileTypes?: string[];\n multiple?: boolean;\n reorderable?: boolean;\n strategy?: string;\n /** Set client-side from the panel routes. */\n uploadRoute?: string;\n}\n\nexport interface ImageFieldProps extends FileFieldProps {\n aspectRatio?: number;\n crop?: boolean;\n}\n\nexport type ColorFormat = 'hex' | 'rgb' | 'hsl';\n\nexport interface ColorFieldProps {\n presets?: string[];\n format?: ColorFormat;\n alpha?: boolean;\n}\n\nexport interface SlugFieldProps extends TextFieldProps {\n reservedSlugs?: string[];\n}\n\n/* ─── Discriminated union ───────────────────────────────────────── */\n\nexport type TextFieldSchema = FieldBase<'text', TextFieldProps>;\nexport type TextareaFieldSchema = FieldBase<'textarea', TextFieldProps>;\nexport type EmailFieldSchema = FieldBase<'email', TextFieldProps>;\nexport type UrlFieldSchema = FieldBase<'url', TextFieldProps>;\nexport type PasswordFieldSchema = FieldBase<'password', TextFieldProps>;\nexport type SlugFieldSchema = FieldBase<'slug', SlugFieldProps>;\nexport type NumberFieldSchema = FieldBase<'number', NumberFieldProps>;\nexport type CurrencyFieldSchema = FieldBase<'currency', CurrencyFieldProps>;\nexport type BooleanFieldSchema = FieldBase<'boolean', BooleanFieldProps>;\nexport type ToggleFieldSchema = FieldBase<'toggle', ToggleFieldProps>;\nexport type SelectFieldSchema = FieldBase<'select', SelectFieldProps>;\nexport type MultiSelectFieldSchema = FieldBase<'multiSelect', MultiSelectFieldProps>;\nexport type RadioFieldSchema = FieldBase<'radio', RadioFieldProps>;\nexport type BelongsToFieldSchema = FieldBase<'belongsTo', BelongsToFieldProps>;\nexport type HasManyFieldSchema = FieldBase<'hasMany', HasManyFieldProps>;\nexport type DateFieldSchema = FieldBase<'date', DateFieldProps>;\nexport type DateTimeFieldSchema = FieldBase<'dateTime', DateTimeFieldProps>;\nexport type FileFieldSchema = FieldBase<'file', FileFieldProps>;\nexport type ImageFieldSchema = FieldBase<'image', ImageFieldProps>;\nexport type ColorFieldSchema = FieldBase<'color', ColorFieldProps>;\nexport type HiddenFieldSchema = FieldBase<'hidden', Record<string, never>>;\n\nexport type FieldSchema =\n | TextFieldSchema\n | TextareaFieldSchema\n | EmailFieldSchema\n | UrlFieldSchema\n | PasswordFieldSchema\n | SlugFieldSchema\n | NumberFieldSchema\n | CurrencyFieldSchema\n | BooleanFieldSchema\n | ToggleFieldSchema\n | SelectFieldSchema\n | MultiSelectFieldSchema\n | RadioFieldSchema\n | BelongsToFieldSchema\n | HasManyFieldSchema\n | DateFieldSchema\n | DateTimeFieldSchema\n | FileFieldSchema\n | ImageFieldSchema\n | ColorFieldSchema\n | HiddenFieldSchema;\n\n/* ─── Type guards ───────────────────────────────────────────────── */\n\nexport function isFieldType<T extends FieldType>(\n field: FieldSchema,\n type: T,\n): field is Extract<FieldSchema, { type: T }> {\n return field.type === type;\n}\n"]}
@@ -0,0 +1,95 @@
1
+ import { FieldSchema } from './fields.js';
2
+
3
+ /**
4
+ * Form schema mirroring `arqel-dev/form` PHP serialisation.
5
+ *
6
+ * The `Form::toArray()` payload is a heterogeneous list of entries
7
+ * tagged by `kind`: a `field` carries `{name, type}` (the React
8
+ * side resolves the full Field schema from the Resource fields
9
+ * array), while a `layout` entry carries `{type, component,
10
+ * columnSpan, props}` plus the component's own children.
11
+ */
12
+
13
+ type LayoutType = 'section' | 'fieldset' | 'grid' | 'columns' | 'group' | 'tabs' | 'tab';
14
+ interface FieldEntry {
15
+ kind: 'field';
16
+ name: string;
17
+ type: string;
18
+ }
19
+ interface LayoutBase<TType extends LayoutType, TProps> {
20
+ kind: 'layout';
21
+ type: TType;
22
+ component: string;
23
+ columnSpan: number | string;
24
+ props: TProps;
25
+ /** Schema may include further entries (recursive). */
26
+ schema?: SchemaEntry[];
27
+ }
28
+ interface SectionProps {
29
+ heading: string;
30
+ description?: string;
31
+ icon?: string;
32
+ collapsible?: boolean;
33
+ collapsed?: boolean;
34
+ columns: number;
35
+ compact?: boolean;
36
+ aside?: boolean;
37
+ }
38
+ interface FieldsetProps {
39
+ legend: string;
40
+ columns: number;
41
+ }
42
+ interface GridProps {
43
+ columns: number | Record<string, number>;
44
+ gap?: string;
45
+ }
46
+ interface ColumnsProps {
47
+ columns: 2;
48
+ }
49
+ type GroupOrientation = 'horizontal' | 'vertical';
50
+ interface GroupProps {
51
+ orientation: GroupOrientation;
52
+ }
53
+ type TabsOrientation = 'horizontal' | 'vertical';
54
+ interface TabsProps {
55
+ defaultTab: string | null;
56
+ orientation: TabsOrientation;
57
+ }
58
+ interface TabProps {
59
+ id: string;
60
+ label: string;
61
+ icon?: string;
62
+ badge?: number;
63
+ }
64
+ type SectionEntry = LayoutBase<'section', SectionProps>;
65
+ type FieldsetEntry = LayoutBase<'fieldset', FieldsetProps>;
66
+ type GridEntry = LayoutBase<'grid', GridProps>;
67
+ type ColumnsEntry = LayoutBase<'columns', ColumnsProps>;
68
+ type GroupEntry = LayoutBase<'group', GroupProps>;
69
+ type TabsEntry = LayoutBase<'tabs', TabsProps>;
70
+ type TabEntry = LayoutBase<'tab', TabProps>;
71
+ type LayoutEntry = SectionEntry | FieldsetEntry | GridEntry | ColumnsEntry | GroupEntry | TabsEntry | TabEntry;
72
+ type SchemaEntry = FieldEntry | LayoutEntry;
73
+ /**
74
+ * The full `Form::toArray()` payload.
75
+ */
76
+ interface FormSchema {
77
+ schema: SchemaEntry[];
78
+ columns: number;
79
+ model: string | null;
80
+ inline: boolean;
81
+ disabled: boolean;
82
+ }
83
+ /**
84
+ * Type guard narrowing on `kind`.
85
+ */
86
+ declare function isLayoutEntry(entry: SchemaEntry): entry is LayoutEntry;
87
+ declare function isFieldEntry(entry: SchemaEntry): entry is FieldEntry;
88
+ /**
89
+ * Resolve a `FieldEntry`'s full schema by name from a flat list
90
+ * of Field schemas. Returns `null` when the field is missing —
91
+ * authoring sites typically render a fallback.
92
+ */
93
+ declare function resolveFieldEntry(entry: FieldEntry, fields: FieldSchema[]): FieldSchema | null;
94
+
95
+ export { type ColumnsEntry, type ColumnsProps, type FieldEntry, type FieldsetEntry, type FieldsetProps, type FormSchema, type GridEntry, type GridProps, type GroupEntry, type GroupOrientation, type GroupProps, type LayoutEntry, type LayoutType, type SchemaEntry, type SectionEntry, type SectionProps, type TabEntry, type TabProps, type TabsEntry, type TabsOrientation, type TabsProps, isFieldEntry, isLayoutEntry, resolveFieldEntry };
package/dist/forms.js ADDED
@@ -0,0 +1,14 @@
1
+ // src/forms.ts
2
+ function isLayoutEntry(entry) {
3
+ return entry.kind === "layout";
4
+ }
5
+ function isFieldEntry(entry) {
6
+ return entry.kind === "field";
7
+ }
8
+ function resolveFieldEntry(entry, fields) {
9
+ return fields.find((field) => field.name === entry.name) ?? null;
10
+ }
11
+
12
+ export { isFieldEntry, isLayoutEntry, resolveFieldEntry };
13
+ //# sourceMappingURL=forms.js.map
14
+ //# sourceMappingURL=forms.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/forms.ts"],"names":[],"mappings":";AA8GO,SAAS,cAAc,KAAA,EAA0C;AACtE,EAAA,OAAO,MAAM,IAAA,KAAS,QAAA;AACxB;AAEO,SAAS,aAAa,KAAA,EAAyC;AACpE,EAAA,OAAO,MAAM,IAAA,KAAS,OAAA;AACxB;AAOO,SAAS,iBAAA,CAAkB,OAAmB,MAAA,EAA2C;AAC9F,EAAA,OAAO,MAAA,CAAO,KAAK,CAAC,KAAA,KAAU,MAAM,IAAA,KAAS,KAAA,CAAM,IAAI,CAAA,IAAK,IAAA;AAC9D","file":"forms.js","sourcesContent":["/**\n * Form schema mirroring `arqel-dev/form` PHP serialisation.\n *\n * The `Form::toArray()` payload is a heterogeneous list of entries\n * tagged by `kind`: a `field` carries `{name, type}` (the React\n * side resolves the full Field schema from the Resource fields\n * array), while a `layout` entry carries `{type, component,\n * columnSpan, props}` plus the component's own children.\n */\n\nimport type { FieldSchema } from './fields.js';\n\nexport type LayoutType = 'section' | 'fieldset' | 'grid' | 'columns' | 'group' | 'tabs' | 'tab';\n\nexport interface FieldEntry {\n kind: 'field';\n name: string;\n type: string;\n}\n\ninterface LayoutBase<TType extends LayoutType, TProps> {\n kind: 'layout';\n type: TType;\n component: string;\n columnSpan: number | string;\n props: TProps;\n /** Schema may include further entries (recursive). */\n schema?: SchemaEntry[];\n}\n\n/* ─── Layout props ──────────────────────────────────────────────── */\n\nexport interface SectionProps {\n heading: string;\n description?: string;\n icon?: string;\n collapsible?: boolean;\n collapsed?: boolean;\n columns: number;\n compact?: boolean;\n aside?: boolean;\n}\n\nexport interface FieldsetProps {\n legend: string;\n columns: number;\n}\n\nexport interface GridProps {\n columns: number | Record<string, number>;\n gap?: string;\n}\n\nexport interface ColumnsProps {\n columns: 2;\n}\n\nexport type GroupOrientation = 'horizontal' | 'vertical';\n\nexport interface GroupProps {\n orientation: GroupOrientation;\n}\n\nexport type TabsOrientation = 'horizontal' | 'vertical';\n\nexport interface TabsProps {\n defaultTab: string | null;\n orientation: TabsOrientation;\n}\n\nexport interface TabProps {\n id: string;\n label: string;\n icon?: string;\n badge?: number;\n}\n\nexport type SectionEntry = LayoutBase<'section', SectionProps>;\nexport type FieldsetEntry = LayoutBase<'fieldset', FieldsetProps>;\nexport type GridEntry = LayoutBase<'grid', GridProps>;\nexport type ColumnsEntry = LayoutBase<'columns', ColumnsProps>;\nexport type GroupEntry = LayoutBase<'group', GroupProps>;\nexport type TabsEntry = LayoutBase<'tabs', TabsProps>;\nexport type TabEntry = LayoutBase<'tab', TabProps>;\n\nexport type LayoutEntry =\n | SectionEntry\n | FieldsetEntry\n | GridEntry\n | ColumnsEntry\n | GroupEntry\n | TabsEntry\n | TabEntry;\n\nexport type SchemaEntry = FieldEntry | LayoutEntry;\n\n/**\n * The full `Form::toArray()` payload.\n */\nexport interface FormSchema {\n schema: SchemaEntry[];\n columns: number;\n model: string | null;\n inline: boolean;\n disabled: boolean;\n}\n\n/**\n * Type guard narrowing on `kind`.\n */\nexport function isLayoutEntry(entry: SchemaEntry): entry is LayoutEntry {\n return entry.kind === 'layout';\n}\n\nexport function isFieldEntry(entry: SchemaEntry): entry is FieldEntry {\n return entry.kind === 'field';\n}\n\n/**\n * Resolve a `FieldEntry`'s full schema by name from a flat list\n * of Field schemas. Returns `null` when the field is missing —\n * authoring sites typically render a fallback.\n */\nexport function resolveFieldEntry(entry: FieldEntry, fields: FieldSchema[]): FieldSchema | null {\n return fields.find((field) => field.name === entry.name) ?? null;\n}\n"]}
@@ -0,0 +1,6 @@
1
+ export { ActionColor, ActionFormField, ActionMethod, ActionSchema, ActionType, ActionVariant, ConfirmationColor, ConfirmationConfig, ModalSize, ResourceActions } from './actions.js';
2
+ export { BelongsToFieldProps, BelongsToFieldSchema, BooleanFieldProps, BooleanFieldSchema, ColorFieldProps, ColorFieldSchema, ColorFormat, CurrencyFieldProps, CurrencyFieldSchema, DateFieldProps, DateFieldSchema, DateTimeFieldProps, DateTimeFieldSchema, EmailFieldSchema, FieldSchema, FieldType, FieldValidation, FieldVisibility, FileFieldProps, FileFieldSchema, HasManyFieldProps, HasManyFieldSchema, HiddenFieldSchema, ImageFieldProps, ImageFieldSchema, MultiSelectFieldProps, MultiSelectFieldSchema, NumberFieldProps, NumberFieldSchema, PasswordFieldSchema, RadioFieldProps, RadioFieldSchema, SelectFieldProps, SelectFieldSchema, SelectOption, SlugFieldProps, SlugFieldSchema, TextFieldProps, TextFieldSchema, TextareaFieldSchema, ToggleFieldProps, ToggleFieldSchema, UrlFieldSchema, isFieldType } from './fields.js';
3
+ export { ColumnsEntry, ColumnsProps, FieldEntry, FieldsetEntry, FieldsetProps, FormSchema, GridEntry, GridProps, GroupEntry, GroupOrientation, GroupProps, LayoutEntry, LayoutType, SchemaEntry, SectionEntry, SectionProps, TabEntry, TabProps, TabsEntry, TabsOrientation, TabsProps, isFieldEntry, isLayoutEntry, resolveFieldEntry } from './forms.js';
4
+ export { ArqelMeta, ArqelPageProps, AuthPayload, AuthUserPayload, FlashPayload, PanelPayload, SharedProps } from './inertia.js';
5
+ export { PaginationMeta, PlainResourceIndexProps, RecordType, ResourceCreateProps, ResourceDetailProps, ResourceEditProps, ResourceIndexProps, ResourceMeta } from './resources.js';
6
+ export { BadgeColumnProps, BadgeColumnSchema, BadgeOption, BooleanColumnProps, BooleanColumnSchema, ColumnAlign, ColumnSchema, ColumnType, ComputedColumnProps, ComputedColumnSchema, DateColumnMode, DateColumnProps, DateColumnSchema, DateRangeFilterProps, DateRangeFilterSchema, FilterSchema, FilterType, IconColumnProps, IconColumnSchema, ImageColumnProps, ImageColumnSchema, ImageColumnShape, MultiSelectFilterSchema, NumberColumnProps, NumberColumnSchema, RelationshipColumnProps, RelationshipColumnSchema, ScopeFilterSchema, SelectFilterProps, SelectFilterSchema, SortDirection, TableSort, TernaryFilterProps, TernaryFilterSchema, TernaryState, TextColumnProps, TextColumnSchema, TextFilterSchema } from './tables.js';
package/dist/index.js ADDED
@@ -0,0 +1,19 @@
1
+ // src/fields.ts
2
+ function isFieldType(field, type) {
3
+ return field.type === type;
4
+ }
5
+
6
+ // src/forms.ts
7
+ function isLayoutEntry(entry) {
8
+ return entry.kind === "layout";
9
+ }
10
+ function isFieldEntry(entry) {
11
+ return entry.kind === "field";
12
+ }
13
+ function resolveFieldEntry(entry, fields) {
14
+ return fields.find((field) => field.name === entry.name) ?? null;
15
+ }
16
+
17
+ export { isFieldEntry, isFieldType, isLayoutEntry, resolveFieldEntry };
18
+ //# sourceMappingURL=index.js.map
19
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/fields.ts","../src/forms.ts"],"names":[],"mappings":";AA2OO,SAAS,WAAA,CACd,OACA,IAAA,EAC4C;AAC5C,EAAA,OAAO,MAAM,IAAA,KAAS,IAAA;AACxB;;;AClIO,SAAS,cAAc,KAAA,EAA0C;AACtE,EAAA,OAAO,MAAM,IAAA,KAAS,QAAA;AACxB;AAEO,SAAS,aAAa,KAAA,EAAyC;AACpE,EAAA,OAAO,MAAM,IAAA,KAAS,OAAA;AACxB;AAOO,SAAS,iBAAA,CAAkB,OAAmB,MAAA,EAA2C;AAC9F,EAAA,OAAO,MAAA,CAAO,KAAK,CAAC,KAAA,KAAU,MAAM,IAAA,KAAS,KAAA,CAAM,IAAI,CAAA,IAAK,IAAA;AAC9D","file":"index.js","sourcesContent":["/**\n * Field schema mirroring `Arqel\\Core\\Support\\FieldSchemaSerializer`\n * output. The discriminated union narrows on `type`; per-type\n * `props` carry the type-specific metadata emitted by each Field's\n * `getTypeSpecificProps()`.\n */\n\nexport type FieldType =\n | 'text'\n | 'textarea'\n | 'email'\n | 'url'\n | 'password'\n | 'slug'\n | 'number'\n | 'currency'\n | 'boolean'\n | 'toggle'\n | 'select'\n | 'multiSelect'\n | 'radio'\n | 'belongsTo'\n | 'hasMany'\n | 'date'\n | 'dateTime'\n | 'file'\n | 'image'\n | 'color'\n | 'hidden';\n\nexport interface FieldValidation {\n rules: string[];\n messages: Record<string, string>;\n attribute: string | null;\n}\n\n/**\n * Per-context visibility flags (mirrors `HasVisibility::isVisibleIn`).\n * `canSee` is the per-user/record gate from `HasAuthorization`.\n */\nexport interface FieldVisibility {\n create: boolean;\n edit: boolean;\n detail: boolean;\n table: boolean;\n canSee: boolean;\n}\n\n/**\n * Common shape every Field carries before the discriminated `props`.\n */\ninterface FieldBase<TType extends FieldType, TProps> {\n type: TType;\n name: string;\n label: string | null;\n component: string | null;\n required: boolean;\n readonly: boolean;\n disabled: boolean;\n placeholder: string | null;\n helperText: string | null;\n defaultValue: unknown;\n columnSpan: number | string;\n live: boolean;\n liveDebounce: number | null;\n validation: FieldValidation;\n visibility: FieldVisibility;\n dependsOn: string[];\n props: TProps;\n}\n\n/* ─── Per-type props ────────────────────────────────────────────── */\n\nexport interface TextFieldProps {\n maxLength?: number;\n minLength?: number;\n pattern?: string;\n autocomplete?: string;\n mask?: string;\n}\n\nexport interface NumberFieldProps {\n min?: number;\n max?: number;\n step?: number | string;\n integer?: boolean;\n decimals?: number;\n}\n\nexport interface CurrencyFieldProps extends NumberFieldProps {\n prefix: string;\n suffix?: string;\n thousandsSeparator: string;\n decimalSeparator: string;\n}\n\nexport interface BooleanFieldProps {\n inline?: boolean;\n}\n\nexport interface ToggleFieldProps {\n onLabel?: string;\n offLabel?: string;\n}\n\nexport interface SelectOption {\n value: string | number;\n label: string;\n}\n\nexport interface SelectFieldProps {\n options: SelectOption[] | Record<string, string>;\n searchable?: boolean;\n multiple?: boolean;\n placeholder?: string;\n}\n\nexport interface MultiSelectFieldProps extends SelectFieldProps {\n multiple: true;\n}\n\nexport interface RadioFieldProps {\n options: SelectOption[];\n inline?: boolean;\n}\n\nexport interface BelongsToFieldProps {\n relatedResource: string;\n relationship: string;\n searchable: boolean;\n searchColumns: string[];\n preload: boolean;\n /** Set client-side from the panel routes. */\n searchRoute?: string;\n}\n\nexport interface HasManyFieldProps {\n relatedResource: string;\n relationship: string;\n canAddRecords?: boolean;\n canEditRecords?: boolean;\n}\n\nexport interface DateFieldProps {\n format: string;\n displayFormat: string;\n minDate?: string;\n maxDate?: string;\n closeOnDateSelection?: boolean;\n timezone?: string;\n}\n\nexport interface DateTimeFieldProps extends DateFieldProps {\n seconds?: boolean;\n}\n\nexport interface FileFieldProps {\n disk: string;\n directory?: string;\n visibility?: 'public' | 'private';\n maxSize?: number;\n acceptedFileTypes?: string[];\n multiple?: boolean;\n reorderable?: boolean;\n strategy?: string;\n /** Set client-side from the panel routes. */\n uploadRoute?: string;\n}\n\nexport interface ImageFieldProps extends FileFieldProps {\n aspectRatio?: number;\n crop?: boolean;\n}\n\nexport type ColorFormat = 'hex' | 'rgb' | 'hsl';\n\nexport interface ColorFieldProps {\n presets?: string[];\n format?: ColorFormat;\n alpha?: boolean;\n}\n\nexport interface SlugFieldProps extends TextFieldProps {\n reservedSlugs?: string[];\n}\n\n/* ─── Discriminated union ───────────────────────────────────────── */\n\nexport type TextFieldSchema = FieldBase<'text', TextFieldProps>;\nexport type TextareaFieldSchema = FieldBase<'textarea', TextFieldProps>;\nexport type EmailFieldSchema = FieldBase<'email', TextFieldProps>;\nexport type UrlFieldSchema = FieldBase<'url', TextFieldProps>;\nexport type PasswordFieldSchema = FieldBase<'password', TextFieldProps>;\nexport type SlugFieldSchema = FieldBase<'slug', SlugFieldProps>;\nexport type NumberFieldSchema = FieldBase<'number', NumberFieldProps>;\nexport type CurrencyFieldSchema = FieldBase<'currency', CurrencyFieldProps>;\nexport type BooleanFieldSchema = FieldBase<'boolean', BooleanFieldProps>;\nexport type ToggleFieldSchema = FieldBase<'toggle', ToggleFieldProps>;\nexport type SelectFieldSchema = FieldBase<'select', SelectFieldProps>;\nexport type MultiSelectFieldSchema = FieldBase<'multiSelect', MultiSelectFieldProps>;\nexport type RadioFieldSchema = FieldBase<'radio', RadioFieldProps>;\nexport type BelongsToFieldSchema = FieldBase<'belongsTo', BelongsToFieldProps>;\nexport type HasManyFieldSchema = FieldBase<'hasMany', HasManyFieldProps>;\nexport type DateFieldSchema = FieldBase<'date', DateFieldProps>;\nexport type DateTimeFieldSchema = FieldBase<'dateTime', DateTimeFieldProps>;\nexport type FileFieldSchema = FieldBase<'file', FileFieldProps>;\nexport type ImageFieldSchema = FieldBase<'image', ImageFieldProps>;\nexport type ColorFieldSchema = FieldBase<'color', ColorFieldProps>;\nexport type HiddenFieldSchema = FieldBase<'hidden', Record<string, never>>;\n\nexport type FieldSchema =\n | TextFieldSchema\n | TextareaFieldSchema\n | EmailFieldSchema\n | UrlFieldSchema\n | PasswordFieldSchema\n | SlugFieldSchema\n | NumberFieldSchema\n | CurrencyFieldSchema\n | BooleanFieldSchema\n | ToggleFieldSchema\n | SelectFieldSchema\n | MultiSelectFieldSchema\n | RadioFieldSchema\n | BelongsToFieldSchema\n | HasManyFieldSchema\n | DateFieldSchema\n | DateTimeFieldSchema\n | FileFieldSchema\n | ImageFieldSchema\n | ColorFieldSchema\n | HiddenFieldSchema;\n\n/* ─── Type guards ───────────────────────────────────────────────── */\n\nexport function isFieldType<T extends FieldType>(\n field: FieldSchema,\n type: T,\n): field is Extract<FieldSchema, { type: T }> {\n return field.type === type;\n}\n","/**\n * Form schema mirroring `arqel-dev/form` PHP serialisation.\n *\n * The `Form::toArray()` payload is a heterogeneous list of entries\n * tagged by `kind`: a `field` carries `{name, type}` (the React\n * side resolves the full Field schema from the Resource fields\n * array), while a `layout` entry carries `{type, component,\n * columnSpan, props}` plus the component's own children.\n */\n\nimport type { FieldSchema } from './fields.js';\n\nexport type LayoutType = 'section' | 'fieldset' | 'grid' | 'columns' | 'group' | 'tabs' | 'tab';\n\nexport interface FieldEntry {\n kind: 'field';\n name: string;\n type: string;\n}\n\ninterface LayoutBase<TType extends LayoutType, TProps> {\n kind: 'layout';\n type: TType;\n component: string;\n columnSpan: number | string;\n props: TProps;\n /** Schema may include further entries (recursive). */\n schema?: SchemaEntry[];\n}\n\n/* ─── Layout props ──────────────────────────────────────────────── */\n\nexport interface SectionProps {\n heading: string;\n description?: string;\n icon?: string;\n collapsible?: boolean;\n collapsed?: boolean;\n columns: number;\n compact?: boolean;\n aside?: boolean;\n}\n\nexport interface FieldsetProps {\n legend: string;\n columns: number;\n}\n\nexport interface GridProps {\n columns: number | Record<string, number>;\n gap?: string;\n}\n\nexport interface ColumnsProps {\n columns: 2;\n}\n\nexport type GroupOrientation = 'horizontal' | 'vertical';\n\nexport interface GroupProps {\n orientation: GroupOrientation;\n}\n\nexport type TabsOrientation = 'horizontal' | 'vertical';\n\nexport interface TabsProps {\n defaultTab: string | null;\n orientation: TabsOrientation;\n}\n\nexport interface TabProps {\n id: string;\n label: string;\n icon?: string;\n badge?: number;\n}\n\nexport type SectionEntry = LayoutBase<'section', SectionProps>;\nexport type FieldsetEntry = LayoutBase<'fieldset', FieldsetProps>;\nexport type GridEntry = LayoutBase<'grid', GridProps>;\nexport type ColumnsEntry = LayoutBase<'columns', ColumnsProps>;\nexport type GroupEntry = LayoutBase<'group', GroupProps>;\nexport type TabsEntry = LayoutBase<'tabs', TabsProps>;\nexport type TabEntry = LayoutBase<'tab', TabProps>;\n\nexport type LayoutEntry =\n | SectionEntry\n | FieldsetEntry\n | GridEntry\n | ColumnsEntry\n | GroupEntry\n | TabsEntry\n | TabEntry;\n\nexport type SchemaEntry = FieldEntry | LayoutEntry;\n\n/**\n * The full `Form::toArray()` payload.\n */\nexport interface FormSchema {\n schema: SchemaEntry[];\n columns: number;\n model: string | null;\n inline: boolean;\n disabled: boolean;\n}\n\n/**\n * Type guard narrowing on `kind`.\n */\nexport function isLayoutEntry(entry: SchemaEntry): entry is LayoutEntry {\n return entry.kind === 'layout';\n}\n\nexport function isFieldEntry(entry: SchemaEntry): entry is FieldEntry {\n return entry.kind === 'field';\n}\n\n/**\n * Resolve a `FieldEntry`'s full schema by name from a flat list\n * of Field schemas. Returns `null` when the field is missing —\n * authoring sites typically render a fallback.\n */\nexport function resolveFieldEntry(entry: FieldEntry, fields: FieldSchema[]): FieldSchema | null {\n return fields.find((field) => field.name === entry.name) ?? null;\n}\n"]}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Inertia shared props injected by
3
+ * `Arqel\Core\Http\Middleware\HandleArqelInertiaRequests`.
4
+ *
5
+ * Apps extend the Inertia `PageProps` interface so every page
6
+ * receives `auth`, `panel`, `tenant`, `flash`, `translations`,
7
+ * `arqel.version` for free.
8
+ */
9
+ interface AuthUserPayload {
10
+ id: number | string;
11
+ name?: string | null;
12
+ email?: string | null;
13
+ }
14
+ interface AuthPayload {
15
+ user: AuthUserPayload | null;
16
+ /** Shape: `{ ability: boolean, ... }` from `AbilityRegistry`. */
17
+ can: Record<string, boolean>;
18
+ }
19
+ interface PanelPayload {
20
+ id: string;
21
+ path: string;
22
+ /** `Panel::brand()` → `{ name, logo? }`. */
23
+ brand: {
24
+ name: string;
25
+ logo: string | null;
26
+ };
27
+ }
28
+ interface FlashPayload {
29
+ success: string | null;
30
+ error: string | null;
31
+ info: string | null;
32
+ warning: string | null;
33
+ }
34
+ interface ArqelMeta {
35
+ version: string;
36
+ }
37
+ /**
38
+ * The full set of shared props every Arqel page receives.
39
+ */
40
+ interface SharedProps {
41
+ auth: AuthPayload;
42
+ panel: PanelPayload | null;
43
+ /** Tenant payload — null in Phase 1 (scaffold only). */
44
+ tenant: unknown;
45
+ flash: FlashPayload;
46
+ /** Translation map under the `arqel::*` namespace. */
47
+ translations: Record<string, unknown>;
48
+ arqel: ArqelMeta;
49
+ }
50
+ /**
51
+ * Convenience alias for Inertia v3 `usePage<T>()`. Apps with extra
52
+ * shared props extend via intersection: `SharedProps & MyShared`.
53
+ */
54
+ type ArqelPageProps = SharedProps;
55
+
56
+ export type { ArqelMeta, ArqelPageProps, AuthPayload, AuthUserPayload, FlashPayload, PanelPayload, SharedProps };
@@ -0,0 +1,3 @@
1
+
2
+ //# sourceMappingURL=inertia.js.map
3
+ //# sourceMappingURL=inertia.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"inertia.js"}
@@ -0,0 +1,91 @@
1
+ import { ResourceActions } from './actions.js';
2
+ import { FieldSchema } from './fields.js';
3
+ import { ColumnSchema, FilterSchema, TableSort } from './tables.js';
4
+
5
+ /**
6
+ * Resource-level Inertia payloads — the shapes returned by
7
+ * `InertiaDataBuilder::buildIndexData/CreateData/EditData/ShowData`.
8
+ */
9
+
10
+ /**
11
+ * Loose record shape — apps that opt into
12
+ * `spatie/laravel-typescript-transformer` (TYPES-003) replace this
13
+ * with strongly-typed `interface User { id: number; ... }` etc.
14
+ */
15
+ type RecordType = Record<string, unknown> & {
16
+ id: number | string;
17
+ arqel?: {
18
+ title?: string;
19
+ subtitle?: string | null;
20
+ };
21
+ };
22
+ /**
23
+ * Resource metadata block (`InertiaDataBuilder::resourceMeta`).
24
+ */
25
+ interface ResourceMeta {
26
+ class: string;
27
+ slug: string;
28
+ label: string;
29
+ pluralLabel: string;
30
+ navigationIcon: string | null;
31
+ navigationGroup: string | null;
32
+ }
33
+ /**
34
+ * Pagination payload (`Illuminate\Pagination\LengthAwarePaginator`
35
+ * stripped for Inertia).
36
+ */
37
+ interface PaginationMeta {
38
+ currentPage: number;
39
+ lastPage: number;
40
+ perPage: number;
41
+ total: number;
42
+ }
43
+ /**
44
+ * Index page props — the rich Table flavour, used when
45
+ * `Resource::table()` returns an Arqel Table.
46
+ */
47
+ interface ResourceIndexProps<TRecord extends RecordType = RecordType> {
48
+ resource: ResourceMeta;
49
+ records: TRecord[];
50
+ pagination: PaginationMeta | null;
51
+ columns: ColumnSchema[];
52
+ filters: FilterSchema[];
53
+ actions: ResourceActions;
54
+ search: string | null;
55
+ sort: TableSort;
56
+ }
57
+ /**
58
+ * Index page props — plain (no Table) flavour.
59
+ */
60
+ interface PlainResourceIndexProps<TRecord extends RecordType = RecordType> {
61
+ resource: ResourceMeta;
62
+ records: TRecord[];
63
+ pagination: PaginationMeta;
64
+ fields: FieldSchema[];
65
+ }
66
+ /**
67
+ * Create page props (no record context).
68
+ */
69
+ interface ResourceCreateProps {
70
+ resource: ResourceMeta;
71
+ record: null;
72
+ fields: FieldSchema[];
73
+ }
74
+ /**
75
+ * Edit page props — record + fields (record-aware visibility +
76
+ * readonly applied server-side).
77
+ */
78
+ interface ResourceEditProps<TRecord extends RecordType = RecordType> {
79
+ resource: ResourceMeta;
80
+ record: TRecord;
81
+ recordTitle: string;
82
+ recordSubtitle: string | null;
83
+ fields: FieldSchema[];
84
+ }
85
+ /**
86
+ * Show / detail page props — same shape as edit; React renders
87
+ * fields read-only.
88
+ */
89
+ type ResourceDetailProps<TRecord extends RecordType = RecordType> = ResourceEditProps<TRecord>;
90
+
91
+ export type { PaginationMeta, PlainResourceIndexProps, RecordType, ResourceCreateProps, ResourceDetailProps, ResourceEditProps, ResourceIndexProps, ResourceMeta };
@@ -0,0 +1,3 @@
1
+
2
+ //# sourceMappingURL=resources.js.map
3
+ //# sourceMappingURL=resources.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"resources.js"}
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Table column schema mirroring `arqel-dev/table` PHP serialisation.
3
+ */
4
+ type ColumnType = 'text' | 'badge' | 'boolean' | 'date' | 'number' | 'icon' | 'image' | 'relationship' | 'computed';
5
+ type ColumnAlign = 'start' | 'center' | 'end';
6
+ type DateColumnMode = 'date' | 'datetime' | 'since';
7
+ type ImageColumnShape = 'square' | 'circular';
8
+ type SortDirection = 'asc' | 'desc';
9
+ interface ColumnBase<TType extends ColumnType, TProps = Record<string, never>> {
10
+ type: TType;
11
+ name: string;
12
+ label: string | null;
13
+ sortable: boolean;
14
+ searchable: boolean;
15
+ copyable: boolean;
16
+ hidden: boolean;
17
+ hiddenOnMobile: boolean;
18
+ align: ColumnAlign;
19
+ width: string | null;
20
+ tooltip: string | null;
21
+ /** Resolved URL for clickable cells. */
22
+ url?: string;
23
+ props: TProps;
24
+ }
25
+ interface TextColumnProps {
26
+ truncate?: number;
27
+ color?: string;
28
+ weight?: 'normal' | 'medium' | 'bold';
29
+ }
30
+ interface BadgeOption {
31
+ value: string | number | boolean;
32
+ label?: string;
33
+ color?: string;
34
+ icon?: string;
35
+ }
36
+ interface BadgeColumnProps {
37
+ options?: BadgeOption[];
38
+ pill?: boolean;
39
+ }
40
+ interface BooleanColumnProps {
41
+ trueIcon?: string;
42
+ falseIcon?: string;
43
+ trueColor?: string;
44
+ falseColor?: string;
45
+ }
46
+ interface DateColumnProps {
47
+ mode: DateColumnMode;
48
+ format?: string;
49
+ timezone?: string;
50
+ }
51
+ interface NumberColumnProps {
52
+ decimals?: number;
53
+ thousandsSeparator?: string;
54
+ decimalSeparator?: string;
55
+ prefix?: string;
56
+ suffix?: string;
57
+ }
58
+ interface IconColumnProps {
59
+ icon: string;
60
+ color?: string;
61
+ size?: 'sm' | 'md' | 'lg';
62
+ }
63
+ interface ImageColumnProps {
64
+ shape?: ImageColumnShape;
65
+ size?: number;
66
+ rounded?: boolean;
67
+ }
68
+ interface RelationshipColumnProps {
69
+ relationship: string;
70
+ attribute: string;
71
+ }
72
+ interface ComputedColumnProps {
73
+ /** Closure-resolved values are emitted directly per row. */
74
+ placeholder?: string;
75
+ }
76
+ type TextColumnSchema = ColumnBase<'text', TextColumnProps>;
77
+ type BadgeColumnSchema = ColumnBase<'badge', BadgeColumnProps>;
78
+ type BooleanColumnSchema = ColumnBase<'boolean', BooleanColumnProps>;
79
+ type DateColumnSchema = ColumnBase<'date', DateColumnProps>;
80
+ type NumberColumnSchema = ColumnBase<'number', NumberColumnProps>;
81
+ type IconColumnSchema = ColumnBase<'icon', IconColumnProps>;
82
+ type ImageColumnSchema = ColumnBase<'image', ImageColumnProps>;
83
+ type RelationshipColumnSchema = ColumnBase<'relationship', RelationshipColumnProps>;
84
+ type ComputedColumnSchema = ColumnBase<'computed', ComputedColumnProps>;
85
+ type ColumnSchema = TextColumnSchema | BadgeColumnSchema | BooleanColumnSchema | DateColumnSchema | NumberColumnSchema | IconColumnSchema | ImageColumnSchema | RelationshipColumnSchema | ComputedColumnSchema;
86
+ type FilterType = 'select' | 'multiSelect' | 'dateRange' | 'text' | 'ternary' | 'scope';
87
+ interface FilterBase<TType extends FilterType, TProps = Record<string, never>> {
88
+ type: TType;
89
+ name: string;
90
+ label: string | null;
91
+ persist: boolean;
92
+ default: unknown;
93
+ props: TProps;
94
+ }
95
+ interface SelectFilterProps {
96
+ options: {
97
+ value: string | number;
98
+ label: string;
99
+ }[];
100
+ }
101
+ interface DateRangeFilterProps {
102
+ minDate?: string;
103
+ maxDate?: string;
104
+ }
105
+ type TernaryState = 'true' | 'false' | 'all';
106
+ interface TernaryFilterProps {
107
+ trueLabel?: string;
108
+ falseLabel?: string;
109
+ allLabel?: string;
110
+ }
111
+ type SelectFilterSchema = FilterBase<'select', SelectFilterProps>;
112
+ type MultiSelectFilterSchema = FilterBase<'multiSelect', SelectFilterProps>;
113
+ type DateRangeFilterSchema = FilterBase<'dateRange', DateRangeFilterProps>;
114
+ type TextFilterSchema = FilterBase<'text'>;
115
+ type TernaryFilterSchema = FilterBase<'ternary', TernaryFilterProps>;
116
+ type ScopeFilterSchema = FilterBase<'scope'>;
117
+ type FilterSchema = SelectFilterSchema | MultiSelectFilterSchema | DateRangeFilterSchema | TextFilterSchema | TernaryFilterSchema | ScopeFilterSchema;
118
+ interface TableSort {
119
+ column: string | null;
120
+ direction: SortDirection | null;
121
+ }
122
+
123
+ export type { BadgeColumnProps, BadgeColumnSchema, BadgeOption, BooleanColumnProps, BooleanColumnSchema, ColumnAlign, ColumnSchema, ColumnType, ComputedColumnProps, ComputedColumnSchema, DateColumnMode, DateColumnProps, DateColumnSchema, DateRangeFilterProps, DateRangeFilterSchema, FilterSchema, FilterType, IconColumnProps, IconColumnSchema, ImageColumnProps, ImageColumnSchema, ImageColumnShape, MultiSelectFilterSchema, NumberColumnProps, NumberColumnSchema, RelationshipColumnProps, RelationshipColumnSchema, ScopeFilterSchema, SelectFilterProps, SelectFilterSchema, SortDirection, TableSort, TernaryFilterProps, TernaryFilterSchema, TernaryState, TextColumnProps, TextColumnSchema, TextFilterSchema };
package/dist/tables.js ADDED
@@ -0,0 +1,3 @@
1
+
2
+ //# sourceMappingURL=tables.js.map
3
+ //# sourceMappingURL=tables.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"tables.js"}
package/package.json ADDED
@@ -0,0 +1,77 @@
1
+ {
2
+ "name": "@arqel-dev/types",
3
+ "version": "0.8.0",
4
+ "description": "Shared TypeScript types for Arqel — fields, resources, tables, forms, actions, and Inertia shared props.",
5
+ "license": "MIT",
6
+ "homepage": "https://arqel.dev",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/arqel-dev/arqel.git",
10
+ "directory": "packages-js/types"
11
+ },
12
+ "keywords": [
13
+ "arqel",
14
+ "laravel",
15
+ "inertia",
16
+ "typescript",
17
+ "types"
18
+ ],
19
+ "type": "module",
20
+ "sideEffects": false,
21
+ "exports": {
22
+ ".": {
23
+ "types": "./dist/index.d.ts",
24
+ "import": "./dist/index.js"
25
+ },
26
+ "./fields": {
27
+ "types": "./dist/fields.d.ts",
28
+ "import": "./dist/fields.js"
29
+ },
30
+ "./resources": {
31
+ "types": "./dist/resources.d.ts",
32
+ "import": "./dist/resources.js"
33
+ },
34
+ "./tables": {
35
+ "types": "./dist/tables.d.ts",
36
+ "import": "./dist/tables.js"
37
+ },
38
+ "./forms": {
39
+ "types": "./dist/forms.d.ts",
40
+ "import": "./dist/forms.js"
41
+ },
42
+ "./actions": {
43
+ "types": "./dist/actions.d.ts",
44
+ "import": "./dist/actions.js"
45
+ },
46
+ "./inertia": {
47
+ "types": "./dist/inertia.d.ts",
48
+ "import": "./dist/inertia.js"
49
+ }
50
+ },
51
+ "files": [
52
+ "dist",
53
+ "README.md",
54
+ "SKILL.md"
55
+ ],
56
+ "devDependencies": {
57
+ "expect-type": "^1.2.2",
58
+ "tsup": "^8.5.0",
59
+ "typescript": "^5.6.3",
60
+ "vitest": "^3.0.0"
61
+ },
62
+ "publishConfig": {
63
+ "access": "public"
64
+ },
65
+ "engines": {
66
+ "node": ">=20.9.0"
67
+ },
68
+ "scripts": {
69
+ "build": "tsup",
70
+ "dev": "tsup --watch",
71
+ "typecheck": "tsc --noEmit",
72
+ "lint": "biome check --diagnostic-level=error src tests",
73
+ "format": "biome format --write src tests",
74
+ "test": "vitest run",
75
+ "test:watch": "vitest"
76
+ }
77
+ }