prax-orm 0.11.0

A next-generation, type-safe ORM for Rust inspired by Prisma
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
---
import DocsLayout from '../../layouts/DocsLayout.astro';
import CodeBlock from '../../components/CodeBlock.astro';

const basicTypes = `model Example {
    // Integer types
    id         Int       @id @auto          // 32-bit integer
    bigId      BigInt                       // 64-bit integer
    smallNum   Int       @db.SmallInt       // 16-bit (PostgreSQL)

    // Floating point
    price      Float                        // 64-bit float
    rating     Decimal   @db.Decimal(3, 2)  // Exact decimal

    // String types
    name       String                       // Variable length text
    bio        String    @db.Text           // Long text
    code       String    @db.Char(6)        // Fixed length

    // Boolean
    active     Boolean   @default(true)

    // Date/Time
    createdAt  DateTime  @default(now())
    birthDate  DateTime  @db.Date           // Date only
    loginTime  DateTime  @db.Time           // Time only

    // Binary
    avatar     Bytes                        // Binary data

    // JSON
    metadata   Json                         // JSON/JSONB
}`;

const modifiers = `model Example {
    // Required (default) - must have a value
    required   String

    // Optional - can be NULL
    optional   String?

    // Array/List - multiple values
    tags       String[]

    // Optional Array - entire array can be NULL
    optTags    String[]?
}

// Type modifier combinations
model Post {
    // Required string
    title      String

    // Optional string (nullable)
    subtitle   String?

    // Required array of strings
    tags       String[]

    // Optional array (array itself can be null)
    aliases    String[]?

    // Array with optional elements (PostgreSQL)
    // Each element can be null
    maybeItems String?[]
}`;

const attributes = `model User {
    // Primary key
    id           Int       @id

    // Auto-increment (usually with @id)
    sequence     Int       @auto

    // Unique constraint
    email        String    @unique

    // Default literal value
    role         String    @default("user")
    active       Boolean   @default(true)
    count        Int       @default(0)
    score        Float     @default(0.0)

    // Default function
    id           String    @default(uuid())
    publicId     String    @default(cuid())
    shortId      String    @default(nanoid())
    sortableId   String    @default(ulid())
    createdAt    DateTime  @default(now())

    // Auto-update timestamp
    updatedAt    DateTime  @updatedAt

    // Database-generated default
    trackingId   String    @default(dbgenerated("gen_random_uuid()"))

    // Column name mapping
    firstName    String    @map("first_name")

    // Ignore in client (still in DB)
    internalNote String?   @ignore

    // Database-specific type
    content      String    @db.Text
    amount       Decimal   @db.Decimal(10, 2)
}`;

const primaryKeys = `// Auto-increment integer (most common)
model User {
    id Int @id @auto
}

// UUID primary key
model Session {
    id String @id @default(uuid())
}

// CUID for distributed systems
model Event {
    id String @id @default(cuid())
}

// ULID for sortable IDs
model LogEntry {
    id String @id @default(ulid())
}

// NanoID for short URLs
model ShortLink {
    id String @id @default(nanoid())
}

// Composite primary key
model Membership {
    userId Int
    orgId  Int
    @@id([userId, orgId])
}

// Natural key (be careful with changes)
model Country {
    code String @id  // "US", "GB", etc.
    name String
}`;

const defaultValues = `model Record {
    // Literal defaults
    name      String   @default("")
    count     Int      @default(0)
    active    Boolean  @default(true)
    rating    Float    @default(0.0)
    tags      String[] @default([])
    config    Json     @default("{}")

    // Built-in functions
    id        String   @default(uuid())      // UUID v4
    publicId  String   @default(cuid())      // CUID
    shortId   String   @default(nanoid())    // NanoID (21 chars)
    tinyId    String   @default(nanoid(8))   // NanoID (custom length)
    sortId    String   @default(ulid())      // ULID
    createdAt DateTime @default(now())       // Current timestamp

    // Auto-increment
    sequence  Int      @default(autoincrement())

    // Enum default
    status    Status   @default(DRAFT)

    // Database-generated (PostgreSQL examples)
    trackId   String   @default(dbgenerated("gen_random_uuid()"))
    serial    Int      @default(dbgenerated("nextval('my_seq')"))
}`;

const timestamps = `model Record {
    // Creation timestamp - set once on insert
    createdAt DateTime @default(now())

    // Update timestamp - auto-updates on every change
    updatedAt DateTime @updatedAt

    // Soft delete timestamp - null means not deleted
    deletedAt DateTime?

    // Custom timestamp fields
    publishedAt DateTime?              // When published
    expiresAt   DateTime?              // Expiration date
    lastLoginAt DateTime?              // Last activity
}

// Database-specific timestamp types
model TimestampExample {
    // With timezone (PostgreSQL)
    createdAt   DateTime @db.Timestamptz

    // Without timezone
    localTime   DateTime @db.Timestamp

    // Date only (no time)
    birthDate   DateTime @db.Date

    // Time only (no date)
    startTime   DateTime @db.Time
}`;

const jsonFields = `model Settings {
    id       Int   @id @auto
    // JSON field (JSONB in PostgreSQL for indexing)
    config   Json  @db.JsonB

    // JSON with default
    options  Json  @default("{}")
    metadata Json  @default("{\\"version\\": 1}")
}

// Using JSON in queries
let users = client
    .user()
    .find_many()
    .where(user::settings::path(["theme"])::equals("dark"))
    .exec()
    .await?;

// JSON structure (not enforced by DB, document in comments)
/// Settings JSON structure:
/// {
///   "theme": "light" | "dark",
///   "notifications": {
///     "email": boolean,
///     "push": boolean
///   },
///   "language": string
/// }
model UserPreferences {
    id       Int  @id @auto
    settings Json @default("{}")
}`;

const arrayFields = `model Post {
    id       Int      @id @auto
    // String array
    tags     String[]
    // Integer array
    scores   Int[]
    // Enum array
    flags    Flag[]

    // Index for array contains queries (PostgreSQL)
    @@index([tags], type: GIN)
}

// Array operations in queries
let posts = client
    .post()
    .find_many()
    .where(post::tags::has("rust"))           // Contains element
    .where(post::tags::has_some(["rust", "orm"])) // Contains any
    .where(post::tags::has_every(["featured"]))   // Contains all
    .where(post::tags::is_empty(false))       // Not empty
    .exec()
    .await?;`;

const decimalFields = `// Decimal for exact precision (money, financial data)
model Product {
    id       Int     @id @auto
    // Default precision varies by database
    price    Decimal

    // Explicit precision: DECIMAL(10, 2)
    // 10 total digits, 2 after decimal
    // Range: -99999999.99 to 99999999.99
    cost     Decimal @db.Decimal(10, 2)

    // High precision for currency calculations
    amount   Decimal @db.Decimal(19, 4)

    // Tax rate (percentage)
    taxRate  Decimal @db.Decimal(5, 4)  // 0.0000 to 9.9999
}

// Decimal vs Float
model Comparison {
    // Use Decimal for:
    // - Money/currency
    // - Financial calculations
    // - When exact precision matters
    price    Decimal @db.Decimal(10, 2)

    // Use Float for:
    // - Scientific calculations
    // - When small rounding errors are acceptable
    // - Performance-critical calculations
    rating   Float
}`;

const bytesFields = `model File {
    id       Int    @id @auto
    name     String
    // Binary data (BYTEA in PostgreSQL, BLOB in MySQL)
    content  Bytes

    // File metadata
    mimeType String
    size     Int
}

model Image {
    id        Int    @id @auto
    // Store small images directly
    thumbnail Bytes?
    // Store image hash for deduplication
    hash      Bytes  @unique
}

// Note: For large files, consider storing in object storage
// (S3, GCS) and keeping only the URL/path in the database`;

const databaseTypes = `model DatabaseSpecific {
    id Int @id @auto

    // PostgreSQL-specific types
    uuid     String   @db.Uuid            // Native UUID
    jsonb    Json     @db.JsonB           // Binary JSON (indexable)
    xml      String   @db.Xml             // XML data
    inet     String   @db.Inet            // IP address
    cidr     String   @db.Cidr            // Network address
    macaddr  String   @db.MacAddr         // MAC address
    money    Decimal  @db.Money           // Currency
    tsVector String   @db.TsVector        // Full-text search vector

    // MySQL-specific types
    tinyText    String @db.TinyText       // 255 bytes
    mediumText  String @db.MediumText     // 16MB
    longText    String @db.LongText       // 4GB
    tinyInt     Int    @db.TinyInt        // -128 to 127
    mediumInt   Int    @db.MediumInt      // -8M to 8M
    year        Int    @db.Year           // 1901 to 2155
    bit         Int    @db.Bit(8)         // Bit field
    binary      Bytes  @db.Binary(16)     // Fixed binary
    varbinary   Bytes  @db.VarBinary(255) // Variable binary

    // SQLite types (limited type system)
    // All map to: INTEGER, REAL, TEXT, BLOB
    // Prax handles the conversion automatically
}`;
---

<DocsLayout title="Fields & Types - Prax ORM">
  <article class="max-w-4xl mx-auto px-6 py-12">
    <header class="mb-12">
      <h1 class="text-4xl font-bold mb-4">Fields & Types</h1>
      <p class="text-xl text-muted">
        Define your model fields with various scalar types, modifiers, and attributes for complete control over your database schema.
      </p>
    </header>

    <div class="space-y-12">
      <!-- Scalar Types -->
      <section>
        <h2 class="text-2xl font-semibold mb-4">Scalar Types</h2>
        <p class="text-muted mb-4">
          Prax provides scalar types that map to native database types and Rust types:
        </p>
        <div class="overflow-x-auto mb-6">
          <table class="w-full text-sm">
            <thead>
              <tr class="border-b border-border">
                <th class="text-left py-3 px-4 font-semibold">Prax Type</th>
                <th class="text-left py-3 px-4 font-semibold">Rust Type</th>
                <th class="text-left py-3 px-4 font-semibold">PostgreSQL</th>
                <th class="text-left py-3 px-4 font-semibold">MySQL</th>
                <th class="text-left py-3 px-4 font-semibold">SQLite</th>
              </tr>
            </thead>
            <tbody class="text-muted">
              <tr class="border-b border-border">
                <td class="py-3 px-4"><code class="text-primary-400">Int</code></td>
                <td class="py-3 px-4">i32</td>
                <td class="py-3 px-4">INTEGER</td>
                <td class="py-3 px-4">INT</td>
                <td class="py-3 px-4">INTEGER</td>
              </tr>
              <tr class="border-b border-border">
                <td class="py-3 px-4"><code class="text-primary-400">BigInt</code></td>
                <td class="py-3 px-4">i64</td>
                <td class="py-3 px-4">BIGINT</td>
                <td class="py-3 px-4">BIGINT</td>
                <td class="py-3 px-4">INTEGER</td>
              </tr>
              <tr class="border-b border-border">
                <td class="py-3 px-4"><code class="text-primary-400">Float</code></td>
                <td class="py-3 px-4">f64</td>
                <td class="py-3 px-4">DOUBLE PRECISION</td>
                <td class="py-3 px-4">DOUBLE</td>
                <td class="py-3 px-4">REAL</td>
              </tr>
              <tr class="border-b border-border">
                <td class="py-3 px-4"><code class="text-primary-400">Decimal</code></td>
                <td class="py-3 px-4">rust_decimal::Decimal</td>
                <td class="py-3 px-4">DECIMAL</td>
                <td class="py-3 px-4">DECIMAL</td>
                <td class="py-3 px-4">REAL</td>
              </tr>
              <tr class="border-b border-border">
                <td class="py-3 px-4"><code class="text-primary-400">String</code></td>
                <td class="py-3 px-4">String</td>
                <td class="py-3 px-4">TEXT</td>
                <td class="py-3 px-4">VARCHAR(191)</td>
                <td class="py-3 px-4">TEXT</td>
              </tr>
              <tr class="border-b border-border">
                <td class="py-3 px-4"><code class="text-primary-400">Boolean</code></td>
                <td class="py-3 px-4">bool</td>
                <td class="py-3 px-4">BOOLEAN</td>
                <td class="py-3 px-4">TINYINT(1)</td>
                <td class="py-3 px-4">INTEGER</td>
              </tr>
              <tr class="border-b border-border">
                <td class="py-3 px-4"><code class="text-primary-400">DateTime</code></td>
                <td class="py-3 px-4">chrono::DateTime&lt;Utc&gt;</td>
                <td class="py-3 px-4">TIMESTAMP(3)</td>
                <td class="py-3 px-4">DATETIME(3)</td>
                <td class="py-3 px-4">TEXT</td>
              </tr>
              <tr class="border-b border-border">
                <td class="py-3 px-4"><code class="text-primary-400">Json</code></td>
                <td class="py-3 px-4">serde_json::Value</td>
                <td class="py-3 px-4">JSONB</td>
                <td class="py-3 px-4">JSON</td>
                <td class="py-3 px-4">TEXT</td>
              </tr>
              <tr class="border-b border-border">
                <td class="py-3 px-4"><code class="text-primary-400">Bytes</code></td>
                <td class="py-3 px-4">Vec&lt;u8&gt;</td>
                <td class="py-3 px-4">BYTEA</td>
                <td class="py-3 px-4">LONGBLOB</td>
                <td class="py-3 px-4">BLOB</td>
              </tr>
            </tbody>
          </table>
        </div>
        <CodeBlock code={basicTypes} lang="prax" filename="prax/schema.prax" />
      </section>

      <!-- Type Modifiers -->
      <section>
        <h2 class="text-2xl font-semibold mb-4">Type Modifiers</h2>
        <p class="text-muted mb-4">
          Modifiers change how field values are handled:
        </p>
        <CodeBlock code={modifiers} lang="prax" filename="prax/schema.prax" />
        <div class="mt-6 overflow-x-auto">
          <table class="w-full text-sm">
            <thead>
              <tr class="border-b border-border">
                <th class="text-left py-3 px-4 font-semibold">Modifier</th>
                <th class="text-left py-3 px-4 font-semibold">Syntax</th>
                <th class="text-left py-3 px-4 font-semibold">Rust Type</th>
                <th class="text-left py-3 px-4 font-semibold">Description</th>
              </tr>
            </thead>
            <tbody class="text-muted">
              <tr class="border-b border-border">
                <td class="py-3 px-4">Required</td>
                <td class="py-3 px-4"><code class="text-primary-400">String</code></td>
                <td class="py-3 px-4">String</td>
                <td class="py-3 px-4">Must have a value (NOT NULL)</td>
              </tr>
              <tr class="border-b border-border">
                <td class="py-3 px-4">Optional</td>
                <td class="py-3 px-4"><code class="text-primary-400">String?</code></td>
                <td class="py-3 px-4">Option&lt;String&gt;</td>
                <td class="py-3 px-4">Can be NULL</td>
              </tr>
              <tr class="border-b border-border">
                <td class="py-3 px-4">Array</td>
                <td class="py-3 px-4"><code class="text-primary-400">String[]</code></td>
                <td class="py-3 px-4">Vec&lt;String&gt;</td>
                <td class="py-3 px-4">List of values</td>
              </tr>
              <tr class="border-b border-border">
                <td class="py-3 px-4">Optional Array</td>
                <td class="py-3 px-4"><code class="text-primary-400">String[]?</code></td>
                <td class="py-3 px-4">Option&lt;Vec&lt;String&gt;&gt;</td>
                <td class="py-3 px-4">Array that can be NULL</td>
              </tr>
            </tbody>
          </table>
        </div>
      </section>

      <!-- Field Attributes -->
      <section>
        <h2 class="text-2xl font-semibold mb-4">Field Attributes</h2>
        <p class="text-muted mb-4">
          Attributes modify field behavior and add constraints:
        </p>
        <CodeBlock code={attributes} lang="prax" filename="prax/schema.prax" />
      </section>

      <!-- Primary Keys -->
      <section>
        <h2 class="text-2xl font-semibold mb-4">Primary Keys</h2>
        <p class="text-muted mb-4">
          Every model needs a unique identifier. Choose the right primary key strategy for your use case:
        </p>
        <CodeBlock code={primaryKeys} lang="prax" filename="prax/schema.prax" />
        <div class="mt-6 grid md:grid-cols-2 gap-4">
          <div class="p-4 rounded-xl bg-success-500/10 border border-success-500/30">
            <h4 class="font-semibold text-success-400 mb-2">Auto-Increment</h4>
            <p class="text-muted text-sm">Best for simple applications with a single database. Compact and fast.</p>
          </div>
          <div class="p-4 rounded-xl bg-info-500/10 border border-info-500/30">
            <h4 class="font-semibold text-info-400 mb-2">UUID/CUID/ULID</h4>
            <p class="text-muted text-sm">Best for distributed systems, APIs, or when you need to generate IDs client-side.</p>
          </div>
        </div>
      </section>

      <!-- Default Values -->
      <section>
        <h2 class="text-2xl font-semibold mb-4">Default Values</h2>
        <p class="text-muted mb-4">
          Set default values to simplify record creation and ensure data consistency:
        </p>
        <CodeBlock code={defaultValues} lang="prax" filename="prax/schema.prax" />
      </section>

      <!-- Timestamps -->
      <section>
        <h2 class="text-2xl font-semibold mb-4">Timestamps</h2>
        <p class="text-muted mb-4">
          Track when records are created, updated, and deleted:
        </p>
        <CodeBlock code={timestamps} lang="prax" filename="prax/schema.prax" />
      </section>

      <!-- JSON Fields -->
      <section>
        <h2 class="text-2xl font-semibold mb-4">JSON Fields</h2>
        <p class="text-muted mb-4">
          Store flexible, schema-less data in JSON fields:
        </p>
        <CodeBlock code={jsonFields} lang="prax" filename="prax/schema.prax" />
        <div class="mt-4 p-4 rounded-xl bg-warning-500/10 border border-warning-500/30">
          <p class="text-warning-400 text-sm">
            <strong>Note:</strong> JSON fields are flexible but lack schema enforcement.
            Document the expected structure in comments and consider validation in your application layer.
          </p>
        </div>
      </section>

      <!-- Array Fields -->
      <section>
        <h2 class="text-2xl font-semibold mb-4">Array Fields</h2>
        <p class="text-muted mb-4">
          Store multiple values in a single field (PostgreSQL native support, emulated on other databases):
        </p>
        <CodeBlock code={arrayFields} lang="prax" filename="prax/schema.prax" />
      </section>

      <!-- Decimal Fields -->
      <section>
        <h2 class="text-2xl font-semibold mb-4">Decimal Fields</h2>
        <p class="text-muted mb-4">
          Use Decimal for exact precision, especially for financial data:
        </p>
        <CodeBlock code={decimalFields} lang="prax" filename="prax/schema.prax" />
      </section>

      <!-- Bytes Fields -->
      <section>
        <h2 class="text-2xl font-semibold mb-4">Binary Data</h2>
        <p class="text-muted mb-4">
          Store binary data like files, images, or hashes:
        </p>
        <CodeBlock code={bytesFields} lang="prax" filename="prax/schema.prax" />
      </section>

      <!-- Database-Specific Types -->
      <section>
        <h2 class="text-2xl font-semibold mb-4">Database-Specific Types</h2>
        <p class="text-muted mb-4">
          Use <code class="px-2 py-1 bg-surface-elevated rounded">&#64;db.TypeName</code> to specify
          native database types for advanced use cases:
        </p>
        <CodeBlock code={databaseTypes} lang="prax" filename="prax/schema.prax" />
      </section>

      <!-- Field Attributes Reference -->
      <section>
        <h2 class="text-2xl font-semibold mb-4">Field Attributes Reference</h2>
        <div class="overflow-x-auto">
          <table class="w-full text-sm">
            <thead>
              <tr class="border-b border-border">
                <th class="text-left py-3 px-4 font-semibold">Attribute</th>
                <th class="text-left py-3 px-4 font-semibold">Description</th>
              </tr>
            </thead>
            <tbody class="text-muted">
              <tr class="border-b border-border">
                <td class="py-3 px-4"><code class="text-primary-400">&#64;id</code></td>
                <td class="py-3 px-4">Marks field as primary key</td>
              </tr>
              <tr class="border-b border-border">
                <td class="py-3 px-4"><code class="text-primary-400">&#64;auto</code></td>
                <td class="py-3 px-4">Auto-increment (for Int primary keys)</td>
              </tr>
              <tr class="border-b border-border">
                <td class="py-3 px-4"><code class="text-primary-400">&#64;unique</code></td>
                <td class="py-3 px-4">Unique constraint on field</td>
              </tr>
              <tr class="border-b border-border">
                <td class="py-3 px-4"><code class="text-primary-400">&#64;default(value)</code></td>
                <td class="py-3 px-4">Default value (literal, function, or dbgenerated)</td>
              </tr>
              <tr class="border-b border-border">
                <td class="py-3 px-4"><code class="text-primary-400">&#64;updatedAt</code></td>
                <td class="py-3 px-4">Auto-update timestamp on record changes</td>
              </tr>
              <tr class="border-b border-border">
                <td class="py-3 px-4"><code class="text-primary-400">&#64;map("name")</code></td>
                <td class="py-3 px-4">Custom column name in database</td>
              </tr>
              <tr class="border-b border-border">
                <td class="py-3 px-4"><code class="text-primary-400">&#64;db.Type</code></td>
                <td class="py-3 px-4">Specific database column type</td>
              </tr>
              <tr class="border-b border-border">
                <td class="py-3 px-4"><code class="text-primary-400">&#64;ignore</code></td>
                <td class="py-3 px-4">Exclude from generated client</td>
              </tr>
              <tr class="border-b border-border">
                <td class="py-3 px-4"><code class="text-primary-400">&#64;relation(...)</code></td>
                <td class="py-3 px-4">Define relationship to another model</td>
              </tr>
            </tbody>
          </table>
        </div>
      </section>
    </div>
  </article>
</DocsLayout>